source: code/trunk/upstream.go@ 130

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

Fix MODE downstream support

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