source: code/trunk/upstream.go@ 128

Last change on this file since 128 was 128, checked in by delthas, 5 years ago

Add WHOIS support

File size: 20.1 KB
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "net"
9 "strconv"
10 "strings"
11 "sync"
12 "time"
13
14 "github.com/emersion/go-sasl"
15 "gopkg.in/irc.v3"
16)
17
18type upstreamChannel struct {
19 Name string
20 conn *upstreamConn
21 Topic string
22 TopicWho string
23 TopicTime time.Time
24 Status channelStatus
25 modes modeSet
26 Members map[string]membership
27 complete bool
28}
29
30type upstreamConn struct {
31 network *network
32 logger Logger
33 net net.Conn
34 irc *irc.Conn
35 srv *Server
36 user *user
37 outgoing chan<- *irc.Message
38 ring *Ring
39
40 serverName string
41 availableUserModes string
42 availableChannelModes string
43 channelModesWithParam string
44
45 registered bool
46 nick string
47 username string
48 realname string
49 closed bool
50 modes modeSet
51 channels map[string]*upstreamChannel
52 caps map[string]string
53
54 saslClient sasl.Client
55 saslStarted bool
56
57 lock sync.Mutex
58 history map[string]uint64 // TODO: move to network
59}
60
61func connectToUpstream(network *network) (*upstreamConn, error) {
62 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
63
64 addr := network.Addr
65 if !strings.ContainsRune(addr, ':') {
66 addr = addr + ":6697"
67 }
68
69 logger.Printf("connecting to TLS server at address %q", addr)
70 netConn, err := tls.Dial("tcp", addr, nil)
71 if err != nil {
72 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
73 }
74
75 setKeepAlive(netConn)
76
77 outgoing := make(chan *irc.Message, 64)
78 uc := &upstreamConn{
79 network: network,
80 logger: logger,
81 net: netConn,
82 irc: irc.NewConn(netConn),
83 srv: network.user.srv,
84 user: network.user,
85 outgoing: outgoing,
86 ring: NewRing(network.user.srv.RingCap),
87 channels: make(map[string]*upstreamChannel),
88 history: make(map[string]uint64),
89 caps: make(map[string]string),
90 }
91
92 go func() {
93 for msg := range outgoing {
94 if uc.srv.Debug {
95 uc.logger.Printf("sent: %v", msg)
96 }
97 if err := uc.irc.WriteMessage(msg); err != nil {
98 uc.logger.Printf("failed to write message: %v", err)
99 }
100 }
101 if err := uc.net.Close(); err != nil {
102 uc.logger.Printf("failed to close connection: %v", err)
103 } else {
104 uc.logger.Printf("connection closed")
105 }
106 }()
107
108 return uc, nil
109}
110
111func (uc *upstreamConn) Close() error {
112 if uc.closed {
113 return fmt.Errorf("upstream connection already closed")
114 }
115 close(uc.outgoing)
116 uc.closed = true
117 return nil
118}
119
120func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
121 uc.user.forEachDownstream(func(dc *downstreamConn) {
122 if dc.network != nil && dc.network != uc.network {
123 return
124 }
125 f(dc)
126 })
127}
128
129func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
130 ch, ok := uc.channels[name]
131 if !ok {
132 return nil, fmt.Errorf("unknown channel %q", name)
133 }
134 return ch, nil
135}
136
137func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
138 switch msg.Command {
139 case "PING":
140 uc.SendMessage(&irc.Message{
141 Command: "PONG",
142 Params: msg.Params,
143 })
144 return nil
145 case "MODE":
146 if msg.Prefix == nil {
147 return fmt.Errorf("missing prefix")
148 }
149
150 var name, modeStr string
151 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
152 return err
153 }
154
155 if name == msg.Prefix.Name { // user mode change
156 if name != uc.nick {
157 return fmt.Errorf("received MODE message for unknow nick %q", name)
158 }
159 return uc.modes.Apply(modeStr)
160 } else { // channel mode change
161 ch, err := uc.getChannel(name)
162 if err != nil {
163 return err
164 }
165 if err := ch.modes.Apply(modeStr); err != nil {
166 return err
167 }
168
169 uc.forEachDownstream(func(dc *downstreamConn) {
170 dc.SendMessage(&irc.Message{
171 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
172 Command: "MODE",
173 Params: []string{dc.marshalChannel(uc, name), modeStr},
174 })
175 })
176 }
177 case "NOTICE":
178 uc.logger.Print(msg)
179
180 uc.forEachDownstream(func(dc *downstreamConn) {
181 dc.SendMessage(msg)
182 })
183 case "CAP":
184 var subCmd string
185 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
186 return err
187 }
188 subCmd = strings.ToUpper(subCmd)
189 subParams := msg.Params[2:]
190 switch subCmd {
191 case "LS":
192 if len(subParams) < 1 {
193 return newNeedMoreParamsError(msg.Command)
194 }
195 caps := strings.Fields(subParams[len(subParams)-1])
196 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
197
198 for _, s := range caps {
199 kv := strings.SplitN(s, "=", 2)
200 k := strings.ToLower(kv[0])
201 var v string
202 if len(kv) == 2 {
203 v = kv[1]
204 }
205 uc.caps[k] = v
206 }
207
208 if more {
209 break // wait to receive all capabilities
210 }
211
212 if uc.requestSASL() {
213 uc.SendMessage(&irc.Message{
214 Command: "CAP",
215 Params: []string{"REQ", "sasl"},
216 })
217 break // we'll send CAP END after authentication is completed
218 }
219
220 uc.SendMessage(&irc.Message{
221 Command: "CAP",
222 Params: []string{"END"},
223 })
224 case "ACK", "NAK":
225 if len(subParams) < 1 {
226 return newNeedMoreParamsError(msg.Command)
227 }
228 caps := strings.Fields(subParams[0])
229
230 for _, name := range caps {
231 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
232 return err
233 }
234 }
235
236 if uc.saslClient == nil {
237 uc.SendMessage(&irc.Message{
238 Command: "CAP",
239 Params: []string{"END"},
240 })
241 }
242 default:
243 uc.logger.Printf("unhandled message: %v", msg)
244 }
245 case "AUTHENTICATE":
246 if uc.saslClient == nil {
247 return fmt.Errorf("received unexpected AUTHENTICATE message")
248 }
249
250 // TODO: if a challenge is 400 bytes long, buffer it
251 var challengeStr string
252 if err := parseMessageParams(msg, &challengeStr); err != nil {
253 uc.SendMessage(&irc.Message{
254 Command: "AUTHENTICATE",
255 Params: []string{"*"},
256 })
257 return err
258 }
259
260 var challenge []byte
261 if challengeStr != "+" {
262 var err error
263 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
264 if err != nil {
265 uc.SendMessage(&irc.Message{
266 Command: "AUTHENTICATE",
267 Params: []string{"*"},
268 })
269 return err
270 }
271 }
272
273 var resp []byte
274 var err error
275 if !uc.saslStarted {
276 _, resp, err = uc.saslClient.Start()
277 uc.saslStarted = true
278 } else {
279 resp, err = uc.saslClient.Next(challenge)
280 }
281 if err != nil {
282 uc.SendMessage(&irc.Message{
283 Command: "AUTHENTICATE",
284 Params: []string{"*"},
285 })
286 return err
287 }
288
289 // TODO: send response in multiple chunks if >= 400 bytes
290 var respStr = "+"
291 if resp != nil {
292 respStr = base64.StdEncoding.EncodeToString(resp)
293 }
294
295 uc.SendMessage(&irc.Message{
296 Command: "AUTHENTICATE",
297 Params: []string{respStr},
298 })
299 case irc.RPL_LOGGEDIN:
300 var account string
301 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
302 return err
303 }
304 uc.logger.Printf("logged in with account %q", account)
305 case irc.RPL_LOGGEDOUT:
306 uc.logger.Printf("logged out")
307 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
308 var info string
309 if err := parseMessageParams(msg, nil, &info); err != nil {
310 return err
311 }
312 switch msg.Command {
313 case irc.ERR_NICKLOCKED:
314 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
315 case irc.ERR_SASLFAIL:
316 uc.logger.Printf("SASL authentication failed: %v", info)
317 case irc.ERR_SASLTOOLONG:
318 uc.logger.Printf("SASL message too long: %v", info)
319 }
320
321 uc.saslClient = nil
322 uc.saslStarted = false
323
324 uc.SendMessage(&irc.Message{
325 Command: "CAP",
326 Params: []string{"END"},
327 })
328 case irc.RPL_WELCOME:
329 uc.registered = true
330 uc.logger.Printf("connection registered")
331
332 channels, err := uc.srv.db.ListChannels(uc.network.ID)
333 if err != nil {
334 uc.logger.Printf("failed to list channels from database: %v", err)
335 break
336 }
337
338 for _, ch := range channels {
339 uc.SendMessage(&irc.Message{
340 Command: "JOIN",
341 Params: []string{ch.Name},
342 })
343 }
344 case irc.RPL_MYINFO:
345 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, &uc.availableChannelModes); err != nil {
346 return err
347 }
348 if len(msg.Params) > 5 {
349 uc.channelModesWithParam = msg.Params[5]
350 }
351 case "NICK":
352 if msg.Prefix == nil {
353 return fmt.Errorf("expected a prefix")
354 }
355
356 var newNick string
357 if err := parseMessageParams(msg, &newNick); err != nil {
358 return err
359 }
360
361 if msg.Prefix.Name == uc.nick {
362 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
363 uc.nick = newNick
364 }
365
366 for _, ch := range uc.channels {
367 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
368 delete(ch.Members, msg.Prefix.Name)
369 ch.Members[newNick] = membership
370 }
371 }
372
373 if msg.Prefix.Name != uc.nick {
374 uc.forEachDownstream(func(dc *downstreamConn) {
375 dc.SendMessage(&irc.Message{
376 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
377 Command: "NICK",
378 Params: []string{newNick},
379 })
380 })
381 }
382 case "JOIN":
383 if msg.Prefix == nil {
384 return fmt.Errorf("expected a prefix")
385 }
386
387 var channels string
388 if err := parseMessageParams(msg, &channels); err != nil {
389 return err
390 }
391
392 for _, ch := range strings.Split(channels, ",") {
393 if msg.Prefix.Name == uc.nick {
394 uc.logger.Printf("joined channel %q", ch)
395 uc.channels[ch] = &upstreamChannel{
396 Name: ch,
397 conn: uc,
398 Members: make(map[string]membership),
399 }
400 } else {
401 ch, err := uc.getChannel(ch)
402 if err != nil {
403 return err
404 }
405 ch.Members[msg.Prefix.Name] = 0
406 }
407
408 uc.forEachDownstream(func(dc *downstreamConn) {
409 dc.SendMessage(&irc.Message{
410 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
411 Command: "JOIN",
412 Params: []string{dc.marshalChannel(uc, ch)},
413 })
414 })
415 }
416 case "PART":
417 if msg.Prefix == nil {
418 return fmt.Errorf("expected a prefix")
419 }
420
421 var channels string
422 if err := parseMessageParams(msg, &channels); err != nil {
423 return err
424 }
425
426 for _, ch := range strings.Split(channels, ",") {
427 if msg.Prefix.Name == uc.nick {
428 uc.logger.Printf("parted channel %q", ch)
429 delete(uc.channels, ch)
430 } else {
431 ch, err := uc.getChannel(ch)
432 if err != nil {
433 return err
434 }
435 delete(ch.Members, msg.Prefix.Name)
436 }
437
438 uc.forEachDownstream(func(dc *downstreamConn) {
439 dc.SendMessage(&irc.Message{
440 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
441 Command: "PART",
442 Params: []string{dc.marshalChannel(uc, ch)},
443 })
444 })
445 }
446 case "QUIT":
447 if msg.Prefix == nil {
448 return fmt.Errorf("expected a prefix")
449 }
450
451 if msg.Prefix.Name == uc.nick {
452 uc.logger.Printf("quit")
453 }
454
455 for _, ch := range uc.channels {
456 delete(ch.Members, msg.Prefix.Name)
457 }
458
459 if msg.Prefix.Name != uc.nick {
460 uc.forEachDownstream(func(dc *downstreamConn) {
461 dc.SendMessage(&irc.Message{
462 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
463 Command: "QUIT",
464 Params: msg.Params,
465 })
466 })
467 }
468 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
469 var name, topic string
470 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
471 return err
472 }
473 ch, err := uc.getChannel(name)
474 if err != nil {
475 return err
476 }
477 if msg.Command == irc.RPL_TOPIC {
478 ch.Topic = topic
479 } else {
480 ch.Topic = ""
481 }
482 case "TOPIC":
483 var name string
484 if err := parseMessageParams(msg, &name); err != nil {
485 return err
486 }
487 ch, err := uc.getChannel(name)
488 if err != nil {
489 return err
490 }
491 if len(msg.Params) > 1 {
492 ch.Topic = msg.Params[1]
493 } else {
494 ch.Topic = ""
495 }
496 uc.forEachDownstream(func(dc *downstreamConn) {
497 params := []string{dc.marshalChannel(uc, name)}
498 if ch.Topic != "" {
499 params = append(params, ch.Topic)
500 }
501 dc.SendMessage(&irc.Message{
502 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
503 Command: "TOPIC",
504 Params: params,
505 })
506 })
507 case rpl_topicwhotime:
508 var name, who, timeStr string
509 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
510 return err
511 }
512 ch, err := uc.getChannel(name)
513 if err != nil {
514 return err
515 }
516 ch.TopicWho = who
517 sec, err := strconv.ParseInt(timeStr, 10, 64)
518 if err != nil {
519 return fmt.Errorf("failed to parse topic time: %v", err)
520 }
521 ch.TopicTime = time.Unix(sec, 0)
522 case irc.RPL_NAMREPLY:
523 var name, statusStr, members string
524 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
525 return err
526 }
527 ch, err := uc.getChannel(name)
528 if err != nil {
529 return err
530 }
531
532 status, err := parseChannelStatus(statusStr)
533 if err != nil {
534 return err
535 }
536 ch.Status = status
537
538 for _, s := range strings.Split(members, " ") {
539 membership, nick := parseMembershipPrefix(s)
540 ch.Members[nick] = membership
541 }
542 case irc.RPL_ENDOFNAMES:
543 var name string
544 if err := parseMessageParams(msg, nil, &name); err != nil {
545 return err
546 }
547 ch, err := uc.getChannel(name)
548 if err != nil {
549 return err
550 }
551
552 if ch.complete {
553 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
554 }
555 ch.complete = true
556
557 uc.forEachDownstream(func(dc *downstreamConn) {
558 forwardChannel(dc, ch)
559 })
560 case irc.RPL_WHOREPLY:
561 var channel, username, host, server, nick, mode, trailing string
562 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
563 return err
564 }
565
566 parts := strings.SplitN(trailing, " ", 2)
567 if len(parts) != 2 {
568 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
569 }
570 realname := parts[1]
571 hops, err := strconv.Atoi(parts[0])
572 if err != nil {
573 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
574 }
575 hops++
576
577 trailing = strconv.Itoa(hops) + " " + realname
578
579 uc.forEachDownstream(func(dc *downstreamConn) {
580 channel := channel
581 if channel != "*" {
582 channel = dc.marshalChannel(uc, channel)
583 }
584 nick := dc.marshalNick(uc, nick)
585 dc.SendMessage(&irc.Message{
586 Prefix: dc.srv.prefix(),
587 Command: irc.RPL_WHOREPLY,
588 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
589 })
590 })
591 case irc.RPL_ENDOFWHO:
592 var name string
593 if err := parseMessageParams(msg, nil, &name); err != nil {
594 return err
595 }
596
597 uc.forEachDownstream(func(dc *downstreamConn) {
598 name := name
599 if name != "*" {
600 // TODO: support WHO masks
601 name = dc.marshalEntity(uc, name)
602 }
603 dc.SendMessage(&irc.Message{
604 Prefix: dc.srv.prefix(),
605 Command: irc.RPL_ENDOFWHO,
606 Params: []string{dc.nick, name, "End of WHO list"},
607 })
608 })
609 case irc.RPL_WHOISUSER:
610 var nick, username, host, realname string
611 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
612 return err
613 }
614
615 uc.forEachDownstream(func(dc *downstreamConn) {
616 nick := dc.marshalNick(uc, nick)
617 dc.SendMessage(&irc.Message{
618 Prefix: dc.srv.prefix(),
619 Command: irc.RPL_WHOISUSER,
620 Params: []string{dc.nick, nick, username, host, "*", realname},
621 })
622 })
623 case irc.RPL_WHOISSERVER:
624 var nick, server, serverInfo string
625 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
626 return err
627 }
628
629 uc.forEachDownstream(func(dc *downstreamConn) {
630 nick := dc.marshalNick(uc, nick)
631 dc.SendMessage(&irc.Message{
632 Prefix: dc.srv.prefix(),
633 Command: irc.RPL_WHOISSERVER,
634 Params: []string{dc.nick, nick, server, serverInfo},
635 })
636 })
637 case irc.RPL_WHOISOPERATOR:
638 var nick string
639 if err := parseMessageParams(msg, nil, &nick); err != nil {
640 return err
641 }
642
643 uc.forEachDownstream(func(dc *downstreamConn) {
644 nick := dc.marshalNick(uc, nick)
645 dc.SendMessage(&irc.Message{
646 Prefix: dc.srv.prefix(),
647 Command: irc.RPL_WHOISOPERATOR,
648 Params: []string{dc.nick, nick, "is an IRC operator"},
649 })
650 })
651 case irc.RPL_WHOISIDLE:
652 var nick string
653 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
654 return err
655 }
656
657 uc.forEachDownstream(func(dc *downstreamConn) {
658 nick := dc.marshalNick(uc, nick)
659 params := []string{dc.nick, nick}
660 params = append(params, msg.Params[2:]...)
661 dc.SendMessage(&irc.Message{
662 Prefix: dc.srv.prefix(),
663 Command: irc.RPL_WHOISIDLE,
664 Params: params,
665 })
666 })
667 case irc.RPL_WHOISCHANNELS:
668 var nick, channelList string
669 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
670 return err
671 }
672 channels := strings.Split(channelList, " ")
673
674 uc.forEachDownstream(func(dc *downstreamConn) {
675 nick := dc.marshalNick(uc, nick)
676 channelList := make([]string, len(channels))
677 for i, channel := range channels {
678 prefix, channel := parseMembershipPrefix(channel)
679 channel = dc.marshalChannel(uc, channel)
680 channelList[i] = prefix.String() + channel
681 }
682 channels := strings.Join(channelList, " ")
683 dc.SendMessage(&irc.Message{
684 Prefix: dc.srv.prefix(),
685 Command: irc.RPL_WHOISCHANNELS,
686 Params: []string{dc.nick, nick, channels},
687 })
688 })
689 case irc.RPL_ENDOFWHOIS:
690 var nick string
691 if err := parseMessageParams(msg, nil, &nick); err != nil {
692 return err
693 }
694
695 uc.forEachDownstream(func(dc *downstreamConn) {
696 nick := dc.marshalNick(uc, nick)
697 dc.SendMessage(&irc.Message{
698 Prefix: dc.srv.prefix(),
699 Command: irc.RPL_ENDOFWHOIS,
700 Params: []string{dc.nick, nick, "End of WHOIS list"},
701 })
702 })
703 case "PRIVMSG":
704 if msg.Prefix == nil {
705 return fmt.Errorf("expected a prefix")
706 }
707
708 var nick string
709 if err := parseMessageParams(msg, &nick, nil); err != nil {
710 return err
711 }
712
713 if msg.Prefix.Name == serviceNick {
714 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
715 break
716 }
717 if nick == serviceNick {
718 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
719 break
720 }
721
722 uc.ring.Produce(msg)
723 case "INVITE":
724 var nick string
725 var channel string
726 if err := parseMessageParams(msg, &nick, &channel); err != nil {
727 return err
728 }
729
730 uc.forEachDownstream(func(dc *downstreamConn) {
731 dc.SendMessage(&irc.Message{
732 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
733 Command: "INVITE",
734 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
735 })
736 })
737 case irc.RPL_YOURHOST, irc.RPL_CREATED:
738 // Ignore
739 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
740 // Ignore
741 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
742 // Ignore
743 case rpl_localusers, rpl_globalusers:
744 // Ignore
745 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
746 // Ignore
747 default:
748 uc.logger.Printf("unhandled message: %v", msg)
749 }
750 return nil
751}
752
753func (uc *upstreamConn) register() {
754 uc.nick = uc.network.Nick
755 uc.username = uc.network.Username
756 if uc.username == "" {
757 uc.username = uc.nick
758 }
759 uc.realname = uc.network.Realname
760 if uc.realname == "" {
761 uc.realname = uc.nick
762 }
763
764 uc.SendMessage(&irc.Message{
765 Command: "CAP",
766 Params: []string{"LS", "302"},
767 })
768
769 if uc.network.Pass != "" {
770 uc.SendMessage(&irc.Message{
771 Command: "PASS",
772 Params: []string{uc.network.Pass},
773 })
774 }
775
776 uc.SendMessage(&irc.Message{
777 Command: "NICK",
778 Params: []string{uc.nick},
779 })
780 uc.SendMessage(&irc.Message{
781 Command: "USER",
782 Params: []string{uc.username, "0", "*", uc.realname},
783 })
784}
785
786func (uc *upstreamConn) requestSASL() bool {
787 if uc.network.SASL.Mechanism == "" {
788 return false
789 }
790
791 v, ok := uc.caps["sasl"]
792 if !ok {
793 return false
794 }
795 if v != "" {
796 mechanisms := strings.Split(v, ",")
797 found := false
798 for _, mech := range mechanisms {
799 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
800 found = true
801 break
802 }
803 }
804 if !found {
805 return false
806 }
807 }
808
809 return true
810}
811
812func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
813 auth := &uc.network.SASL
814 switch name {
815 case "sasl":
816 if !ok {
817 uc.logger.Printf("server refused to acknowledge the SASL capability")
818 return nil
819 }
820
821 switch auth.Mechanism {
822 case "PLAIN":
823 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
824 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
825 default:
826 return fmt.Errorf("unsupported SASL mechanism %q", name)
827 }
828
829 uc.SendMessage(&irc.Message{
830 Command: "AUTHENTICATE",
831 Params: []string{auth.Mechanism},
832 })
833 }
834 return nil
835}
836
837func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
838 for {
839 msg, err := uc.irc.ReadMessage()
840 if err == io.EOF {
841 break
842 } else if err != nil {
843 return fmt.Errorf("failed to read IRC command: %v", err)
844 }
845
846 if uc.srv.Debug {
847 uc.logger.Printf("received: %v", msg)
848 }
849
850 ch <- upstreamIncomingMessage{msg, uc}
851 }
852
853 return nil
854}
855
856func (uc *upstreamConn) SendMessage(msg *irc.Message) {
857 uc.outgoing <- msg
858}
Note: See TracBrowser for help on using the repository browser.