source: code/trunk/upstream.go@ 158

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

Fix wrong handling of members parameter of RPL_NAMREPLY

Some servers (namely UnrealIRCd) wrongly add a trailing space to the
members parameters of the RPL_NAMREPLY command, which was not handled
correctly.

Adding a trailing space is not legal wrt the IRC specs, but since
UnrealIRCd does it and is in wide use today, we have to work around it.

File size: 27.4 KB
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "errors"
7 "fmt"
8 "io"
9 "net"
10 "strconv"
11 "strings"
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 channelModes
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
39 serverName string
40 availableUserModes string
41 availableChannelModes map[byte]channelModeType
42 availableChannelTypes string
43 availableMemberships []membership
44
45 registered bool
46 nick string
47 username string
48 realname string
49 closed bool
50 modes userModes
51 channels map[string]*upstreamChannel
52 caps map[string]string
53 batches map[string]batch
54
55 tagsSupported bool
56 labelsSupported bool
57 nextLabelId uint64
58
59 saslClient sasl.Client
60 saslStarted bool
61}
62
63func connectToUpstream(network *network) (*upstreamConn, error) {
64 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
65
66 addr := network.Addr
67 if !strings.ContainsRune(addr, ':') {
68 addr = addr + ":6697"
69 }
70
71 logger.Printf("connecting to TLS server at address %q", addr)
72 netConn, err := tls.Dial("tcp", addr, nil)
73 if err != nil {
74 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
75 }
76
77 setKeepAlive(netConn)
78
79 outgoing := make(chan *irc.Message, 64)
80 uc := &upstreamConn{
81 network: network,
82 logger: logger,
83 net: netConn,
84 irc: irc.NewConn(netConn),
85 srv: network.user.srv,
86 user: network.user,
87 outgoing: outgoing,
88 channels: make(map[string]*upstreamChannel),
89 caps: make(map[string]string),
90 batches: make(map[string]batch),
91 availableChannelTypes: stdChannelTypes,
92 availableChannelModes: stdChannelModes,
93 availableMemberships: stdMemberships,
94 }
95
96 go func() {
97 for msg := range outgoing {
98 if uc.srv.Debug {
99 uc.logger.Printf("sent: %v", msg)
100 }
101 if err := uc.irc.WriteMessage(msg); err != nil {
102 uc.logger.Printf("failed to write message: %v", err)
103 }
104 }
105 if err := uc.net.Close(); err != nil {
106 uc.logger.Printf("failed to close connection: %v", err)
107 } else {
108 uc.logger.Printf("connection closed")
109 }
110 }()
111
112 return uc, nil
113}
114
115func (uc *upstreamConn) Close() error {
116 if uc.closed {
117 return fmt.Errorf("upstream connection already closed")
118 }
119 close(uc.outgoing)
120 uc.closed = true
121 return nil
122}
123
124func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
125 uc.user.forEachDownstream(func(dc *downstreamConn) {
126 if dc.network != nil && dc.network != uc.network {
127 return
128 }
129 f(dc)
130 })
131}
132
133func (uc *upstreamConn) forEachDownstreamById(id uint64, f func(*downstreamConn)) {
134 uc.forEachDownstream(func(dc *downstreamConn) {
135 if id != 0 && id != dc.id {
136 return
137 }
138 f(dc)
139 })
140}
141
142func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
143 ch, ok := uc.channels[name]
144 if !ok {
145 return nil, fmt.Errorf("unknown channel %q", name)
146 }
147 return ch, nil
148}
149
150func (uc *upstreamConn) isChannel(entity string) bool {
151 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 {
152 return true
153 }
154 return false
155}
156
157func (uc *upstreamConn) parseMembershipPrefix(s string) (membership *membership, nick string) {
158 for _, m := range uc.availableMemberships {
159 if m.Prefix == s[0] {
160 return &m, s[1:]
161 }
162 }
163 return nil, s
164}
165
166func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
167 var label string
168 if l, ok := msg.GetTag("label"); ok {
169 label = l
170 }
171
172 var msgBatch *batch
173 if batchName, ok := msg.GetTag("batch"); ok {
174 b, ok := uc.batches[batchName]
175 if !ok {
176 return fmt.Errorf("unexpected batch reference: batch was not defined: %q", batchName)
177 }
178 msgBatch = &b
179 if label == "" {
180 label = msgBatch.Label
181 }
182 }
183
184 var downstreamId uint64 = 0
185 if label != "" {
186 var labelOffset uint64
187 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamId, &labelOffset)
188 if err == nil && n < 2 {
189 err = errors.New("not enough arguments")
190 }
191 if err != nil {
192 return fmt.Errorf("unexpected message label: invalid downstream reference for label %q: %v", label, err)
193 }
194 }
195
196 switch msg.Command {
197 case "PING":
198 uc.SendMessage(&irc.Message{
199 Command: "PONG",
200 Params: msg.Params,
201 })
202 return nil
203 case "NOTICE":
204 uc.logger.Print(msg)
205
206 uc.forEachDownstream(func(dc *downstreamConn) {
207 dc.SendMessage(msg)
208 })
209 case "CAP":
210 var subCmd string
211 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
212 return err
213 }
214 subCmd = strings.ToUpper(subCmd)
215 subParams := msg.Params[2:]
216 switch subCmd {
217 case "LS":
218 if len(subParams) < 1 {
219 return newNeedMoreParamsError(msg.Command)
220 }
221 caps := strings.Fields(subParams[len(subParams)-1])
222 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
223
224 for _, s := range caps {
225 kv := strings.SplitN(s, "=", 2)
226 k := strings.ToLower(kv[0])
227 var v string
228 if len(kv) == 2 {
229 v = kv[1]
230 }
231 uc.caps[k] = v
232 }
233
234 if more {
235 break // wait to receive all capabilities
236 }
237
238 requestCaps := make([]string, 0, 16)
239 for _, c := range []string{"message-tags", "batch", "labeled-response"} {
240 if _, ok := uc.caps[c]; ok {
241 requestCaps = append(requestCaps, c)
242 }
243 }
244
245 if uc.requestSASL() {
246 requestCaps = append(requestCaps, "sasl")
247 }
248
249 if len(requestCaps) > 0 {
250 uc.SendMessage(&irc.Message{
251 Command: "CAP",
252 Params: []string{"REQ", strings.Join(requestCaps, " ")},
253 })
254 }
255
256 if uc.requestSASL() {
257 break // we'll send CAP END after authentication is completed
258 }
259
260 uc.SendMessage(&irc.Message{
261 Command: "CAP",
262 Params: []string{"END"},
263 })
264 case "ACK", "NAK":
265 if len(subParams) < 1 {
266 return newNeedMoreParamsError(msg.Command)
267 }
268 caps := strings.Fields(subParams[0])
269
270 for _, name := range caps {
271 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
272 return err
273 }
274 }
275
276 if uc.saslClient == nil {
277 uc.SendMessage(&irc.Message{
278 Command: "CAP",
279 Params: []string{"END"},
280 })
281 }
282 default:
283 uc.logger.Printf("unhandled message: %v", msg)
284 }
285 case "AUTHENTICATE":
286 if uc.saslClient == nil {
287 return fmt.Errorf("received unexpected AUTHENTICATE message")
288 }
289
290 // TODO: if a challenge is 400 bytes long, buffer it
291 var challengeStr string
292 if err := parseMessageParams(msg, &challengeStr); err != nil {
293 uc.SendMessage(&irc.Message{
294 Command: "AUTHENTICATE",
295 Params: []string{"*"},
296 })
297 return err
298 }
299
300 var challenge []byte
301 if challengeStr != "+" {
302 var err error
303 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
304 if err != nil {
305 uc.SendMessage(&irc.Message{
306 Command: "AUTHENTICATE",
307 Params: []string{"*"},
308 })
309 return err
310 }
311 }
312
313 var resp []byte
314 var err error
315 if !uc.saslStarted {
316 _, resp, err = uc.saslClient.Start()
317 uc.saslStarted = true
318 } else {
319 resp, err = uc.saslClient.Next(challenge)
320 }
321 if err != nil {
322 uc.SendMessage(&irc.Message{
323 Command: "AUTHENTICATE",
324 Params: []string{"*"},
325 })
326 return err
327 }
328
329 // TODO: send response in multiple chunks if >= 400 bytes
330 var respStr = "+"
331 if resp != nil {
332 respStr = base64.StdEncoding.EncodeToString(resp)
333 }
334
335 uc.SendMessage(&irc.Message{
336 Command: "AUTHENTICATE",
337 Params: []string{respStr},
338 })
339 case irc.RPL_LOGGEDIN:
340 var account string
341 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
342 return err
343 }
344 uc.logger.Printf("logged in with account %q", account)
345 case irc.RPL_LOGGEDOUT:
346 uc.logger.Printf("logged out")
347 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
348 var info string
349 if err := parseMessageParams(msg, nil, &info); err != nil {
350 return err
351 }
352 switch msg.Command {
353 case irc.ERR_NICKLOCKED:
354 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
355 case irc.ERR_SASLFAIL:
356 uc.logger.Printf("SASL authentication failed: %v", info)
357 case irc.ERR_SASLTOOLONG:
358 uc.logger.Printf("SASL message too long: %v", info)
359 }
360
361 uc.saslClient = nil
362 uc.saslStarted = false
363
364 uc.SendMessage(&irc.Message{
365 Command: "CAP",
366 Params: []string{"END"},
367 })
368 case irc.RPL_WELCOME:
369 uc.registered = true
370 uc.logger.Printf("connection registered")
371
372 channels, err := uc.srv.db.ListChannels(uc.network.ID)
373 if err != nil {
374 uc.logger.Printf("failed to list channels from database: %v", err)
375 break
376 }
377
378 for _, ch := range channels {
379 params := []string{ch.Name}
380 if ch.Key != "" {
381 params = append(params, ch.Key)
382 }
383 uc.SendMessage(&irc.Message{
384 Command: "JOIN",
385 Params: params,
386 })
387 }
388 case irc.RPL_MYINFO:
389 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
390 return err
391 }
392 case irc.RPL_ISUPPORT:
393 if err := parseMessageParams(msg, nil, nil); err != nil {
394 return err
395 }
396 for _, token := range msg.Params[1 : len(msg.Params)-1] {
397 negate := false
398 parameter := token
399 value := ""
400 if strings.HasPrefix(token, "-") {
401 negate = true
402 token = token[1:]
403 } else {
404 if i := strings.IndexByte(token, '='); i >= 0 {
405 parameter = token[:i]
406 value = token[i+1:]
407 }
408 }
409 if !negate {
410 switch parameter {
411 case "CHANMODES":
412 parts := strings.SplitN(value, ",", 5)
413 if len(parts) < 4 {
414 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", value)
415 }
416 modes := make(map[byte]channelModeType)
417 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
418 for j := 0; j < len(parts[i]); j++ {
419 mode := parts[i][j]
420 modes[mode] = mt
421 }
422 }
423 uc.availableChannelModes = modes
424 case "CHANTYPES":
425 uc.availableChannelTypes = value
426 case "PREFIX":
427 if value == "" {
428 uc.availableMemberships = nil
429 } else {
430 if value[0] != '(' {
431 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
432 }
433 sep := strings.IndexByte(value, ')')
434 if sep < 0 || len(value) != sep*2 {
435 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
436 }
437 memberships := make([]membership, len(value)/2-1)
438 for i := range memberships {
439 memberships[i] = membership{
440 Mode: value[i+1],
441 Prefix: value[sep+i+1],
442 }
443 }
444 uc.availableMemberships = memberships
445 }
446 }
447 } else {
448 // TODO: handle ISUPPORT negations
449 }
450 }
451 case "BATCH":
452 var tag string
453 if err := parseMessageParams(msg, &tag); err != nil {
454 return err
455 }
456
457 if strings.HasPrefix(tag, "+") {
458 tag = tag[1:]
459 if _, ok := uc.batches[tag]; ok {
460 return fmt.Errorf("unexpected BATCH reference tag: batch was already defined: %q", tag)
461 }
462 var batchType string
463 if err := parseMessageParams(msg, nil, &batchType); err != nil {
464 return err
465 }
466 label := label
467 if label == "" && msgBatch != nil {
468 label = msgBatch.Label
469 }
470 uc.batches[tag] = batch{
471 Type: batchType,
472 Params: msg.Params[2:],
473 Outer: msgBatch,
474 Label: label,
475 }
476 } else if strings.HasPrefix(tag, "-") {
477 tag = tag[1:]
478 if _, ok := uc.batches[tag]; !ok {
479 return fmt.Errorf("unknown BATCH reference tag: %q", tag)
480 }
481 delete(uc.batches, tag)
482 } else {
483 return fmt.Errorf("unexpected BATCH reference tag: missing +/- prefix: %q", tag)
484 }
485 case "NICK":
486 if msg.Prefix == nil {
487 return fmt.Errorf("expected a prefix")
488 }
489
490 var newNick string
491 if err := parseMessageParams(msg, &newNick); err != nil {
492 return err
493 }
494
495 if msg.Prefix.Name == uc.nick {
496 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
497 uc.nick = newNick
498 }
499
500 for _, ch := range uc.channels {
501 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
502 delete(ch.Members, msg.Prefix.Name)
503 ch.Members[newNick] = membership
504 }
505 }
506
507 if msg.Prefix.Name != uc.nick {
508 uc.forEachDownstream(func(dc *downstreamConn) {
509 dc.SendMessage(&irc.Message{
510 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
511 Command: "NICK",
512 Params: []string{newNick},
513 })
514 })
515 }
516 case "JOIN":
517 if msg.Prefix == nil {
518 return fmt.Errorf("expected a prefix")
519 }
520
521 var channels string
522 if err := parseMessageParams(msg, &channels); err != nil {
523 return err
524 }
525
526 for _, ch := range strings.Split(channels, ",") {
527 if msg.Prefix.Name == uc.nick {
528 uc.logger.Printf("joined channel %q", ch)
529 uc.channels[ch] = &upstreamChannel{
530 Name: ch,
531 conn: uc,
532 Members: make(map[string]*membership),
533 }
534
535 uc.SendMessage(&irc.Message{
536 Command: "MODE",
537 Params: []string{ch},
538 })
539 } else {
540 ch, err := uc.getChannel(ch)
541 if err != nil {
542 return err
543 }
544 ch.Members[msg.Prefix.Name] = nil
545 }
546
547 uc.forEachDownstream(func(dc *downstreamConn) {
548 dc.SendMessage(&irc.Message{
549 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
550 Command: "JOIN",
551 Params: []string{dc.marshalChannel(uc, ch)},
552 })
553 })
554 }
555 case "PART":
556 if msg.Prefix == nil {
557 return fmt.Errorf("expected a prefix")
558 }
559
560 var channels string
561 if err := parseMessageParams(msg, &channels); err != nil {
562 return err
563 }
564
565 for _, ch := range strings.Split(channels, ",") {
566 if msg.Prefix.Name == uc.nick {
567 uc.logger.Printf("parted channel %q", ch)
568 delete(uc.channels, ch)
569 } else {
570 ch, err := uc.getChannel(ch)
571 if err != nil {
572 return err
573 }
574 delete(ch.Members, msg.Prefix.Name)
575 }
576
577 uc.forEachDownstream(func(dc *downstreamConn) {
578 dc.SendMessage(&irc.Message{
579 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
580 Command: "PART",
581 Params: []string{dc.marshalChannel(uc, ch)},
582 })
583 })
584 }
585 case "QUIT":
586 if msg.Prefix == nil {
587 return fmt.Errorf("expected a prefix")
588 }
589
590 if msg.Prefix.Name == uc.nick {
591 uc.logger.Printf("quit")
592 }
593
594 for _, ch := range uc.channels {
595 delete(ch.Members, msg.Prefix.Name)
596 }
597
598 if msg.Prefix.Name != uc.nick {
599 uc.forEachDownstream(func(dc *downstreamConn) {
600 dc.SendMessage(&irc.Message{
601 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
602 Command: "QUIT",
603 Params: msg.Params,
604 })
605 })
606 }
607 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
608 var name, topic string
609 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
610 return err
611 }
612 ch, err := uc.getChannel(name)
613 if err != nil {
614 return err
615 }
616 if msg.Command == irc.RPL_TOPIC {
617 ch.Topic = topic
618 } else {
619 ch.Topic = ""
620 }
621 case "TOPIC":
622 var name string
623 if err := parseMessageParams(msg, &name); err != nil {
624 return err
625 }
626 ch, err := uc.getChannel(name)
627 if err != nil {
628 return err
629 }
630 if len(msg.Params) > 1 {
631 ch.Topic = msg.Params[1]
632 } else {
633 ch.Topic = ""
634 }
635 uc.forEachDownstream(func(dc *downstreamConn) {
636 params := []string{dc.marshalChannel(uc, name)}
637 if ch.Topic != "" {
638 params = append(params, ch.Topic)
639 }
640 dc.SendMessage(&irc.Message{
641 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
642 Command: "TOPIC",
643 Params: params,
644 })
645 })
646 case "MODE":
647 var name, modeStr string
648 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
649 return err
650 }
651
652 if !uc.isChannel(name) { // user mode change
653 if name != uc.nick {
654 return fmt.Errorf("received MODE message for unknown nick %q", name)
655 }
656 return uc.modes.Apply(modeStr)
657 // TODO: notify downstreams about user mode change?
658 } else { // channel mode change
659 ch, err := uc.getChannel(name)
660 if err != nil {
661 return err
662 }
663
664 if ch.modes != nil {
665 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil {
666 return err
667 }
668 }
669
670 uc.forEachDownstream(func(dc *downstreamConn) {
671 params := []string{dc.marshalChannel(uc, name), modeStr}
672 params = append(params, msg.Params[2:]...)
673
674 dc.SendMessage(&irc.Message{
675 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
676 Command: "MODE",
677 Params: params,
678 })
679 })
680 }
681 case irc.RPL_UMODEIS:
682 if err := parseMessageParams(msg, nil); err != nil {
683 return err
684 }
685 modeStr := ""
686 if len(msg.Params) > 1 {
687 modeStr = msg.Params[1]
688 }
689
690 uc.modes = ""
691 if err := uc.modes.Apply(modeStr); err != nil {
692 return err
693 }
694 // TODO: send RPL_UMODEIS to downstream connections when applicable
695 case irc.RPL_CHANNELMODEIS:
696 var channel string
697 if err := parseMessageParams(msg, nil, &channel); err != nil {
698 return err
699 }
700 modeStr := ""
701 if len(msg.Params) > 2 {
702 modeStr = msg.Params[2]
703 }
704
705 ch, err := uc.getChannel(channel)
706 if err != nil {
707 return err
708 }
709
710 firstMode := ch.modes == nil
711 ch.modes = make(map[byte]string)
712 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil {
713 return err
714 }
715 if firstMode {
716 modeStr, modeParams := ch.modes.Format()
717
718 uc.forEachDownstream(func(dc *downstreamConn) {
719 params := []string{dc.nick, dc.marshalChannel(uc, channel), modeStr}
720 params = append(params, modeParams...)
721
722 dc.SendMessage(&irc.Message{
723 Prefix: dc.srv.prefix(),
724 Command: irc.RPL_CHANNELMODEIS,
725 Params: params,
726 })
727 })
728 }
729 case rpl_topicwhotime:
730 var name, who, timeStr string
731 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
732 return err
733 }
734 ch, err := uc.getChannel(name)
735 if err != nil {
736 return err
737 }
738 ch.TopicWho = who
739 sec, err := strconv.ParseInt(timeStr, 10, 64)
740 if err != nil {
741 return fmt.Errorf("failed to parse topic time: %v", err)
742 }
743 ch.TopicTime = time.Unix(sec, 0)
744 case irc.RPL_NAMREPLY:
745 var name, statusStr, members string
746 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
747 return err
748 }
749 members = strings.TrimRight(members, " ")
750
751 ch, ok := uc.channels[name]
752 if !ok {
753 // NAMES on a channel we have not joined, forward to downstream
754 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
755 channel := dc.marshalChannel(uc, name)
756 members := strings.Split(members, " ")
757 for i, member := range members {
758 membership, nick := uc.parseMembershipPrefix(member)
759 members[i] = membership.String() + dc.marshalNick(uc, nick)
760 }
761 memberStr := strings.Join(members, " ")
762
763 dc.SendMessage(&irc.Message{
764 Prefix: dc.srv.prefix(),
765 Command: irc.RPL_NAMREPLY,
766 Params: []string{dc.nick, statusStr, channel, memberStr},
767 })
768 })
769 return nil
770 }
771
772 status, err := parseChannelStatus(statusStr)
773 if err != nil {
774 return err
775 }
776 ch.Status = status
777
778 for _, s := range strings.Split(members, " ") {
779 membership, nick := uc.parseMembershipPrefix(s)
780 ch.Members[nick] = membership
781 }
782 case irc.RPL_ENDOFNAMES:
783 var name string
784 if err := parseMessageParams(msg, nil, &name); err != nil {
785 return err
786 }
787
788 ch, ok := uc.channels[name]
789 if !ok {
790 // NAMES on a channel we have not joined, forward to downstream
791 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
792 channel := dc.marshalChannel(uc, name)
793
794 dc.SendMessage(&irc.Message{
795 Prefix: dc.srv.prefix(),
796 Command: irc.RPL_ENDOFNAMES,
797 Params: []string{dc.nick, channel, "End of /NAMES list"},
798 })
799 })
800 return nil
801 }
802
803 if ch.complete {
804 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
805 }
806 ch.complete = true
807
808 uc.forEachDownstream(func(dc *downstreamConn) {
809 forwardChannel(dc, ch)
810 })
811 case irc.RPL_WHOREPLY:
812 var channel, username, host, server, nick, mode, trailing string
813 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
814 return err
815 }
816
817 parts := strings.SplitN(trailing, " ", 2)
818 if len(parts) != 2 {
819 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
820 }
821 realname := parts[1]
822 hops, err := strconv.Atoi(parts[0])
823 if err != nil {
824 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
825 }
826 hops++
827
828 trailing = strconv.Itoa(hops) + " " + realname
829
830 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
831 channel := channel
832 if channel != "*" {
833 channel = dc.marshalChannel(uc, channel)
834 }
835 nick := dc.marshalNick(uc, nick)
836 dc.SendMessage(&irc.Message{
837 Prefix: dc.srv.prefix(),
838 Command: irc.RPL_WHOREPLY,
839 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
840 })
841 })
842 case irc.RPL_ENDOFWHO:
843 var name string
844 if err := parseMessageParams(msg, nil, &name); err != nil {
845 return err
846 }
847
848 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
849 name := name
850 if name != "*" {
851 // TODO: support WHO masks
852 name = dc.marshalEntity(uc, name)
853 }
854 dc.SendMessage(&irc.Message{
855 Prefix: dc.srv.prefix(),
856 Command: irc.RPL_ENDOFWHO,
857 Params: []string{dc.nick, name, "End of /WHO list"},
858 })
859 })
860 case irc.RPL_WHOISUSER:
861 var nick, username, host, realname string
862 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
863 return err
864 }
865
866 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
867 nick := dc.marshalNick(uc, nick)
868 dc.SendMessage(&irc.Message{
869 Prefix: dc.srv.prefix(),
870 Command: irc.RPL_WHOISUSER,
871 Params: []string{dc.nick, nick, username, host, "*", realname},
872 })
873 })
874 case irc.RPL_WHOISSERVER:
875 var nick, server, serverInfo string
876 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
877 return err
878 }
879
880 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
881 nick := dc.marshalNick(uc, nick)
882 dc.SendMessage(&irc.Message{
883 Prefix: dc.srv.prefix(),
884 Command: irc.RPL_WHOISSERVER,
885 Params: []string{dc.nick, nick, server, serverInfo},
886 })
887 })
888 case irc.RPL_WHOISOPERATOR:
889 var nick string
890 if err := parseMessageParams(msg, nil, &nick); err != nil {
891 return err
892 }
893
894 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
895 nick := dc.marshalNick(uc, nick)
896 dc.SendMessage(&irc.Message{
897 Prefix: dc.srv.prefix(),
898 Command: irc.RPL_WHOISOPERATOR,
899 Params: []string{dc.nick, nick, "is an IRC operator"},
900 })
901 })
902 case irc.RPL_WHOISIDLE:
903 var nick string
904 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
905 return err
906 }
907
908 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
909 nick := dc.marshalNick(uc, nick)
910 params := []string{dc.nick, nick}
911 params = append(params, msg.Params[2:]...)
912 dc.SendMessage(&irc.Message{
913 Prefix: dc.srv.prefix(),
914 Command: irc.RPL_WHOISIDLE,
915 Params: params,
916 })
917 })
918 case irc.RPL_WHOISCHANNELS:
919 var nick, channelList string
920 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
921 return err
922 }
923 channels := strings.Split(channelList, " ")
924
925 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
926 nick := dc.marshalNick(uc, nick)
927 channelList := make([]string, len(channels))
928 for i, channel := range channels {
929 prefix, channel := uc.parseMembershipPrefix(channel)
930 channel = dc.marshalChannel(uc, channel)
931 channelList[i] = prefix.String() + channel
932 }
933 channels := strings.Join(channelList, " ")
934 dc.SendMessage(&irc.Message{
935 Prefix: dc.srv.prefix(),
936 Command: irc.RPL_WHOISCHANNELS,
937 Params: []string{dc.nick, nick, channels},
938 })
939 })
940 case irc.RPL_ENDOFWHOIS:
941 var nick string
942 if err := parseMessageParams(msg, nil, &nick); err != nil {
943 return err
944 }
945
946 uc.forEachDownstreamById(downstreamId, func(dc *downstreamConn) {
947 nick := dc.marshalNick(uc, nick)
948 dc.SendMessage(&irc.Message{
949 Prefix: dc.srv.prefix(),
950 Command: irc.RPL_ENDOFWHOIS,
951 Params: []string{dc.nick, nick, "End of /WHOIS list"},
952 })
953 })
954 case "PRIVMSG":
955 if msg.Prefix == nil {
956 return fmt.Errorf("expected a prefix")
957 }
958
959 var nick string
960 if err := parseMessageParams(msg, &nick, nil); err != nil {
961 return err
962 }
963
964 if msg.Prefix.Name == serviceNick {
965 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
966 break
967 }
968 if nick == serviceNick {
969 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
970 break
971 }
972
973 uc.network.ring.Produce(msg)
974 case "INVITE":
975 var nick string
976 var channel string
977 if err := parseMessageParams(msg, &nick, &channel); err != nil {
978 return err
979 }
980
981 uc.forEachDownstream(func(dc *downstreamConn) {
982 dc.SendMessage(&irc.Message{
983 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
984 Command: "INVITE",
985 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
986 })
987 })
988 case "TAGMSG":
989 // TODO: relay to downstream connections that accept message-tags
990 case "ACK":
991 // Ignore
992 case irc.RPL_YOURHOST, irc.RPL_CREATED:
993 // Ignore
994 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
995 // Ignore
996 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
997 // Ignore
998 case rpl_localusers, rpl_globalusers:
999 // Ignore
1000 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
1001 // Ignore
1002 default:
1003 uc.logger.Printf("unhandled message: %v", msg)
1004 }
1005 return nil
1006}
1007
1008func (uc *upstreamConn) register() {
1009 uc.nick = uc.network.Nick
1010 uc.username = uc.network.Username
1011 if uc.username == "" {
1012 uc.username = uc.nick
1013 }
1014 uc.realname = uc.network.Realname
1015 if uc.realname == "" {
1016 uc.realname = uc.nick
1017 }
1018
1019 uc.SendMessage(&irc.Message{
1020 Command: "CAP",
1021 Params: []string{"LS", "302"},
1022 })
1023
1024 if uc.network.Pass != "" {
1025 uc.SendMessage(&irc.Message{
1026 Command: "PASS",
1027 Params: []string{uc.network.Pass},
1028 })
1029 }
1030
1031 uc.SendMessage(&irc.Message{
1032 Command: "NICK",
1033 Params: []string{uc.nick},
1034 })
1035 uc.SendMessage(&irc.Message{
1036 Command: "USER",
1037 Params: []string{uc.username, "0", "*", uc.realname},
1038 })
1039}
1040
1041func (uc *upstreamConn) requestSASL() bool {
1042 if uc.network.SASL.Mechanism == "" {
1043 return false
1044 }
1045
1046 v, ok := uc.caps["sasl"]
1047 if !ok {
1048 return false
1049 }
1050 if v != "" {
1051 mechanisms := strings.Split(v, ",")
1052 found := false
1053 for _, mech := range mechanisms {
1054 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
1055 found = true
1056 break
1057 }
1058 }
1059 if !found {
1060 return false
1061 }
1062 }
1063
1064 return true
1065}
1066
1067func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1068 auth := &uc.network.SASL
1069 switch name {
1070 case "sasl":
1071 if !ok {
1072 uc.logger.Printf("server refused to acknowledge the SASL capability")
1073 return nil
1074 }
1075
1076 switch auth.Mechanism {
1077 case "PLAIN":
1078 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1079 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1080 default:
1081 return fmt.Errorf("unsupported SASL mechanism %q", name)
1082 }
1083
1084 uc.SendMessage(&irc.Message{
1085 Command: "AUTHENTICATE",
1086 Params: []string{auth.Mechanism},
1087 })
1088 case "message-tags":
1089 uc.tagsSupported = ok
1090 case "labeled-response":
1091 uc.labelsSupported = ok
1092 }
1093 return nil
1094}
1095
1096func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
1097 for {
1098 msg, err := uc.irc.ReadMessage()
1099 if err == io.EOF {
1100 break
1101 } else if err != nil {
1102 return fmt.Errorf("failed to read IRC command: %v", err)
1103 }
1104
1105 if uc.srv.Debug {
1106 uc.logger.Printf("received: %v", msg)
1107 }
1108
1109 ch <- upstreamIncomingMessage{msg, uc}
1110 }
1111
1112 return nil
1113}
1114
1115func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1116 uc.outgoing <- msg
1117}
1118
1119func (uc *upstreamConn) SendMessageLabeled(dc *downstreamConn, msg *irc.Message) {
1120 if uc.labelsSupported {
1121 if msg.Tags == nil {
1122 msg.Tags = make(map[string]irc.TagValue)
1123 }
1124 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", dc.id, uc.nextLabelId))
1125 uc.nextLabelId++
1126 }
1127 uc.SendMessage(msg)
1128}
Note: See TracBrowser for help on using the repository browser.