source: code/trunk/upstream.go@ 737

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

Mark ACCOUNT_REQUIRED error as permanent connection failure

There's no point in retrying to connect in this case.

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