source: code/trunk/upstream.go@ 725

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

Remove sasl cap after registration if network doesn't support it

This will stop clients from trying to issue AUTHENTICATE requests
after connection registration.

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