source: code/trunk/upstream.go@ 736

Last change on this file since 736 was 736, checked in by contact, 4 years ago

Don't retry connecting on permanent failure

Closes: https://todo.sr.ht/~emersion/soju/164

File size: 53.2 KB
Line 
1package soju
2
3import (
4 "context"
5 "crypto"
6 "crypto/sha256"
7 "crypto/tls"
8 "crypto/x509"
9 "encoding/base64"
10 "errors"
11 "fmt"
12 "io"
13 "net"
14 "strconv"
15 "strings"
16 "time"
17
18 "github.com/emersion/go-sasl"
19 "gopkg.in/irc.v3"
20)
21
22// permanentUpstreamCaps is the static list of upstream capabilities always
23// requested when supported.
24var permanentUpstreamCaps = map[string]bool{
25 "account-notify": true,
26 "account-tag": true,
27 "away-notify": true,
28 "batch": true,
29 "extended-join": true,
30 "invite-notify": true,
31 "labeled-response": true,
32 "message-tags": true,
33 "multi-prefix": true,
34 "sasl": true,
35 "server-time": true,
36 "setname": true,
37
38 "draft/account-registration": true,
39 "draft/extended-monitor": true,
40}
41
42type registrationError struct {
43 *irc.Message
44}
45
46func (err registrationError) Error() string {
47 return fmt.Sprintf("registration error (%v): %v", err.Command, err.Reason())
48}
49
50func (err registrationError) Reason() string {
51 if len(err.Params) > 0 {
52 return err.Params[len(err.Params)-1]
53 }
54 return err.Command
55}
56
57func (err registrationError) Temporary() bool {
58 // Only return false if we're 100% sure that fixing the error requires a
59 // network configuration change
60 return err.Command != irc.ERR_PASSWDMISMATCH && err.Command != irc.ERR_ERRONEUSNICKNAME
61}
62
63type upstreamChannel struct {
64 Name string
65 conn *upstreamConn
66 Topic string
67 TopicWho *irc.Prefix
68 TopicTime time.Time
69 Status channelStatus
70 modes channelModes
71 creationTime string
72 Members membershipsCasemapMap
73 complete bool
74 detachTimer *time.Timer
75}
76
77func (uc *upstreamChannel) updateAutoDetach(dur time.Duration) {
78 if uc.detachTimer != nil {
79 uc.detachTimer.Stop()
80 uc.detachTimer = nil
81 }
82
83 if dur == 0 {
84 return
85 }
86
87 uc.detachTimer = time.AfterFunc(dur, func() {
88 uc.conn.network.user.events <- eventChannelDetach{
89 uc: uc.conn,
90 name: uc.Name,
91 }
92 })
93}
94
95type pendingUpstreamCommand struct {
96 downstreamID uint64
97 msg *irc.Message
98}
99
100type upstreamConn struct {
101 conn
102
103 network *network
104 user *user
105
106 serverName string
107 availableUserModes string
108 availableChannelModes map[byte]channelModeType
109 availableChannelTypes string
110 availableMemberships []membership
111 isupport map[string]*string
112
113 registered bool
114 nick string
115 nickCM string
116 username string
117 realname string
118 modes userModes
119 channels upstreamChannelCasemapMap
120 supportedCaps map[string]string
121 caps map[string]bool
122 batches map[string]batch
123 away bool
124 account string
125 nextLabelID uint64
126 monitored monitorCasemapMap
127
128 saslClient sasl.Client
129 saslStarted bool
130
131 casemapIsSet bool
132
133 // Queue of commands in progress, indexed by type. The first entry has been
134 // sent to the server and is awaiting reply. The following entries have not
135 // been sent yet.
136 pendingCmds map[string][]pendingUpstreamCommand
137
138 gotMotd bool
139}
140
141func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, error) {
142 logger := &prefixLogger{network.user.logger, fmt.Sprintf("upstream %q: ", network.GetName())}
143
144 dialer := net.Dialer{Timeout: connectTimeout}
145
146 u, err := network.URL()
147 if err != nil {
148 return nil, err
149 }
150
151 var netConn net.Conn
152 switch u.Scheme {
153 case "ircs":
154 addr := u.Host
155 host, _, err := net.SplitHostPort(u.Host)
156 if err != nil {
157 host = u.Host
158 addr = u.Host + ":6697"
159 }
160
161 dialer.LocalAddr, err = network.user.localTCPAddrForHost(ctx, host)
162 if err != nil {
163 return nil, fmt.Errorf("failed to pick local IP for remote host %q: %v", host, err)
164 }
165
166 logger.Printf("connecting to TLS server at address %q", addr)
167
168 tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
169 if network.SASL.Mechanism == "EXTERNAL" {
170 if network.SASL.External.CertBlob == nil {
171 return nil, fmt.Errorf("missing certificate for authentication")
172 }
173 if network.SASL.External.PrivKeyBlob == nil {
174 return nil, fmt.Errorf("missing private key for authentication")
175 }
176 key, err := x509.ParsePKCS8PrivateKey(network.SASL.External.PrivKeyBlob)
177 if err != nil {
178 return nil, fmt.Errorf("failed to parse private key: %v", err)
179 }
180 tlsConfig.Certificates = []tls.Certificate{
181 {
182 Certificate: [][]byte{network.SASL.External.CertBlob},
183 PrivateKey: key.(crypto.PrivateKey),
184 },
185 }
186 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob))
187 }
188
189 netConn, err = dialer.DialContext(ctx, "tcp", addr)
190 if err != nil {
191 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
192 }
193
194 // Don't do the TLS handshake immediately, because we need to register
195 // the new connection with identd ASAP. See:
196 // https://todo.sr.ht/~emersion/soju/69#event-41859
197 netConn = tls.Client(netConn, tlsConfig)
198 case "irc+insecure":
199 addr := u.Host
200 host, _, err := net.SplitHostPort(addr)
201 if err != nil {
202 host = u.Host
203 addr = u.Host + ":6667"
204 }
205
206 dialer.LocalAddr, err = network.user.localTCPAddrForHost(ctx, host)
207 if err != nil {
208 return nil, fmt.Errorf("failed to pick local IP for remote host %q: %v", host, err)
209 }
210
211 logger.Printf("connecting to plain-text server at address %q", addr)
212 netConn, err = dialer.DialContext(ctx, "tcp", addr)
213 if err != nil {
214 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
215 }
216 case "irc+unix", "unix":
217 logger.Printf("connecting to Unix socket at path %q", u.Path)
218 netConn, err = dialer.DialContext(ctx, "unix", u.Path)
219 if err != nil {
220 return nil, fmt.Errorf("failed to connect to Unix socket %q: %v", u.Path, err)
221 }
222 default:
223 return nil, fmt.Errorf("failed to dial %q: unknown scheme: %v", network.Addr, u.Scheme)
224 }
225
226 options := connOptions{
227 Logger: logger,
228 RateLimitDelay: upstreamMessageDelay,
229 RateLimitBurst: upstreamMessageBurst,
230 }
231
232 uc := &upstreamConn{
233 conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
234 network: network,
235 user: network.user,
236 channels: upstreamChannelCasemapMap{newCasemapMap(0)},
237 supportedCaps: make(map[string]string),
238 caps: make(map[string]bool),
239 batches: make(map[string]batch),
240 availableChannelTypes: stdChannelTypes,
241 availableChannelModes: stdChannelModes,
242 availableMemberships: stdMemberships,
243 isupport: make(map[string]*string),
244 pendingCmds: make(map[string][]pendingUpstreamCommand),
245 monitored: monitorCasemapMap{newCasemapMap(0)},
246 }
247 return uc, nil
248}
249
250func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
251 uc.network.forEachDownstream(f)
252}
253
254func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
255 uc.forEachDownstream(func(dc *downstreamConn) {
256 if id != 0 && id != dc.id {
257 return
258 }
259 f(dc)
260 })
261}
262
263func (uc *upstreamConn) downstreamByID(id uint64) *downstreamConn {
264 for _, dc := range uc.user.downstreamConns {
265 if dc.id == id {
266 return dc
267 }
268 }
269 return nil
270}
271
272func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
273 ch := uc.channels.Value(name)
274 if ch == nil {
275 return nil, fmt.Errorf("unknown channel %q", name)
276 }
277 return ch, nil
278}
279
280func (uc *upstreamConn) isChannel(entity string) bool {
281 return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
282}
283
284func (uc *upstreamConn) isOurNick(nick string) bool {
285 return uc.nickCM == uc.network.casemap(nick)
286}
287
288func (uc *upstreamConn) endPendingCommands() {
289 for _, l := range uc.pendingCmds {
290 for _, pendingCmd := range l {
291 dc := uc.downstreamByID(pendingCmd.downstreamID)
292 if dc == nil {
293 continue
294 }
295
296 switch pendingCmd.msg.Command {
297 case "LIST":
298 dc.SendMessage(&irc.Message{
299 Prefix: dc.srv.prefix(),
300 Command: irc.RPL_LISTEND,
301 Params: []string{dc.nick, "End of /LIST"},
302 })
303 case "WHO":
304 mask := "*"
305 if len(pendingCmd.msg.Params) > 0 {
306 mask = pendingCmd.msg.Params[0]
307 }
308 dc.SendMessage(&irc.Message{
309 Prefix: dc.srv.prefix(),
310 Command: irc.RPL_ENDOFWHO,
311 Params: []string{dc.nick, mask, "End of /WHO"},
312 })
313 case "AUTHENTICATE":
314 dc.endSASL(&irc.Message{
315 Prefix: dc.srv.prefix(),
316 Command: irc.ERR_SASLABORTED,
317 Params: []string{dc.nick, "SASL authentication aborted"},
318 })
319 case "REGISTER", "VERIFY":
320 dc.SendMessage(&irc.Message{
321 Prefix: dc.srv.prefix(),
322 Command: "FAIL",
323 Params: []string{pendingCmd.msg.Command, "TEMPORARILY_UNAVAILABLE", pendingCmd.msg.Params[0], "Command aborted"},
324 })
325 default:
326 panic(fmt.Errorf("Unsupported pending command %q", pendingCmd.msg.Command))
327 }
328 }
329 }
330
331 uc.pendingCmds = make(map[string][]pendingUpstreamCommand)
332}
333
334func (uc *upstreamConn) sendNextPendingCommand(cmd string) {
335 if len(uc.pendingCmds[cmd]) == 0 {
336 return
337 }
338 uc.SendMessage(uc.pendingCmds[cmd][0].msg)
339}
340
341func (uc *upstreamConn) enqueueCommand(dc *downstreamConn, msg *irc.Message) {
342 switch msg.Command {
343 case "LIST", "WHO", "AUTHENTICATE", "REGISTER", "VERIFY":
344 // Supported
345 default:
346 panic(fmt.Errorf("Unsupported pending command %q", msg.Command))
347 }
348
349 uc.pendingCmds[msg.Command] = append(uc.pendingCmds[msg.Command], pendingUpstreamCommand{
350 downstreamID: dc.id,
351 msg: msg,
352 })
353
354 if len(uc.pendingCmds[msg.Command]) == 1 {
355 uc.sendNextPendingCommand(msg.Command)
356 }
357}
358
359func (uc *upstreamConn) currentPendingCommand(cmd string) (*downstreamConn, *irc.Message) {
360 if len(uc.pendingCmds[cmd]) == 0 {
361 return nil, nil
362 }
363
364 pendingCmd := uc.pendingCmds[cmd][0]
365 return uc.downstreamByID(pendingCmd.downstreamID), pendingCmd.msg
366}
367
368func (uc *upstreamConn) dequeueCommand(cmd string) (*downstreamConn, *irc.Message) {
369 dc, msg := uc.currentPendingCommand(cmd)
370
371 if len(uc.pendingCmds[cmd]) > 0 {
372 copy(uc.pendingCmds[cmd], uc.pendingCmds[cmd][1:])
373 uc.pendingCmds[cmd] = uc.pendingCmds[cmd][:len(uc.pendingCmds[cmd])-1]
374 }
375
376 uc.sendNextPendingCommand(cmd)
377
378 return dc, msg
379}
380
381func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
382 memberships := make(memberships, 0, 4)
383 i := 0
384 for _, m := range uc.availableMemberships {
385 if i >= len(s) {
386 break
387 }
388 if s[i] == m.Prefix {
389 memberships = append(memberships, m)
390 i++
391 }
392 }
393 return &memberships, s[i:]
394}
395
396func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
397 var label string
398 if l, ok := msg.GetTag("label"); ok {
399 label = l
400 delete(msg.Tags, "label")
401 }
402
403 var msgBatch *batch
404 if batchName, ok := msg.GetTag("batch"); ok {
405 b, ok := uc.batches[batchName]
406 if !ok {
407 return fmt.Errorf("unexpected batch reference: batch was not defined: %q", batchName)
408 }
409 msgBatch = &b
410 if label == "" {
411 label = msgBatch.Label
412 }
413 delete(msg.Tags, "batch")
414 }
415
416 var downstreamID uint64 = 0
417 if label != "" {
418 var labelOffset uint64
419 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
420 if err == nil && n < 2 {
421 err = errors.New("not enough arguments")
422 }
423 if err != nil {
424 return fmt.Errorf("unexpected message label: invalid downstream reference for label %q: %v", label, err)
425 }
426 }
427
428 if _, ok := msg.Tags["time"]; !ok {
429 msg.Tags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
430 }
431
432 switch msg.Command {
433 case "PING":
434 uc.SendMessage(&irc.Message{
435 Command: "PONG",
436 Params: msg.Params,
437 })
438 return nil
439 case "NOTICE", "PRIVMSG", "TAGMSG":
440 if msg.Prefix == nil {
441 return fmt.Errorf("expected a prefix")
442 }
443
444 var entity, text string
445 if msg.Command != "TAGMSG" {
446 if err := parseMessageParams(msg, &entity, &text); err != nil {
447 return err
448 }
449 } else {
450 if err := parseMessageParams(msg, &entity); err != nil {
451 return err
452 }
453 }
454
455 if msg.Prefix.Name == serviceNick {
456 uc.logger.Printf("skipping %v from soju's service: %v", msg.Command, msg)
457 break
458 }
459 if entity == serviceNick {
460 uc.logger.Printf("skipping %v to soju's service: %v", msg.Command, msg)
461 break
462 }
463
464 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
465 uc.produce("", msg, nil)
466 } else { // regular user message
467 target := entity
468 if uc.isOurNick(target) {
469 target = msg.Prefix.Name
470 }
471
472 ch := uc.network.channels.Value(target)
473 if ch != nil && msg.Command != "TAGMSG" {
474 if ch.Detached {
475 uc.handleDetachedMessage(ch, msg)
476 }
477
478 highlight := uc.network.isHighlight(msg)
479 if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
480 uc.updateChannelAutoDetach(target)
481 }
482 }
483
484 uc.produce(target, msg, nil)
485 }
486 case "CAP":
487 var subCmd string
488 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
489 return err
490 }
491 subCmd = strings.ToUpper(subCmd)
492 subParams := msg.Params[2:]
493 switch subCmd {
494 case "LS":
495 if len(subParams) < 1 {
496 return newNeedMoreParamsError(msg.Command)
497 }
498 caps := subParams[len(subParams)-1]
499 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
500
501 uc.handleSupportedCaps(caps)
502
503 if more {
504 break // wait to receive all capabilities
505 }
506
507 uc.requestCaps()
508
509 if uc.requestSASL() {
510 break // we'll send CAP END after authentication is completed
511 }
512
513 uc.SendMessage(&irc.Message{
514 Command: "CAP",
515 Params: []string{"END"},
516 })
517 case "ACK", "NAK":
518 if len(subParams) < 1 {
519 return newNeedMoreParamsError(msg.Command)
520 }
521 caps := strings.Fields(subParams[0])
522
523 for _, name := range caps {
524 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
525 return err
526 }
527 }
528
529 if uc.registered {
530 uc.forEachDownstream(func(dc *downstreamConn) {
531 dc.updateSupportedCaps()
532 })
533 }
534 case "NEW":
535 if len(subParams) < 1 {
536 return newNeedMoreParamsError(msg.Command)
537 }
538 uc.handleSupportedCaps(subParams[0])
539 uc.requestCaps()
540 case "DEL":
541 if len(subParams) < 1 {
542 return newNeedMoreParamsError(msg.Command)
543 }
544 caps := strings.Fields(subParams[0])
545
546 for _, c := range caps {
547 delete(uc.supportedCaps, c)
548 delete(uc.caps, c)
549 }
550
551 if uc.registered {
552 uc.forEachDownstream(func(dc *downstreamConn) {
553 dc.updateSupportedCaps()
554 })
555 }
556 default:
557 uc.logger.Printf("unhandled message: %v", msg)
558 }
559 case "AUTHENTICATE":
560 if uc.saslClient == nil {
561 return fmt.Errorf("received unexpected AUTHENTICATE message")
562 }
563
564 // TODO: if a challenge is 400 bytes long, buffer it
565 var challengeStr string
566 if err := parseMessageParams(msg, &challengeStr); err != nil {
567 uc.SendMessage(&irc.Message{
568 Command: "AUTHENTICATE",
569 Params: []string{"*"},
570 })
571 return err
572 }
573
574 var challenge []byte
575 if challengeStr != "+" {
576 var err error
577 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
578 if err != nil {
579 uc.SendMessage(&irc.Message{
580 Command: "AUTHENTICATE",
581 Params: []string{"*"},
582 })
583 return err
584 }
585 }
586
587 var resp []byte
588 var err error
589 if !uc.saslStarted {
590 _, resp, err = uc.saslClient.Start()
591 uc.saslStarted = true
592 } else {
593 resp, err = uc.saslClient.Next(challenge)
594 }
595 if err != nil {
596 uc.SendMessage(&irc.Message{
597 Command: "AUTHENTICATE",
598 Params: []string{"*"},
599 })
600 return err
601 }
602
603 // TODO: send response in multiple chunks if >= 400 bytes
604 var respStr = "+"
605 if len(resp) != 0 {
606 respStr = base64.StdEncoding.EncodeToString(resp)
607 }
608
609 uc.SendMessage(&irc.Message{
610 Command: "AUTHENTICATE",
611 Params: []string{respStr},
612 })
613 case irc.RPL_LOGGEDIN:
614 if err := parseMessageParams(msg, nil, nil, &uc.account); err != nil {
615 return err
616 }
617 uc.logger.Printf("logged in with account %q", uc.account)
618 uc.forEachDownstream(func(dc *downstreamConn) {
619 dc.updateAccount()
620 })
621 case irc.RPL_LOGGEDOUT:
622 uc.account = ""
623 uc.logger.Printf("logged out")
624 uc.forEachDownstream(func(dc *downstreamConn) {
625 dc.updateAccount()
626 })
627 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
628 var info string
629 if err := parseMessageParams(msg, nil, &info); err != nil {
630 return err
631 }
632 switch msg.Command {
633 case irc.ERR_NICKLOCKED:
634 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
635 case irc.ERR_SASLFAIL:
636 uc.logger.Printf("SASL authentication failed: %v", info)
637 case irc.ERR_SASLTOOLONG:
638 uc.logger.Printf("SASL message too long: %v", info)
639 }
640
641 uc.saslClient = nil
642 uc.saslStarted = false
643
644 if dc, _ := uc.dequeueCommand("AUTHENTICATE"); dc != nil && dc.sasl != nil {
645 if msg.Command == irc.RPL_SASLSUCCESS {
646 uc.network.autoSaveSASLPlain(context.TODO(), dc.sasl.plainUsername, dc.sasl.plainPassword)
647 }
648
649 dc.endSASL(msg)
650 }
651
652 if !uc.registered {
653 uc.SendMessage(&irc.Message{
654 Command: "CAP",
655 Params: []string{"END"},
656 })
657 }
658 case "REGISTER", "VERIFY":
659 if dc, cmd := uc.dequeueCommand(msg.Command); dc != nil {
660 if msg.Command == "REGISTER" {
661 var account, password string
662 if err := parseMessageParams(msg, nil, &account); err != nil {
663 return err
664 }
665 if err := parseMessageParams(cmd, nil, nil, &password); err != nil {
666 return err
667 }
668 uc.network.autoSaveSASLPlain(context.TODO(), account, password)
669 }
670
671 dc.SendMessage(msg)
672 }
673 case irc.RPL_WELCOME:
674 uc.registered = true
675 uc.logger.Printf("connection registered")
676
677 if uc.network.channels.Len() > 0 {
678 var channels, keys []string
679 for _, entry := range uc.network.channels.innerMap {
680 ch := entry.value.(*Channel)
681 channels = append(channels, ch.Name)
682 keys = append(keys, ch.Key)
683 }
684
685 for _, msg := range join(channels, keys) {
686 uc.SendMessage(msg)
687 }
688 }
689 case irc.RPL_MYINFO:
690 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
691 return err
692 }
693 case irc.RPL_ISUPPORT:
694 if err := parseMessageParams(msg, nil, nil); err != nil {
695 return err
696 }
697
698 var downstreamIsupport []string
699 for _, token := range msg.Params[1 : len(msg.Params)-1] {
700 parameter := token
701 var negate, hasValue bool
702 var value string
703 if strings.HasPrefix(token, "-") {
704 negate = true
705 token = token[1:]
706 } else if i := strings.IndexByte(token, '='); i >= 0 {
707 parameter = token[:i]
708 value = token[i+1:]
709 hasValue = true
710 }
711
712 if hasValue {
713 uc.isupport[parameter] = &value
714 } else if !negate {
715 uc.isupport[parameter] = nil
716 } else {
717 delete(uc.isupport, parameter)
718 }
719
720 var err error
721 switch parameter {
722 case "CASEMAPPING":
723 casemap, ok := parseCasemappingToken(value)
724 if !ok {
725 casemap = casemapRFC1459
726 }
727 uc.network.updateCasemapping(casemap)
728 uc.nickCM = uc.network.casemap(uc.nick)
729 uc.casemapIsSet = true
730 case "CHANMODES":
731 if !negate {
732 err = uc.handleChanModes(value)
733 } else {
734 uc.availableChannelModes = stdChannelModes
735 }
736 case "CHANTYPES":
737 if !negate {
738 uc.availableChannelTypes = value
739 } else {
740 uc.availableChannelTypes = stdChannelTypes
741 }
742 case "PREFIX":
743 if !negate {
744 err = uc.handleMemberships(value)
745 } else {
746 uc.availableMemberships = stdMemberships
747 }
748 }
749 if err != nil {
750 return err
751 }
752
753 if passthroughIsupport[parameter] {
754 downstreamIsupport = append(downstreamIsupport, token)
755 }
756 }
757
758 uc.forEachDownstream(func(dc *downstreamConn) {
759 if dc.network == nil {
760 return
761 }
762 msgs := generateIsupport(dc.srv.prefix(), dc.nick, downstreamIsupport)
763 for _, msg := range msgs {
764 dc.SendMessage(msg)
765 }
766 })
767 case irc.ERR_NOMOTD, irc.RPL_ENDOFMOTD:
768 if !uc.casemapIsSet {
769 // upstream did not send any CASEMAPPING token, thus
770 // we assume it implements the old RFCs with rfc1459.
771 uc.casemapIsSet = true
772 uc.network.updateCasemapping(casemapRFC1459)
773 uc.nickCM = uc.network.casemap(uc.nick)
774 }
775
776 if !uc.gotMotd {
777 // Ignore the initial MOTD upon connection, but forward
778 // subsequent MOTD messages downstream
779 uc.gotMotd = true
780 return nil
781 }
782
783 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
784 dc.SendMessage(&irc.Message{
785 Prefix: uc.srv.prefix(),
786 Command: msg.Command,
787 Params: msg.Params,
788 })
789 })
790 case "BATCH":
791 var tag string
792 if err := parseMessageParams(msg, &tag); err != nil {
793 return err
794 }
795
796 if strings.HasPrefix(tag, "+") {
797 tag = tag[1:]
798 if _, ok := uc.batches[tag]; ok {
799 return fmt.Errorf("unexpected BATCH reference tag: batch was already defined: %q", tag)
800 }
801 var batchType string
802 if err := parseMessageParams(msg, nil, &batchType); err != nil {
803 return err
804 }
805 label := label
806 if label == "" && msgBatch != nil {
807 label = msgBatch.Label
808 }
809 uc.batches[tag] = batch{
810 Type: batchType,
811 Params: msg.Params[2:],
812 Outer: msgBatch,
813 Label: label,
814 }
815 } else if strings.HasPrefix(tag, "-") {
816 tag = tag[1:]
817 if _, ok := uc.batches[tag]; !ok {
818 return fmt.Errorf("unknown BATCH reference tag: %q", tag)
819 }
820 delete(uc.batches, tag)
821 } else {
822 return fmt.Errorf("unexpected BATCH reference tag: missing +/- prefix: %q", tag)
823 }
824 case "NICK":
825 if msg.Prefix == nil {
826 return fmt.Errorf("expected a prefix")
827 }
828
829 var newNick string
830 if err := parseMessageParams(msg, &newNick); err != nil {
831 return err
832 }
833
834 me := false
835 if uc.isOurNick(msg.Prefix.Name) {
836 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
837 me = true
838 uc.nick = newNick
839 uc.nickCM = uc.network.casemap(uc.nick)
840 }
841
842 for _, entry := range uc.channels.innerMap {
843 ch := entry.value.(*upstreamChannel)
844 memberships := ch.Members.Value(msg.Prefix.Name)
845 if memberships != nil {
846 ch.Members.Delete(msg.Prefix.Name)
847 ch.Members.SetValue(newNick, memberships)
848 uc.appendLog(ch.Name, msg)
849 }
850 }
851
852 if !me {
853 uc.forEachDownstream(func(dc *downstreamConn) {
854 dc.SendMessage(dc.marshalMessage(msg, uc.network))
855 })
856 } else {
857 uc.forEachDownstream(func(dc *downstreamConn) {
858 dc.updateNick()
859 })
860 }
861 case "SETNAME":
862 if msg.Prefix == nil {
863 return fmt.Errorf("expected a prefix")
864 }
865
866 var newRealname string
867 if err := parseMessageParams(msg, &newRealname); err != nil {
868 return err
869 }
870
871 // TODO: consider appending this message to logs
872
873 if uc.isOurNick(msg.Prefix.Name) {
874 uc.logger.Printf("changed realname from %q to %q", uc.realname, newRealname)
875 uc.realname = newRealname
876
877 uc.forEachDownstream(func(dc *downstreamConn) {
878 dc.updateRealname()
879 })
880 } else {
881 uc.forEachDownstream(func(dc *downstreamConn) {
882 dc.SendMessage(dc.marshalMessage(msg, uc.network))
883 })
884 }
885 case "JOIN":
886 if msg.Prefix == nil {
887 return fmt.Errorf("expected a prefix")
888 }
889
890 var channels string
891 if err := parseMessageParams(msg, &channels); err != nil {
892 return err
893 }
894
895 for _, ch := range strings.Split(channels, ",") {
896 if uc.isOurNick(msg.Prefix.Name) {
897 uc.logger.Printf("joined channel %q", ch)
898 members := membershipsCasemapMap{newCasemapMap(0)}
899 members.casemap = uc.network.casemap
900 uc.channels.SetValue(ch, &upstreamChannel{
901 Name: ch,
902 conn: uc,
903 Members: members,
904 })
905 uc.updateChannelAutoDetach(ch)
906
907 uc.SendMessage(&irc.Message{
908 Command: "MODE",
909 Params: []string{ch},
910 })
911 } else {
912 ch, err := uc.getChannel(ch)
913 if err != nil {
914 return err
915 }
916 ch.Members.SetValue(msg.Prefix.Name, &memberships{})
917 }
918
919 chMsg := msg.Copy()
920 chMsg.Params[0] = ch
921 uc.produce(ch, chMsg, nil)
922 }
923 case "PART":
924 if msg.Prefix == nil {
925 return fmt.Errorf("expected a prefix")
926 }
927
928 var channels string
929 if err := parseMessageParams(msg, &channels); err != nil {
930 return err
931 }
932
933 for _, ch := range strings.Split(channels, ",") {
934 if uc.isOurNick(msg.Prefix.Name) {
935 uc.logger.Printf("parted channel %q", ch)
936 uch := uc.channels.Value(ch)
937 if uch != nil {
938 uc.channels.Delete(ch)
939 uch.updateAutoDetach(0)
940 }
941 } else {
942 ch, err := uc.getChannel(ch)
943 if err != nil {
944 return err
945 }
946 ch.Members.Delete(msg.Prefix.Name)
947 }
948
949 chMsg := msg.Copy()
950 chMsg.Params[0] = ch
951 uc.produce(ch, chMsg, nil)
952 }
953 case "KICK":
954 if msg.Prefix == nil {
955 return fmt.Errorf("expected a prefix")
956 }
957
958 var channel, user string
959 if err := parseMessageParams(msg, &channel, &user); err != nil {
960 return err
961 }
962
963 if uc.isOurNick(user) {
964 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
965 uc.channels.Delete(channel)
966 } else {
967 ch, err := uc.getChannel(channel)
968 if err != nil {
969 return err
970 }
971 ch.Members.Delete(user)
972 }
973
974 uc.produce(channel, msg, nil)
975 case "QUIT":
976 if msg.Prefix == nil {
977 return fmt.Errorf("expected a prefix")
978 }
979
980 if uc.isOurNick(msg.Prefix.Name) {
981 uc.logger.Printf("quit")
982 }
983
984 for _, entry := range uc.channels.innerMap {
985 ch := entry.value.(*upstreamChannel)
986 if ch.Members.Has(msg.Prefix.Name) {
987 ch.Members.Delete(msg.Prefix.Name)
988
989 uc.appendLog(ch.Name, msg)
990 }
991 }
992
993 if msg.Prefix.Name != uc.nick {
994 uc.forEachDownstream(func(dc *downstreamConn) {
995 dc.SendMessage(dc.marshalMessage(msg, uc.network))
996 })
997 }
998 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
999 var name, topic string
1000 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
1001 return err
1002 }
1003 ch, err := uc.getChannel(name)
1004 if err != nil {
1005 return err
1006 }
1007 if msg.Command == irc.RPL_TOPIC {
1008 ch.Topic = topic
1009 } else {
1010 ch.Topic = ""
1011 }
1012 case "TOPIC":
1013 if msg.Prefix == nil {
1014 return fmt.Errorf("expected a prefix")
1015 }
1016
1017 var name string
1018 if err := parseMessageParams(msg, &name); err != nil {
1019 return err
1020 }
1021 ch, err := uc.getChannel(name)
1022 if err != nil {
1023 return err
1024 }
1025 if len(msg.Params) > 1 {
1026 ch.Topic = msg.Params[1]
1027 ch.TopicWho = msg.Prefix.Copy()
1028 ch.TopicTime = time.Now() // TODO use msg.Tags["time"]
1029 } else {
1030 ch.Topic = ""
1031 }
1032 uc.produce(ch.Name, msg, nil)
1033 case "MODE":
1034 var name, modeStr string
1035 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
1036 return err
1037 }
1038
1039 if !uc.isChannel(name) { // user mode change
1040 if name != uc.nick {
1041 return fmt.Errorf("received MODE message for unknown nick %q", name)
1042 }
1043
1044 if err := uc.modes.Apply(modeStr); err != nil {
1045 return err
1046 }
1047
1048 uc.forEachDownstream(func(dc *downstreamConn) {
1049 if dc.upstream() == nil {
1050 return
1051 }
1052
1053 dc.SendMessage(msg)
1054 })
1055 } else { // channel mode change
1056 ch, err := uc.getChannel(name)
1057 if err != nil {
1058 return err
1059 }
1060
1061 needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
1062 if err != nil {
1063 return err
1064 }
1065
1066 uc.appendLog(ch.Name, msg)
1067
1068 c := uc.network.channels.Value(name)
1069 if c == nil || !c.Detached {
1070 uc.forEachDownstream(func(dc *downstreamConn) {
1071 params := make([]string, len(msg.Params))
1072 params[0] = dc.marshalEntity(uc.network, name)
1073 params[1] = modeStr
1074
1075 copy(params[2:], msg.Params[2:])
1076 for i, modeParam := range params[2:] {
1077 if _, ok := needMarshaling[i]; ok {
1078 params[2+i] = dc.marshalEntity(uc.network, modeParam)
1079 }
1080 }
1081
1082 dc.SendMessage(&irc.Message{
1083 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1084 Command: "MODE",
1085 Params: params,
1086 })
1087 })
1088 }
1089 }
1090 case irc.RPL_UMODEIS:
1091 if err := parseMessageParams(msg, nil); err != nil {
1092 return err
1093 }
1094 modeStr := ""
1095 if len(msg.Params) > 1 {
1096 modeStr = msg.Params[1]
1097 }
1098
1099 uc.modes = ""
1100 if err := uc.modes.Apply(modeStr); err != nil {
1101 return err
1102 }
1103
1104 uc.forEachDownstream(func(dc *downstreamConn) {
1105 if dc.upstream() == nil {
1106 return
1107 }
1108
1109 dc.SendMessage(msg)
1110 })
1111 case irc.RPL_CHANNELMODEIS:
1112 var channel string
1113 if err := parseMessageParams(msg, nil, &channel); err != nil {
1114 return err
1115 }
1116 modeStr := ""
1117 if len(msg.Params) > 2 {
1118 modeStr = msg.Params[2]
1119 }
1120
1121 ch, err := uc.getChannel(channel)
1122 if err != nil {
1123 return err
1124 }
1125
1126 firstMode := ch.modes == nil
1127 ch.modes = make(map[byte]string)
1128 if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
1129 return err
1130 }
1131 if firstMode {
1132 c := uc.network.channels.Value(channel)
1133 if c == nil || !c.Detached {
1134 modeStr, modeParams := ch.modes.Format()
1135
1136 uc.forEachDownstream(func(dc *downstreamConn) {
1137 params := []string{dc.nick, dc.marshalEntity(uc.network, channel), modeStr}
1138 params = append(params, modeParams...)
1139
1140 dc.SendMessage(&irc.Message{
1141 Prefix: dc.srv.prefix(),
1142 Command: irc.RPL_CHANNELMODEIS,
1143 Params: params,
1144 })
1145 })
1146 }
1147 }
1148 case rpl_creationtime:
1149 var channel, creationTime string
1150 if err := parseMessageParams(msg, nil, &channel, &creationTime); err != nil {
1151 return err
1152 }
1153
1154 ch, err := uc.getChannel(channel)
1155 if err != nil {
1156 return err
1157 }
1158
1159 firstCreationTime := ch.creationTime == ""
1160 ch.creationTime = creationTime
1161 if firstCreationTime {
1162 uc.forEachDownstream(func(dc *downstreamConn) {
1163 dc.SendMessage(&irc.Message{
1164 Prefix: dc.srv.prefix(),
1165 Command: rpl_creationtime,
1166 Params: []string{dc.nick, dc.marshalEntity(uc.network, ch.Name), creationTime},
1167 })
1168 })
1169 }
1170 case rpl_topicwhotime:
1171 var name, who, timeStr string
1172 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
1173 return err
1174 }
1175 ch, err := uc.getChannel(name)
1176 if err != nil {
1177 return err
1178 }
1179 firstTopicWhoTime := ch.TopicWho == nil
1180 ch.TopicWho = irc.ParsePrefix(who)
1181 sec, err := strconv.ParseInt(timeStr, 10, 64)
1182 if err != nil {
1183 return fmt.Errorf("failed to parse topic time: %v", err)
1184 }
1185 ch.TopicTime = time.Unix(sec, 0)
1186 if firstTopicWhoTime {
1187 uc.forEachDownstream(func(dc *downstreamConn) {
1188 topicWho := dc.marshalUserPrefix(uc.network, ch.TopicWho)
1189 dc.SendMessage(&irc.Message{
1190 Prefix: dc.srv.prefix(),
1191 Command: rpl_topicwhotime,
1192 Params: []string{
1193 dc.nick,
1194 dc.marshalEntity(uc.network, ch.Name),
1195 topicWho.String(),
1196 timeStr,
1197 },
1198 })
1199 })
1200 }
1201 case irc.RPL_LIST:
1202 var channel, clients, topic string
1203 if err := parseMessageParams(msg, nil, &channel, &clients, &topic); err != nil {
1204 return err
1205 }
1206
1207 dc, cmd := uc.currentPendingCommand("LIST")
1208 if cmd == nil {
1209 return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
1210 } else if dc == nil {
1211 return nil
1212 }
1213
1214 dc.SendMessage(&irc.Message{
1215 Prefix: dc.srv.prefix(),
1216 Command: irc.RPL_LIST,
1217 Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
1218 })
1219 case irc.RPL_LISTEND:
1220 dc, cmd := uc.dequeueCommand("LIST")
1221 if cmd == nil {
1222 return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
1223 } else if dc == nil {
1224 return nil
1225 }
1226
1227 dc.SendMessage(&irc.Message{
1228 Prefix: dc.srv.prefix(),
1229 Command: irc.RPL_LISTEND,
1230 Params: []string{dc.nick, "End of /LIST"},
1231 })
1232 case irc.RPL_NAMREPLY:
1233 var name, statusStr, members string
1234 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
1235 return err
1236 }
1237
1238 ch := uc.channels.Value(name)
1239 if ch == nil {
1240 // NAMES on a channel we have not joined, forward to downstream
1241 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1242 channel := dc.marshalEntity(uc.network, name)
1243 members := splitSpace(members)
1244 for i, member := range members {
1245 memberships, nick := uc.parseMembershipPrefix(member)
1246 members[i] = memberships.Format(dc) + dc.marshalEntity(uc.network, nick)
1247 }
1248 memberStr := strings.Join(members, " ")
1249
1250 dc.SendMessage(&irc.Message{
1251 Prefix: dc.srv.prefix(),
1252 Command: irc.RPL_NAMREPLY,
1253 Params: []string{dc.nick, statusStr, channel, memberStr},
1254 })
1255 })
1256 return nil
1257 }
1258
1259 status, err := parseChannelStatus(statusStr)
1260 if err != nil {
1261 return err
1262 }
1263 ch.Status = status
1264
1265 for _, s := range splitSpace(members) {
1266 memberships, nick := uc.parseMembershipPrefix(s)
1267 ch.Members.SetValue(nick, memberships)
1268 }
1269 case irc.RPL_ENDOFNAMES:
1270 var name string
1271 if err := parseMessageParams(msg, nil, &name); err != nil {
1272 return err
1273 }
1274
1275 ch := uc.channels.Value(name)
1276 if ch == nil {
1277 // NAMES on a channel we have not joined, forward to downstream
1278 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1279 channel := dc.marshalEntity(uc.network, name)
1280
1281 dc.SendMessage(&irc.Message{
1282 Prefix: dc.srv.prefix(),
1283 Command: irc.RPL_ENDOFNAMES,
1284 Params: []string{dc.nick, channel, "End of /NAMES list"},
1285 })
1286 })
1287 return nil
1288 }
1289
1290 if ch.complete {
1291 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
1292 }
1293 ch.complete = true
1294
1295 c := uc.network.channels.Value(name)
1296 if c == nil || !c.Detached {
1297 uc.forEachDownstream(func(dc *downstreamConn) {
1298 forwardChannel(dc, ch)
1299 })
1300 }
1301 case irc.RPL_WHOREPLY:
1302 var channel, username, host, server, nick, mode, trailing string
1303 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
1304 return err
1305 }
1306
1307 dc, cmd := uc.currentPendingCommand("WHO")
1308 if cmd == nil {
1309 return fmt.Errorf("unexpected RPL_WHOREPLY: no matching pending WHO")
1310 } else if dc == nil {
1311 return nil
1312 }
1313
1314 parts := strings.SplitN(trailing, " ", 2)
1315 if len(parts) != 2 {
1316 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
1317 }
1318 realname := parts[1]
1319 hops, err := strconv.Atoi(parts[0])
1320 if err != nil {
1321 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
1322 }
1323 hops++
1324
1325 trailing = strconv.Itoa(hops) + " " + realname
1326
1327 if channel != "*" {
1328 channel = dc.marshalEntity(uc.network, channel)
1329 }
1330 nick = dc.marshalEntity(uc.network, nick)
1331 dc.SendMessage(&irc.Message{
1332 Prefix: dc.srv.prefix(),
1333 Command: irc.RPL_WHOREPLY,
1334 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
1335 })
1336 case rpl_whospcrpl:
1337 dc, cmd := uc.currentPendingCommand("WHO")
1338 if cmd == nil {
1339 return fmt.Errorf("unexpected RPL_WHOSPCRPL: no matching pending WHO")
1340 } else if dc == nil {
1341 return nil
1342 }
1343
1344 // Only supported in single-upstream mode, so forward as-is
1345 dc.SendMessage(msg)
1346 case irc.RPL_ENDOFWHO:
1347 var name string
1348 if err := parseMessageParams(msg, nil, &name); err != nil {
1349 return err
1350 }
1351
1352 dc, cmd := uc.dequeueCommand("WHO")
1353 if cmd == nil {
1354 return fmt.Errorf("unexpected RPL_ENDOFWHO: no matching pending WHO")
1355 } else if dc == nil {
1356 return nil
1357 }
1358
1359 mask := "*"
1360 if len(cmd.Params) > 0 {
1361 mask = cmd.Params[0]
1362 }
1363 dc.SendMessage(&irc.Message{
1364 Prefix: dc.srv.prefix(),
1365 Command: irc.RPL_ENDOFWHO,
1366 Params: []string{dc.nick, mask, "End of /WHO list"},
1367 })
1368 case irc.RPL_WHOISUSER:
1369 var nick, username, host, realname string
1370 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
1371 return err
1372 }
1373
1374 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1375 nick := dc.marshalEntity(uc.network, nick)
1376 dc.SendMessage(&irc.Message{
1377 Prefix: dc.srv.prefix(),
1378 Command: irc.RPL_WHOISUSER,
1379 Params: []string{dc.nick, nick, username, host, "*", realname},
1380 })
1381 })
1382 case irc.RPL_WHOISSERVER:
1383 var nick, server, serverInfo string
1384 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
1385 return err
1386 }
1387
1388 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1389 nick := dc.marshalEntity(uc.network, nick)
1390 dc.SendMessage(&irc.Message{
1391 Prefix: dc.srv.prefix(),
1392 Command: irc.RPL_WHOISSERVER,
1393 Params: []string{dc.nick, nick, server, serverInfo},
1394 })
1395 })
1396 case irc.RPL_WHOISOPERATOR:
1397 var nick string
1398 if err := parseMessageParams(msg, nil, &nick); err != nil {
1399 return err
1400 }
1401
1402 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1403 nick := dc.marshalEntity(uc.network, nick)
1404 dc.SendMessage(&irc.Message{
1405 Prefix: dc.srv.prefix(),
1406 Command: irc.RPL_WHOISOPERATOR,
1407 Params: []string{dc.nick, nick, "is an IRC operator"},
1408 })
1409 })
1410 case irc.RPL_WHOISIDLE:
1411 var nick string
1412 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
1413 return err
1414 }
1415
1416 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1417 nick := dc.marshalEntity(uc.network, nick)
1418 params := []string{dc.nick, nick}
1419 params = append(params, msg.Params[2:]...)
1420 dc.SendMessage(&irc.Message{
1421 Prefix: dc.srv.prefix(),
1422 Command: irc.RPL_WHOISIDLE,
1423 Params: params,
1424 })
1425 })
1426 case irc.RPL_WHOISCHANNELS:
1427 var nick, channelList string
1428 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
1429 return err
1430 }
1431 channels := splitSpace(channelList)
1432
1433 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1434 nick := dc.marshalEntity(uc.network, nick)
1435 channelList := make([]string, len(channels))
1436 for i, channel := range channels {
1437 prefix, channel := uc.parseMembershipPrefix(channel)
1438 channel = dc.marshalEntity(uc.network, channel)
1439 channelList[i] = prefix.Format(dc) + channel
1440 }
1441 channels := strings.Join(channelList, " ")
1442 dc.SendMessage(&irc.Message{
1443 Prefix: dc.srv.prefix(),
1444 Command: irc.RPL_WHOISCHANNELS,
1445 Params: []string{dc.nick, nick, channels},
1446 })
1447 })
1448 case irc.RPL_ENDOFWHOIS:
1449 var nick string
1450 if err := parseMessageParams(msg, nil, &nick); err != nil {
1451 return err
1452 }
1453
1454 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1455 nick := dc.marshalEntity(uc.network, nick)
1456 dc.SendMessage(&irc.Message{
1457 Prefix: dc.srv.prefix(),
1458 Command: irc.RPL_ENDOFWHOIS,
1459 Params: []string{dc.nick, nick, "End of /WHOIS list"},
1460 })
1461 })
1462 case "INVITE":
1463 var nick, channel string
1464 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1465 return err
1466 }
1467
1468 weAreInvited := uc.isOurNick(nick)
1469
1470 uc.forEachDownstream(func(dc *downstreamConn) {
1471 if !weAreInvited && !dc.caps["invite-notify"] {
1472 return
1473 }
1474 dc.SendMessage(&irc.Message{
1475 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1476 Command: "INVITE",
1477 Params: []string{dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1478 })
1479 })
1480 case irc.RPL_INVITING:
1481 var nick, channel string
1482 if err := parseMessageParams(msg, nil, &nick, &channel); err != nil {
1483 return err
1484 }
1485
1486 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1487 dc.SendMessage(&irc.Message{
1488 Prefix: dc.srv.prefix(),
1489 Command: irc.RPL_INVITING,
1490 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1491 })
1492 })
1493 case irc.RPL_MONONLINE, irc.RPL_MONOFFLINE:
1494 var targetsStr string
1495 if err := parseMessageParams(msg, nil, &targetsStr); err != nil {
1496 return err
1497 }
1498 targets := strings.Split(targetsStr, ",")
1499
1500 online := msg.Command == irc.RPL_MONONLINE
1501 for _, target := range targets {
1502 prefix := irc.ParsePrefix(target)
1503 uc.monitored.SetValue(prefix.Name, online)
1504 }
1505
1506 uc.forEachDownstream(func(dc *downstreamConn) {
1507 for _, target := range targets {
1508 prefix := irc.ParsePrefix(target)
1509 if dc.monitored.Has(prefix.Name) {
1510 dc.SendMessage(&irc.Message{
1511 Prefix: dc.srv.prefix(),
1512 Command: msg.Command,
1513 Params: []string{dc.nick, target},
1514 })
1515 }
1516 }
1517 })
1518 case irc.ERR_MONLISTFULL:
1519 var limit, targetsStr string
1520 if err := parseMessageParams(msg, nil, &limit, &targetsStr); err != nil {
1521 return err
1522 }
1523
1524 targets := strings.Split(targetsStr, ",")
1525 uc.forEachDownstream(func(dc *downstreamConn) {
1526 for _, target := range targets {
1527 if dc.monitored.Has(target) {
1528 dc.SendMessage(&irc.Message{
1529 Prefix: dc.srv.prefix(),
1530 Command: msg.Command,
1531 Params: []string{dc.nick, limit, target},
1532 })
1533 }
1534 }
1535 })
1536 case irc.RPL_AWAY:
1537 var nick, reason string
1538 if err := parseMessageParams(msg, nil, &nick, &reason); err != nil {
1539 return err
1540 }
1541
1542 uc.forEachDownstream(func(dc *downstreamConn) {
1543 dc.SendMessage(&irc.Message{
1544 Prefix: dc.srv.prefix(),
1545 Command: irc.RPL_AWAY,
1546 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), reason},
1547 })
1548 })
1549 case "AWAY", "ACCOUNT":
1550 if msg.Prefix == nil {
1551 return fmt.Errorf("expected a prefix")
1552 }
1553
1554 uc.forEachDownstream(func(dc *downstreamConn) {
1555 dc.SendMessage(&irc.Message{
1556 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1557 Command: msg.Command,
1558 Params: msg.Params,
1559 })
1560 })
1561 case irc.RPL_BANLIST, irc.RPL_INVITELIST, irc.RPL_EXCEPTLIST:
1562 var channel, mask string
1563 if err := parseMessageParams(msg, nil, &channel, &mask); err != nil {
1564 return err
1565 }
1566 var addNick, addTime string
1567 if len(msg.Params) >= 5 {
1568 addNick = msg.Params[3]
1569 addTime = msg.Params[4]
1570 }
1571
1572 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1573 channel := dc.marshalEntity(uc.network, channel)
1574
1575 var params []string
1576 if addNick != "" && addTime != "" {
1577 addNick := dc.marshalEntity(uc.network, addNick)
1578 params = []string{dc.nick, channel, mask, addNick, addTime}
1579 } else {
1580 params = []string{dc.nick, channel, mask}
1581 }
1582
1583 dc.SendMessage(&irc.Message{
1584 Prefix: dc.srv.prefix(),
1585 Command: msg.Command,
1586 Params: params,
1587 })
1588 })
1589 case irc.RPL_ENDOFBANLIST, irc.RPL_ENDOFINVITELIST, irc.RPL_ENDOFEXCEPTLIST:
1590 var channel, trailing string
1591 if err := parseMessageParams(msg, nil, &channel, &trailing); err != nil {
1592 return err
1593 }
1594
1595 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1596 upstreamChannel := dc.marshalEntity(uc.network, channel)
1597 dc.SendMessage(&irc.Message{
1598 Prefix: dc.srv.prefix(),
1599 Command: msg.Command,
1600 Params: []string{dc.nick, upstreamChannel, trailing},
1601 })
1602 })
1603 case irc.ERR_UNKNOWNCOMMAND, irc.RPL_TRYAGAIN:
1604 var command, reason string
1605 if err := parseMessageParams(msg, nil, &command, &reason); err != nil {
1606 return err
1607 }
1608
1609 if dc, _ := uc.dequeueCommand(command); dc != nil && downstreamID == 0 {
1610 downstreamID = dc.id
1611 }
1612
1613 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1614 dc.SendMessage(&irc.Message{
1615 Prefix: uc.srv.prefix(),
1616 Command: msg.Command,
1617 Params: []string{dc.nick, command, reason},
1618 })
1619 })
1620 case "FAIL":
1621 var command string
1622 if err := parseMessageParams(msg, &command); err != nil {
1623 return err
1624 }
1625
1626 if dc, _ := uc.dequeueCommand(command); dc != nil && downstreamID == 0 {
1627 downstreamID = dc.id
1628 }
1629
1630 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1631 dc.SendMessage(msg)
1632 })
1633 case "ACK":
1634 // Ignore
1635 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1636 // Ignore
1637 case irc.RPL_YOURHOST, irc.RPL_CREATED:
1638 // Ignore
1639 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1640 fallthrough
1641 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
1642 fallthrough
1643 case rpl_localusers, rpl_globalusers:
1644 fallthrough
1645 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
1646 // Ignore these messages if they're part of the initial registration
1647 // message burst. Forward them if the user explicitly asked for them.
1648 if !uc.gotMotd {
1649 return nil
1650 }
1651
1652 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1653 dc.SendMessage(&irc.Message{
1654 Prefix: uc.srv.prefix(),
1655 Command: msg.Command,
1656 Params: msg.Params,
1657 })
1658 })
1659 case irc.RPL_LISTSTART:
1660 // Ignore
1661 case "ERROR":
1662 var text string
1663 if err := parseMessageParams(msg, &text); err != nil {
1664 return err
1665 }
1666 return fmt.Errorf("fatal server error: %v", text)
1667 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
1668 if !uc.registered {
1669 return registrationError{msg}
1670 }
1671 fallthrough
1672 default:
1673 uc.logger.Printf("unhandled message: %v", msg)
1674
1675 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1676 // best effort marshaling for unknown messages, replies and errors:
1677 // most numerics start with the user nick, marshal it if that's the case
1678 // otherwise, conservately keep the params without marshaling
1679 params := msg.Params
1680 if _, err := strconv.Atoi(msg.Command); err == nil { // numeric
1681 if len(msg.Params) > 0 && isOurNick(uc.network, msg.Params[0]) {
1682 params[0] = dc.nick
1683 }
1684 }
1685 dc.SendMessage(&irc.Message{
1686 Prefix: uc.srv.prefix(),
1687 Command: msg.Command,
1688 Params: params,
1689 })
1690 })
1691 }
1692 return nil
1693}
1694
1695func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) {
1696 if uc.network.detachedMessageNeedsRelay(ch, msg) {
1697 uc.forEachDownstream(func(dc *downstreamConn) {
1698 dc.relayDetachedMessage(uc.network, msg)
1699 })
1700 }
1701 if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) {
1702 uc.network.attach(ch)
1703 if err := uc.srv.db.StoreChannel(context.TODO(), uc.network.ID, ch); err != nil {
1704 uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
1705 }
1706 }
1707}
1708
1709func (uc *upstreamConn) handleChanModes(s string) error {
1710 parts := strings.SplitN(s, ",", 5)
1711 if len(parts) < 4 {
1712 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", s)
1713 }
1714 modes := make(map[byte]channelModeType)
1715 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
1716 for j := 0; j < len(parts[i]); j++ {
1717 mode := parts[i][j]
1718 modes[mode] = mt
1719 }
1720 }
1721 uc.availableChannelModes = modes
1722 return nil
1723}
1724
1725func (uc *upstreamConn) handleMemberships(s string) error {
1726 if s == "" {
1727 uc.availableMemberships = nil
1728 return nil
1729 }
1730
1731 if s[0] != '(' {
1732 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1733 }
1734 sep := strings.IndexByte(s, ')')
1735 if sep < 0 || len(s) != sep*2 {
1736 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1737 }
1738 memberships := make([]membership, len(s)/2-1)
1739 for i := range memberships {
1740 memberships[i] = membership{
1741 Mode: s[i+1],
1742 Prefix: s[sep+i+1],
1743 }
1744 }
1745 uc.availableMemberships = memberships
1746 return nil
1747}
1748
1749func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
1750 caps := strings.Fields(capsStr)
1751 for _, s := range caps {
1752 kv := strings.SplitN(s, "=", 2)
1753 k := strings.ToLower(kv[0])
1754 var v string
1755 if len(kv) == 2 {
1756 v = kv[1]
1757 }
1758 uc.supportedCaps[k] = v
1759 }
1760}
1761
1762func (uc *upstreamConn) requestCaps() {
1763 var requestCaps []string
1764 for c := range permanentUpstreamCaps {
1765 if _, ok := uc.supportedCaps[c]; ok && !uc.caps[c] {
1766 requestCaps = append(requestCaps, c)
1767 }
1768 }
1769
1770 if len(requestCaps) == 0 {
1771 return
1772 }
1773
1774 uc.SendMessage(&irc.Message{
1775 Command: "CAP",
1776 Params: []string{"REQ", strings.Join(requestCaps, " ")},
1777 })
1778}
1779
1780func (uc *upstreamConn) supportsSASL(mech string) bool {
1781 v, ok := uc.supportedCaps["sasl"]
1782 if !ok {
1783 return false
1784 }
1785
1786 if v == "" {
1787 return true
1788 }
1789
1790 mechanisms := strings.Split(v, ",")
1791 for _, mech := range mechanisms {
1792 if strings.EqualFold(mech, mech) {
1793 return true
1794 }
1795 }
1796 return false
1797}
1798
1799func (uc *upstreamConn) requestSASL() bool {
1800 if uc.network.SASL.Mechanism == "" {
1801 return false
1802 }
1803 return uc.supportsSASL(uc.network.SASL.Mechanism)
1804}
1805
1806func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1807 uc.caps[name] = ok
1808
1809 switch name {
1810 case "sasl":
1811 if !uc.requestSASL() {
1812 return nil
1813 }
1814 if !ok {
1815 uc.logger.Printf("server refused to acknowledge the SASL capability")
1816 return nil
1817 }
1818
1819 auth := &uc.network.SASL
1820 switch auth.Mechanism {
1821 case "PLAIN":
1822 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1823 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1824 case "EXTERNAL":
1825 uc.logger.Printf("starting SASL EXTERNAL authentication")
1826 uc.saslClient = sasl.NewExternalClient("")
1827 default:
1828 return fmt.Errorf("unsupported SASL mechanism %q", name)
1829 }
1830
1831 uc.SendMessage(&irc.Message{
1832 Command: "AUTHENTICATE",
1833 Params: []string{auth.Mechanism},
1834 })
1835 default:
1836 if permanentUpstreamCaps[name] {
1837 break
1838 }
1839 uc.logger.Printf("received CAP ACK/NAK for a cap we don't support: %v", name)
1840 }
1841 return nil
1842}
1843
1844func splitSpace(s string) []string {
1845 return strings.FieldsFunc(s, func(r rune) bool {
1846 return r == ' '
1847 })
1848}
1849
1850func (uc *upstreamConn) register() {
1851 uc.nick = GetNick(&uc.user.User, &uc.network.Network)
1852 uc.nickCM = uc.network.casemap(uc.nick)
1853 uc.username = GetUsername(&uc.user.User, &uc.network.Network)
1854 uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
1855
1856 uc.SendMessage(&irc.Message{
1857 Command: "CAP",
1858 Params: []string{"LS", "302"},
1859 })
1860
1861 if uc.network.Pass != "" {
1862 uc.SendMessage(&irc.Message{
1863 Command: "PASS",
1864 Params: []string{uc.network.Pass},
1865 })
1866 }
1867
1868 uc.SendMessage(&irc.Message{
1869 Command: "NICK",
1870 Params: []string{uc.nick},
1871 })
1872 uc.SendMessage(&irc.Message{
1873 Command: "USER",
1874 Params: []string{uc.username, "0", "*", uc.realname},
1875 })
1876}
1877
1878func (uc *upstreamConn) ReadMessage() (*irc.Message, error) {
1879 msg, err := uc.conn.ReadMessage()
1880 if err != nil {
1881 return nil, err
1882 }
1883 uc.srv.metrics.upstreamInMessagesTotal.Inc()
1884 return msg, nil
1885}
1886
1887func (uc *upstreamConn) runUntilRegistered() error {
1888 for !uc.registered {
1889 msg, err := uc.ReadMessage()
1890 if err != nil {
1891 return fmt.Errorf("failed to read message: %v", err)
1892 }
1893
1894 if err := uc.handleMessage(msg); err != nil {
1895 if _, ok := err.(registrationError); ok {
1896 return err
1897 } else {
1898 msg.Tags = nil // prevent message tags from cluttering logs
1899 return fmt.Errorf("failed to handle message %q: %v", msg, err)
1900 }
1901 }
1902 }
1903
1904 for _, command := range uc.network.ConnectCommands {
1905 m, err := irc.ParseMessage(command)
1906 if err != nil {
1907 uc.logger.Printf("failed to parse connect command %q: %v", command, err)
1908 } else {
1909 uc.SendMessage(m)
1910 }
1911 }
1912
1913 return nil
1914}
1915
1916func (uc *upstreamConn) readMessages(ch chan<- event) error {
1917 for {
1918 msg, err := uc.ReadMessage()
1919 if errors.Is(err, io.EOF) {
1920 break
1921 } else if err != nil {
1922 return fmt.Errorf("failed to read IRC command: %v", err)
1923 }
1924
1925 ch <- eventUpstreamMessage{msg, uc}
1926 }
1927
1928 return nil
1929}
1930
1931func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1932 if !uc.caps["message-tags"] {
1933 msg = msg.Copy()
1934 msg.Tags = nil
1935 }
1936
1937 uc.srv.metrics.upstreamOutMessagesTotal.Inc()
1938 uc.conn.SendMessage(msg)
1939}
1940
1941func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
1942 if uc.caps["labeled-response"] {
1943 if msg.Tags == nil {
1944 msg.Tags = make(map[string]irc.TagValue)
1945 }
1946 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
1947 uc.nextLabelID++
1948 }
1949 uc.SendMessage(msg)
1950}
1951
1952// appendLog appends a message to the log file.
1953//
1954// The internal message ID is returned. If the message isn't recorded in the
1955// log file, an empty string is returned.
1956func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) (msgID string) {
1957 if uc.user.msgStore == nil {
1958 return ""
1959 }
1960
1961 // Don't store messages with a server mask target
1962 if strings.HasPrefix(entity, "$") {
1963 return ""
1964 }
1965
1966 entityCM := uc.network.casemap(entity)
1967 if entityCM == "nickserv" {
1968 // The messages sent/received from NickServ may contain
1969 // security-related information (like passwords). Don't store these.
1970 return ""
1971 }
1972
1973 if !uc.network.delivered.HasTarget(entity) {
1974 // This is the first message we receive from this target. Save the last
1975 // message ID in delivery receipts, so that we can send the new message
1976 // in the backlog if an offline client reconnects.
1977 lastID, err := uc.user.msgStore.LastMsgID(&uc.network.Network, entityCM, time.Now())
1978 if err != nil {
1979 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
1980 return ""
1981 }
1982
1983 uc.network.delivered.ForEachClient(func(clientName string) {
1984 uc.network.delivered.StoreID(entity, clientName, lastID)
1985 })
1986 }
1987
1988 msgID, err := uc.user.msgStore.Append(&uc.network.Network, entityCM, msg)
1989 if err != nil {
1990 uc.logger.Printf("failed to log message: %v", err)
1991 return ""
1992 }
1993
1994 return msgID
1995}
1996
1997// produce appends a message to the logs and forwards it to connected downstream
1998// connections.
1999//
2000// If origin is not nil and origin doesn't support echo-message, the message is
2001// forwarded to all connections except origin.
2002func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
2003 var msgID string
2004 if target != "" {
2005 msgID = uc.appendLog(target, msg)
2006 }
2007
2008 // Don't forward messages if it's a detached channel
2009 ch := uc.network.channels.Value(target)
2010 detached := ch != nil && ch.Detached
2011
2012 uc.forEachDownstream(func(dc *downstreamConn) {
2013 if !detached && (dc != origin || dc.caps["echo-message"]) {
2014 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
2015 } else {
2016 dc.advanceMessageWithID(msg, msgID)
2017 }
2018 })
2019}
2020
2021func (uc *upstreamConn) updateAway() {
2022 away := true
2023 uc.forEachDownstream(func(*downstreamConn) {
2024 away = false
2025 })
2026 if away == uc.away {
2027 return
2028 }
2029 if away {
2030 uc.SendMessage(&irc.Message{
2031 Command: "AWAY",
2032 Params: []string{"Auto away"},
2033 })
2034 } else {
2035 uc.SendMessage(&irc.Message{
2036 Command: "AWAY",
2037 })
2038 }
2039 uc.away = away
2040}
2041
2042func (uc *upstreamConn) updateChannelAutoDetach(name string) {
2043 uch := uc.channels.Value(name)
2044 if uch == nil {
2045 return
2046 }
2047 ch := uc.network.channels.Value(name)
2048 if ch == nil || ch.Detached {
2049 return
2050 }
2051 uch.updateAutoDetach(ch.DetachAfter)
2052}
2053
2054func (uc *upstreamConn) updateMonitor() {
2055 add := make(map[string]struct{})
2056 var addList []string
2057 seen := make(map[string]struct{})
2058 uc.forEachDownstream(func(dc *downstreamConn) {
2059 for targetCM := range dc.monitored.innerMap {
2060 if !uc.monitored.Has(targetCM) {
2061 if _, ok := add[targetCM]; !ok {
2062 addList = append(addList, targetCM)
2063 }
2064 add[targetCM] = struct{}{}
2065 } else {
2066 seen[targetCM] = struct{}{}
2067 }
2068 }
2069 })
2070
2071 removeAll := true
2072 var removeList []string
2073 for targetCM, entry := range uc.monitored.innerMap {
2074 if _, ok := seen[targetCM]; ok {
2075 removeAll = false
2076 } else {
2077 removeList = append(removeList, entry.originalKey)
2078 }
2079 }
2080
2081 // TODO: better handle the case where len(uc.monitored) + len(addList)
2082 // exceeds the limit, probably by immediately sending ERR_MONLISTFULL?
2083
2084 if removeAll && len(addList) == 0 && len(removeList) > 0 {
2085 // Optimization when the last MONITOR-aware downstream disconnects
2086 uc.SendMessage(&irc.Message{
2087 Command: "MONITOR",
2088 Params: []string{"C"},
2089 })
2090 } else {
2091 msgs := generateMonitor("-", removeList)
2092 msgs = append(msgs, generateMonitor("+", addList)...)
2093 for _, msg := range msgs {
2094 uc.SendMessage(msg)
2095 }
2096 }
2097
2098 for _, target := range removeList {
2099 uc.monitored.Delete(target)
2100 }
2101}
Note: See TracBrowser for help on using the repository browser.