source: code/trunk/downstream.go@ 208

Last change on this file since 208 was 207, checked in by contact, 5 years ago

Fix SQL error logged on JOIN

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

File size: 32.1 KB
RevLine 
[98]1package soju
[13]2
3import (
[91]4 "crypto/tls"
[112]5 "encoding/base64"
[13]6 "fmt"
7 "io"
8 "net"
[108]9 "strconv"
[39]10 "strings"
[105]11 "sync"
[91]12 "time"
[13]13
[112]14 "github.com/emersion/go-sasl"
[85]15 "golang.org/x/crypto/bcrypt"
[13]16 "gopkg.in/irc.v3"
17)
18
19type ircError struct {
20 Message *irc.Message
21}
22
[85]23func (err ircError) Error() string {
24 return err.Message.String()
25}
26
[13]27func newUnknownCommandError(cmd string) ircError {
28 return ircError{&irc.Message{
29 Command: irc.ERR_UNKNOWNCOMMAND,
30 Params: []string{
31 "*",
32 cmd,
33 "Unknown command",
34 },
35 }}
36}
37
38func newNeedMoreParamsError(cmd string) ircError {
39 return ircError{&irc.Message{
40 Command: irc.ERR_NEEDMOREPARAMS,
41 Params: []string{
42 "*",
43 cmd,
44 "Not enough parameters",
45 },
46 }}
47}
48
[85]49var errAuthFailed = ircError{&irc.Message{
50 Command: irc.ERR_PASSWDMISMATCH,
51 Params: []string{"*", "Invalid username or password"},
52}}
[13]53
54type downstreamConn struct {
[188]55 id uint64
56 net net.Conn
57 irc *irc.Conn
58 srv *Server
59 logger Logger
60 outgoing chan *irc.Message
61 closed chan struct{}
[22]62
[100]63 registered bool
64 user *user
65 nick string
66 rawUsername string
[168]67 networkName string
[183]68 clientName string
[100]69 realname string
[141]70 hostname string
[100]71 password string // empty after authentication
72 network *network // can be nil
[105]73
[204]74 ringConsumers map[*network]*RingConsumer
75
[108]76 negociatingCaps bool
77 capVersion int
78 caps map[string]bool
79
[112]80 saslServer sasl.Server
81
[105]82 lock sync.Mutex
83 ourMessages map[*irc.Message]struct{}
[13]84}
85
[154]86func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
[55]87 dc := &downstreamConn{
[204]88 id: id,
89 net: netConn,
90 irc: irc.NewConn(netConn),
91 srv: srv,
92 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
93 outgoing: make(chan *irc.Message, 64),
94 closed: make(chan struct{}),
95 ringConsumers: make(map[*network]*RingConsumer),
96 caps: make(map[string]bool),
97 ourMessages: make(map[*irc.Message]struct{}),
[22]98 }
[141]99 dc.hostname = netConn.RemoteAddr().String()
100 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
101 dc.hostname = host
102 }
[26]103
104 go func() {
[56]105 if err := dc.writeMessages(); err != nil {
106 dc.logger.Printf("failed to write message: %v", err)
[26]107 }
[55]108 if err := dc.net.Close(); err != nil {
109 dc.logger.Printf("failed to close connection: %v", err)
[45]110 } else {
[55]111 dc.logger.Printf("connection closed")
[45]112 }
[26]113 }()
114
[130]115 dc.logger.Printf("new connection")
[55]116 return dc
[22]117}
118
[55]119func (dc *downstreamConn) prefix() *irc.Prefix {
[27]120 return &irc.Prefix{
[55]121 Name: dc.nick,
[184]122 User: dc.user.Username,
[141]123 Host: dc.hostname,
[27]124 }
125}
126
[90]127func (dc *downstreamConn) forEachNetwork(f func(*network)) {
128 if dc.network != nil {
129 f(dc.network)
130 } else {
131 dc.user.forEachNetwork(f)
132 }
133}
134
[73]135func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
136 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]137 if dc.network != nil && uc.network != dc.network {
[73]138 return
139 }
140 f(uc)
141 })
142}
143
[89]144// upstream returns the upstream connection, if any. If there are zero or if
145// there are multiple upstream connections, it returns nil.
146func (dc *downstreamConn) upstream() *upstreamConn {
147 if dc.network == nil {
148 return nil
149 }
[136]150 return dc.network.upstream()
[89]151}
152
[129]153func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
154 if uc.isChannel(entity) {
155 return dc.marshalChannel(uc, entity)
[119]156 }
[129]157 return dc.marshalNick(uc, entity)
[119]158}
159
160func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
[130]161 if dc.network != nil {
[119]162 return name
163 }
164 return name + "/" + uc.network.GetName()
165}
166
[127]167func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]168 if uc := dc.upstream(); uc != nil {
169 return uc, name, nil
170 }
171
[127]172 var conn *upstreamConn
[119]173 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]174 network := name[i+1:]
[119]175 name = name[:i]
176
177 dc.forEachUpstream(func(uc *upstreamConn) {
178 if network != uc.network.GetName() {
179 return
180 }
181 conn = uc
182 })
183 }
184
[127]185 if conn == nil {
[73]186 return nil, "", ircError{&irc.Message{
187 Command: irc.ERR_NOSUCHCHANNEL,
188 Params: []string{name, "No such channel"},
189 }}
[69]190 }
[127]191 return conn, name, nil
[69]192}
193
194func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
195 if nick == uc.nick {
196 return dc.nick
197 }
[130]198 if dc.network != nil {
[119]199 return nick
200 }
201 return nick + "/" + uc.network.GetName()
[69]202}
203
204func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
205 if prefix.Name == uc.nick {
206 return dc.prefix()
207 }
[130]208 if dc.network != nil {
[119]209 return prefix
210 }
211 return &irc.Prefix{
212 Name: prefix.Name + "/" + uc.network.GetName(),
213 User: prefix.User,
214 Host: prefix.Host,
215 }
[69]216}
217
[57]218func (dc *downstreamConn) isClosed() bool {
219 select {
220 case <-dc.closed:
221 return true
222 default:
223 return false
224 }
225}
226
[165]227func (dc *downstreamConn) readMessages(ch chan<- event) error {
[22]228 for {
[55]229 msg, err := dc.irc.ReadMessage()
[22]230 if err == io.EOF {
231 break
232 } else if err != nil {
233 return fmt.Errorf("failed to read IRC command: %v", err)
234 }
235
[64]236 if dc.srv.Debug {
237 dc.logger.Printf("received: %v", msg)
238 }
239
[165]240 ch <- eventDownstreamMessage{msg, dc}
[22]241 }
242
[45]243 return nil
[22]244}
245
[56]246func (dc *downstreamConn) writeMessages() error {
[205]247 // TODO: any SendMessage call after the connection is closed will
248 // either block or drop
[57]249 for {
250 var err error
251 var closed bool
252 select {
[102]253 case msg := <-dc.outgoing:
[64]254 if dc.srv.Debug {
255 dc.logger.Printf("sent: %v", msg)
256 }
[205]257 dc.net.SetWriteDeadline(time.Now().Add(writeTimeout))
[57]258 err = dc.irc.WriteMessage(msg)
259 case <-dc.closed:
260 closed = true
261 }
262 if err != nil {
[56]263 return err
264 }
[57]265 if closed {
266 break
267 }
[56]268 }
269 return nil
270}
271
[180]272// Close closes the connection. It is safe to call from any goroutine.
[55]273func (dc *downstreamConn) Close() error {
[57]274 if dc.isClosed() {
[26]275 return fmt.Errorf("downstream connection already closed")
276 }
[57]277 close(dc.closed)
[45]278 return nil
[13]279}
280
[180]281// SendMessage queues a new outgoing message. It is safe to call from any
282// goroutine.
[55]283func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[191]284 // TODO: strip tags if the client doesn't support them (see runNetwork)
[102]285 dc.outgoing <- msg
[54]286}
287
[55]288func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]289 switch msg.Command {
[28]290 case "QUIT":
[55]291 return dc.Close()
[13]292 default:
[55]293 if dc.registered {
294 return dc.handleMessageRegistered(msg)
[13]295 } else {
[55]296 return dc.handleMessageUnregistered(msg)
[13]297 }
298 }
299}
300
[55]301func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]302 switch msg.Command {
303 case "NICK":
[117]304 var nick string
305 if err := parseMessageParams(msg, &nick); err != nil {
[43]306 return err
[13]307 }
[117]308 if nick == serviceNick {
309 return ircError{&irc.Message{
310 Command: irc.ERR_NICKNAMEINUSE,
311 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
312 }}
313 }
314 dc.nick = nick
[13]315 case "USER":
[117]316 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]317 return err
[13]318 }
[85]319 case "PASS":
320 if err := parseMessageParams(msg, &dc.password); err != nil {
321 return err
322 }
[108]323 case "CAP":
324 var subCmd string
325 if err := parseMessageParams(msg, &subCmd); err != nil {
326 return err
327 }
328 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
329 return err
330 }
[112]331 case "AUTHENTICATE":
332 if !dc.caps["sasl"] {
333 return ircError{&irc.Message{
[125]334 Command: irc.ERR_SASLFAIL,
[112]335 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
336 }}
337 }
338 if len(msg.Params) == 0 {
339 return ircError{&irc.Message{
[125]340 Command: irc.ERR_SASLFAIL,
[112]341 Params: []string{"*", "Missing AUTHENTICATE argument"},
342 }}
343 }
344 if dc.nick == "" {
345 return ircError{&irc.Message{
[125]346 Command: irc.ERR_SASLFAIL,
[112]347 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
348 }}
349 }
350
351 var resp []byte
352 if dc.saslServer == nil {
353 mech := strings.ToUpper(msg.Params[0])
354 switch mech {
355 case "PLAIN":
356 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
357 return dc.authenticate(username, password)
358 }))
359 default:
360 return ircError{&irc.Message{
[125]361 Command: irc.ERR_SASLFAIL,
[112]362 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
363 }}
364 }
365 } else if msg.Params[0] == "*" {
366 dc.saslServer = nil
367 return ircError{&irc.Message{
[125]368 Command: irc.ERR_SASLABORTED,
[112]369 Params: []string{"*", "SASL authentication aborted"},
370 }}
371 } else if msg.Params[0] == "+" {
372 resp = nil
373 } else {
374 // TODO: multi-line messages
375 var err error
376 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
377 if err != nil {
378 dc.saslServer = nil
379 return ircError{&irc.Message{
[125]380 Command: irc.ERR_SASLFAIL,
[112]381 Params: []string{"*", "Invalid base64-encoded response"},
382 }}
383 }
384 }
385
386 challenge, done, err := dc.saslServer.Next(resp)
387 if err != nil {
388 dc.saslServer = nil
389 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
390 return ircError{&irc.Message{
[125]391 Command: irc.ERR_SASLFAIL,
[112]392 Params: []string{"*", ircErr.Message.Params[1]},
393 }}
394 }
395 dc.SendMessage(&irc.Message{
396 Prefix: dc.srv.prefix(),
[125]397 Command: irc.ERR_SASLFAIL,
[112]398 Params: []string{"*", "SASL error"},
399 })
400 return fmt.Errorf("SASL authentication failed: %v", err)
401 } else if done {
402 dc.saslServer = nil
403 dc.SendMessage(&irc.Message{
404 Prefix: dc.srv.prefix(),
[125]405 Command: irc.RPL_LOGGEDIN,
[112]406 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
407 })
408 dc.SendMessage(&irc.Message{
409 Prefix: dc.srv.prefix(),
[125]410 Command: irc.RPL_SASLSUCCESS,
[112]411 Params: []string{dc.nick, "SASL authentication successful"},
412 })
413 } else {
414 challengeStr := "+"
[135]415 if len(challenge) > 0 {
[112]416 challengeStr = base64.StdEncoding.EncodeToString(challenge)
417 }
418
419 // TODO: multi-line messages
420 dc.SendMessage(&irc.Message{
421 Prefix: dc.srv.prefix(),
422 Command: "AUTHENTICATE",
423 Params: []string{challengeStr},
424 })
425 }
[13]426 default:
[55]427 dc.logger.Printf("unhandled message: %v", msg)
[13]428 return newUnknownCommandError(msg.Command)
429 }
[108]430 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]431 return dc.register()
[13]432 }
433 return nil
434}
435
[108]436func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]437 cmd = strings.ToUpper(cmd)
438
[108]439 replyTo := dc.nick
440 if !dc.registered {
441 replyTo = "*"
442 }
443
444 switch cmd {
445 case "LS":
446 if len(args) > 0 {
447 var err error
448 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
449 return err
450 }
451 }
452
[194]453 caps := []string{"message-tags", "server-time"}
454
[112]455 if dc.capVersion >= 302 {
[108]456 caps = append(caps, "sasl=PLAIN")
457 } else {
458 caps = append(caps, "sasl")
[112]459 }
[108]460
461 // TODO: multi-line replies
462 dc.SendMessage(&irc.Message{
463 Prefix: dc.srv.prefix(),
464 Command: "CAP",
465 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
466 })
467
468 if !dc.registered {
469 dc.negociatingCaps = true
470 }
471 case "LIST":
472 var caps []string
473 for name := range dc.caps {
474 caps = append(caps, name)
475 }
476
477 // TODO: multi-line replies
478 dc.SendMessage(&irc.Message{
479 Prefix: dc.srv.prefix(),
480 Command: "CAP",
481 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
482 })
483 case "REQ":
484 if len(args) == 0 {
485 return ircError{&irc.Message{
486 Command: err_invalidcapcmd,
487 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
488 }}
489 }
490
491 caps := strings.Fields(args[0])
492 ack := true
493 for _, name := range caps {
494 name = strings.ToLower(name)
495 enable := !strings.HasPrefix(name, "-")
496 if !enable {
497 name = strings.TrimPrefix(name, "-")
498 }
499
500 enabled := dc.caps[name]
501 if enable == enabled {
502 continue
503 }
504
505 switch name {
[194]506 case "sasl", "message-tags", "server-time":
[112]507 dc.caps[name] = enable
[108]508 default:
509 ack = false
510 }
511 }
512
513 reply := "NAK"
514 if ack {
515 reply = "ACK"
516 }
517 dc.SendMessage(&irc.Message{
518 Prefix: dc.srv.prefix(),
519 Command: "CAP",
520 Params: []string{replyTo, reply, args[0]},
521 })
522 case "END":
523 dc.negociatingCaps = false
524 default:
525 return ircError{&irc.Message{
526 Command: err_invalidcapcmd,
527 Params: []string{replyTo, cmd, "Unknown CAP command"},
528 }}
529 }
530 return nil
531}
532
[91]533func sanityCheckServer(addr string) error {
534 dialer := net.Dialer{Timeout: 30 * time.Second}
535 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
536 if err != nil {
537 return err
538 }
539 return conn.Close()
540}
541
[183]542func unmarshalUsername(rawUsername string) (username, client, network string) {
[112]543 username = rawUsername
[183]544
545 i := strings.IndexAny(username, "/@")
546 j := strings.LastIndexAny(username, "/@")
547 if i >= 0 {
548 username = rawUsername[:i]
[73]549 }
[183]550 if j >= 0 {
[190]551 if rawUsername[j] == '@' {
552 client = rawUsername[j+1:]
553 } else {
554 network = rawUsername[j+1:]
555 }
[73]556 }
[183]557 if i >= 0 && j >= 0 && i < j {
[190]558 if rawUsername[i] == '@' {
559 client = rawUsername[i+1 : j]
560 } else {
561 network = rawUsername[i+1 : j]
562 }
[183]563 }
564
565 return username, client, network
[112]566}
[73]567
[168]568func (dc *downstreamConn) authenticate(username, password string) error {
[183]569 username, clientName, networkName := unmarshalUsername(username)
[168]570
[173]571 u, err := dc.srv.db.GetUser(username)
572 if err != nil {
573 dc.logger.Printf("failed authentication for %q: %v", username, err)
[168]574 return errAuthFailed
575 }
576
[173]577 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
[168]578 if err != nil {
579 dc.logger.Printf("failed authentication for %q: %v", username, err)
580 return errAuthFailed
581 }
582
[173]583 dc.user = dc.srv.getUser(username)
584 if dc.user == nil {
585 dc.logger.Printf("failed authentication for %q: user not active", username)
586 return errAuthFailed
587 }
[183]588 dc.clientName = clientName
[168]589 dc.networkName = networkName
590 return nil
591}
592
593func (dc *downstreamConn) register() error {
594 if dc.registered {
595 return fmt.Errorf("tried to register twice")
596 }
597
598 password := dc.password
599 dc.password = ""
600 if dc.user == nil {
601 if err := dc.authenticate(dc.rawUsername, password); err != nil {
602 return err
603 }
604 }
605
[183]606 if dc.clientName == "" && dc.networkName == "" {
607 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
[168]608 }
609
610 dc.registered = true
[184]611 dc.logger.Printf("registration complete for user %q", dc.user.Username)
[168]612 return nil
613}
614
615func (dc *downstreamConn) loadNetwork() error {
616 if dc.networkName == "" {
[112]617 return nil
618 }
[85]619
[168]620 network := dc.user.getNetwork(dc.networkName)
[112]621 if network == nil {
[168]622 addr := dc.networkName
[112]623 if !strings.ContainsRune(addr, ':') {
624 addr = addr + ":6697"
625 }
626
627 dc.logger.Printf("trying to connect to new network %q", addr)
628 if err := sanityCheckServer(addr); err != nil {
629 dc.logger.Printf("failed to connect to %q: %v", addr, err)
630 return ircError{&irc.Message{
631 Command: irc.ERR_PASSWDMISMATCH,
[168]632 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
[112]633 }}
634 }
635
[168]636 dc.logger.Printf("auto-saving network %q", dc.networkName)
[112]637 var err error
[120]638 network, err = dc.user.createNetwork(&Network{
[168]639 Addr: dc.networkName,
[120]640 Nick: dc.nick,
641 })
[112]642 if err != nil {
643 return err
644 }
645 }
646
647 dc.network = network
648 return nil
649}
650
[168]651func (dc *downstreamConn) welcome() error {
652 if dc.user == nil || !dc.registered {
653 panic("tried to welcome an unregistered connection")
[37]654 }
655
[168]656 // TODO: doing this might take some time. We should do it in dc.register
657 // instead, but we'll potentially be adding a new network and this must be
658 // done in the user goroutine.
659 if err := dc.loadNetwork(); err != nil {
660 return err
[85]661 }
662
[185]663 // Only send history if we're the first connected client with that name and
664 // network
665 sendHistory := true
666 dc.user.forEachDownstream(func(conn *downstreamConn) {
667 if dc.clientName == conn.clientName && dc.network == conn.network {
668 sendHistory = false
669 }
670 })
[40]671
[55]672 dc.SendMessage(&irc.Message{
673 Prefix: dc.srv.prefix(),
[13]674 Command: irc.RPL_WELCOME,
[98]675 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]676 })
[55]677 dc.SendMessage(&irc.Message{
678 Prefix: dc.srv.prefix(),
[13]679 Command: irc.RPL_YOURHOST,
[55]680 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]681 })
[55]682 dc.SendMessage(&irc.Message{
683 Prefix: dc.srv.prefix(),
[13]684 Command: irc.RPL_CREATED,
[55]685 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]686 })
[55]687 dc.SendMessage(&irc.Message{
688 Prefix: dc.srv.prefix(),
[13]689 Command: irc.RPL_MYINFO,
[98]690 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]691 })
[93]692 // TODO: RPL_ISUPPORT
[55]693 dc.SendMessage(&irc.Message{
694 Prefix: dc.srv.prefix(),
[13]695 Command: irc.ERR_NOMOTD,
[55]696 Params: []string{dc.nick, "No MOTD"},
[54]697 })
[13]698
[73]699 dc.forEachUpstream(func(uc *upstreamConn) {
[30]700 for _, ch := range uc.channels {
701 if ch.complete {
[132]702 dc.SendMessage(&irc.Message{
703 Prefix: dc.prefix(),
704 Command: "JOIN",
705 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
706 })
707
[55]708 forwardChannel(dc, ch)
[30]709 }
710 }
[143]711 })
[50]712
[143]713 dc.forEachNetwork(func(net *network) {
[185]714 dc.runNetwork(net, sendHistory)
[144]715 })
[57]716
[144]717 return nil
718}
719
720// runNetwork starts listening for messages coming from the network's ring
721// buffer.
722//
723// It panics if the network is not suitable for the downstream connection.
724func (dc *downstreamConn) runNetwork(net *network, loadHistory bool) {
725 if dc.network != nil && net != dc.network {
726 panic("network not suitable for downstream connection")
727 }
728
729 var seqPtr *uint64
730 if loadHistory {
[185]731 seq, ok := net.history[dc.clientName]
[144]732 if ok {
733 seqPtr = &seq
[50]734 }
[144]735 }
[57]736
[191]737 // TODO: can't be enabled/disabled on-the-fly
738 msgTagsEnabled := dc.caps["message-tags"]
[194]739 serverTimeEnabled := dc.caps["server-time"]
[191]740
[144]741 consumer, ch := net.ring.NewConsumer(seqPtr)
[204]742
743 if _, ok := dc.ringConsumers[net]; ok {
744 panic("network has been added twice")
745 }
746 dc.ringConsumers[net] = consumer
747
[144]748 go func() {
[204]749 for range ch {
750 uc := net.upstream()
751 if uc == nil {
752 dc.logger.Printf("ignoring messages for upstream %q: upstream is disconnected", net.Addr)
753 continue
754 }
755
756 for {
757 msg := consumer.Peek()
758 if msg == nil {
[203]759 break
760 }
761
[204]762 dc.lock.Lock()
763 _, ours := dc.ourMessages[msg]
764 delete(dc.ourMessages, msg)
765 dc.lock.Unlock()
766 if ours {
767 // The message comes from our connection, don't echo it
768 // back
769 consumer.Consume()
770 continue
[57]771 }
[188]772
[204]773 msg = msg.Copy()
774 switch msg.Command {
775 case "PRIVMSG":
776 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
777 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
778 default:
779 panic("expected to consume a PRIVMSG message")
780 }
[188]781
[204]782 if !msgTagsEnabled {
783 for name := range msg.Tags {
784 supported := false
785 switch name {
786 case "time":
787 supported = serverTimeEnabled
[194]788 }
[204]789 if !supported {
790 delete(msg.Tags, name)
791 }
[191]792 }
[204]793 }
[191]794
[204]795 dc.SendMessage(msg)
796 consumer.Consume()
[57]797 }
[144]798 }
799 }()
[13]800}
801
[103]802func (dc *downstreamConn) runUntilRegistered() error {
803 for !dc.registered {
804 msg, err := dc.irc.ReadMessage()
[106]805 if err != nil {
[103]806 return fmt.Errorf("failed to read IRC command: %v", err)
807 }
808
[110]809 if dc.srv.Debug {
810 dc.logger.Printf("received: %v", msg)
811 }
812
[103]813 err = dc.handleMessage(msg)
814 if ircErr, ok := err.(ircError); ok {
815 ircErr.Message.Prefix = dc.srv.prefix()
816 dc.SendMessage(ircErr.Message)
817 } else if err != nil {
818 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
819 }
820 }
821
822 return nil
823}
824
[55]825func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]826 switch msg.Command {
[111]827 case "CAP":
828 var subCmd string
829 if err := parseMessageParams(msg, &subCmd); err != nil {
830 return err
831 }
832 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
833 return err
834 }
[107]835 case "PING":
836 dc.SendMessage(&irc.Message{
837 Prefix: dc.srv.prefix(),
838 Command: "PONG",
839 Params: msg.Params,
840 })
841 return nil
[42]842 case "USER":
[13]843 return ircError{&irc.Message{
844 Command: irc.ERR_ALREADYREGISTERED,
[55]845 Params: []string{dc.nick, "You may not reregister"},
[13]846 }}
[42]847 case "NICK":
[90]848 var nick string
849 if err := parseMessageParams(msg, &nick); err != nil {
850 return err
851 }
852
853 var err error
854 dc.forEachNetwork(func(n *network) {
855 if err != nil {
856 return
857 }
858 n.Nick = nick
859 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
860 })
861 if err != nil {
862 return err
863 }
864
[73]865 dc.forEachUpstream(func(uc *upstreamConn) {
[60]866 uc.SendMessage(msg)
[42]867 })
[146]868 case "JOIN":
869 var namesStr string
870 if err := parseMessageParams(msg, &namesStr); err != nil {
[48]871 return err
872 }
873
[146]874 var keys []string
875 if len(msg.Params) > 1 {
876 keys = strings.Split(msg.Params[1], ",")
877 }
878
879 for i, name := range strings.Split(namesStr, ",") {
[145]880 uc, upstreamName, err := dc.unmarshalEntity(name)
881 if err != nil {
[158]882 return err
[145]883 }
[48]884
[146]885 var key string
886 if len(keys) > i {
887 key = keys[i]
888 }
889
890 params := []string{upstreamName}
891 if key != "" {
892 params = append(params, key)
893 }
[145]894 uc.SendMessage(&irc.Message{
[146]895 Command: "JOIN",
896 Params: params,
[145]897 })
[89]898
[207]899 ch, err := dc.srv.db.GetChannel(uc.network.ID, upstreamName)
900 if err == ErrNoSuchChannel {
901 ch = &Channel{Name: upstreamName}
902 } else if err != nil {
903 return err
[89]904 }
[207]905
906 ch.Key = key
907
908 if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
909 return err
910 }
[89]911 }
[146]912 case "PART":
913 var namesStr string
914 if err := parseMessageParams(msg, &namesStr); err != nil {
915 return err
916 }
917
918 var reason string
919 if len(msg.Params) > 1 {
920 reason = msg.Params[1]
921 }
922
923 for _, name := range strings.Split(namesStr, ",") {
924 uc, upstreamName, err := dc.unmarshalEntity(name)
925 if err != nil {
[158]926 return err
[146]927 }
928
929 params := []string{upstreamName}
930 if reason != "" {
931 params = append(params, reason)
932 }
933 uc.SendMessage(&irc.Message{
934 Command: "PART",
935 Params: params,
936 })
937
938 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
939 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
940 }
941 }
[159]942 case "KICK":
943 var channelStr, userStr string
944 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
945 return err
946 }
947
948 channels := strings.Split(channelStr, ",")
949 users := strings.Split(userStr, ",")
950
951 var reason string
952 if len(msg.Params) > 2 {
953 reason = msg.Params[2]
954 }
955
956 if len(channels) != 1 && len(channels) != len(users) {
957 return ircError{&irc.Message{
958 Command: irc.ERR_BADCHANMASK,
959 Params: []string{dc.nick, channelStr, "Bad channel mask"},
960 }}
961 }
962
963 for i, user := range users {
964 var channel string
965 if len(channels) == 1 {
966 channel = channels[0]
967 } else {
968 channel = channels[i]
969 }
970
971 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
972 if err != nil {
973 return err
974 }
975
976 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
977 if err != nil {
978 return err
979 }
980
981 if ucChannel != ucUser {
982 return ircError{&irc.Message{
983 Command: irc.ERR_USERNOTINCHANNEL,
984 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
985 }}
986 }
987 uc := ucChannel
988
989 params := []string{upstreamChannel, upstreamUser}
990 if reason != "" {
991 params = append(params, reason)
992 }
993 uc.SendMessage(&irc.Message{
994 Command: "KICK",
995 Params: params,
996 })
997 }
[69]998 case "MODE":
[46]999 var name string
1000 if err := parseMessageParams(msg, &name); err != nil {
1001 return err
1002 }
1003
1004 var modeStr string
1005 if len(msg.Params) > 1 {
1006 modeStr = msg.Params[1]
1007 }
1008
[139]1009 if name == dc.nick {
[46]1010 if modeStr != "" {
[73]1011 dc.forEachUpstream(func(uc *upstreamConn) {
[69]1012 uc.SendMessage(&irc.Message{
1013 Command: "MODE",
1014 Params: []string{uc.nick, modeStr},
1015 })
[46]1016 })
1017 } else {
[55]1018 dc.SendMessage(&irc.Message{
1019 Prefix: dc.srv.prefix(),
[46]1020 Command: irc.RPL_UMODEIS,
[129]1021 Params: []string{dc.nick, ""}, // TODO
[54]1022 })
[46]1023 }
[139]1024 return nil
[46]1025 }
[139]1026
1027 uc, upstreamName, err := dc.unmarshalEntity(name)
1028 if err != nil {
1029 return err
1030 }
1031
1032 if !uc.isChannel(upstreamName) {
1033 return ircError{&irc.Message{
1034 Command: irc.ERR_USERSDONTMATCH,
1035 Params: []string{dc.nick, "Cannot change mode for other users"},
1036 }}
1037 }
1038
1039 if modeStr != "" {
1040 params := []string{upstreamName, modeStr}
1041 params = append(params, msg.Params[2:]...)
1042 uc.SendMessage(&irc.Message{
1043 Command: "MODE",
1044 Params: params,
1045 })
1046 } else {
1047 ch, ok := uc.channels[upstreamName]
1048 if !ok {
1049 return ircError{&irc.Message{
1050 Command: irc.ERR_NOSUCHCHANNEL,
1051 Params: []string{dc.nick, name, "No such channel"},
1052 }}
1053 }
1054
1055 if ch.modes == nil {
1056 // we haven't received the initial RPL_CHANNELMODEIS yet
1057 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1058 return nil
1059 }
1060
1061 modeStr, modeParams := ch.modes.Format()
1062 params := []string{dc.nick, name, modeStr}
1063 params = append(params, modeParams...)
1064
1065 dc.SendMessage(&irc.Message{
1066 Prefix: dc.srv.prefix(),
1067 Command: irc.RPL_CHANNELMODEIS,
1068 Params: params,
1069 })
[162]1070 if ch.creationTime != "" {
1071 dc.SendMessage(&irc.Message{
1072 Prefix: dc.srv.prefix(),
1073 Command: rpl_creationtime,
1074 Params: []string{dc.nick, name, ch.creationTime},
1075 })
1076 }
[139]1077 }
[160]1078 case "TOPIC":
1079 var channel string
1080 if err := parseMessageParams(msg, &channel); err != nil {
1081 return err
1082 }
1083
1084 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1085 if err != nil {
1086 return err
1087 }
1088
1089 if len(msg.Params) > 1 { // setting topic
1090 topic := msg.Params[1]
1091 uc.SendMessage(&irc.Message{
1092 Command: "TOPIC",
1093 Params: []string{upstreamChannel, topic},
1094 })
1095 } else { // getting topic
1096 ch, ok := uc.channels[upstreamChannel]
1097 if !ok {
1098 return ircError{&irc.Message{
1099 Command: irc.ERR_NOSUCHCHANNEL,
1100 Params: []string{dc.nick, upstreamChannel, "No such channel"},
1101 }}
1102 }
1103 sendTopic(dc, ch)
1104 }
[177]1105 case "LIST":
1106 // TODO: support ELIST when supported by all upstreams
1107
1108 pl := pendingLIST{
1109 downstreamID: dc.id,
1110 pendingCommands: make(map[int64]*irc.Message),
1111 }
1112 var upstreamChannels map[int64][]string
1113 if len(msg.Params) > 0 {
1114 upstreamChannels = make(map[int64][]string)
1115 channels := strings.Split(msg.Params[0], ",")
1116 for _, channel := range channels {
1117 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1118 if err != nil {
1119 return err
1120 }
1121 upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
1122 }
1123 }
1124
1125 dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
1126 dc.forEachUpstream(func(uc *upstreamConn) {
1127 var params []string
1128 if upstreamChannels != nil {
1129 if channels, ok := upstreamChannels[uc.network.ID]; ok {
1130 params = []string{strings.Join(channels, ",")}
1131 } else {
1132 return
1133 }
1134 }
1135 pl.pendingCommands[uc.network.ID] = &irc.Message{
1136 Command: "LIST",
1137 Params: params,
1138 }
[181]1139 uc.trySendLIST(dc.id)
[177]1140 })
[140]1141 case "NAMES":
1142 if len(msg.Params) == 0 {
1143 dc.SendMessage(&irc.Message{
1144 Prefix: dc.srv.prefix(),
1145 Command: irc.RPL_ENDOFNAMES,
1146 Params: []string{dc.nick, "*", "End of /NAMES list"},
1147 })
1148 return nil
1149 }
1150
1151 channels := strings.Split(msg.Params[0], ",")
1152 for _, channel := range channels {
1153 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1154 if err != nil {
1155 return err
1156 }
1157
1158 ch, ok := uc.channels[upstreamChannel]
1159 if ok {
1160 sendNames(dc, ch)
1161 } else {
1162 // NAMES on a channel we have not joined, ask upstream
[176]1163 uc.SendMessageLabeled(dc.id, &irc.Message{
[140]1164 Command: "NAMES",
1165 Params: []string{upstreamChannel},
1166 })
1167 }
1168 }
[127]1169 case "WHO":
1170 if len(msg.Params) == 0 {
1171 // TODO: support WHO without parameters
1172 dc.SendMessage(&irc.Message{
1173 Prefix: dc.srv.prefix(),
1174 Command: irc.RPL_ENDOFWHO,
[140]1175 Params: []string{dc.nick, "*", "End of /WHO list"},
[127]1176 })
1177 return nil
1178 }
1179
1180 // TODO: support WHO masks
1181 entity := msg.Params[0]
1182
[142]1183 if entity == dc.nick {
1184 // TODO: support AWAY (H/G) in self WHO reply
1185 dc.SendMessage(&irc.Message{
1186 Prefix: dc.srv.prefix(),
1187 Command: irc.RPL_WHOREPLY,
[184]1188 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
[142]1189 })
1190 dc.SendMessage(&irc.Message{
1191 Prefix: dc.srv.prefix(),
1192 Command: irc.RPL_ENDOFWHO,
1193 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1194 })
1195 return nil
1196 }
1197
[127]1198 uc, upstreamName, err := dc.unmarshalEntity(entity)
1199 if err != nil {
1200 return err
1201 }
1202
1203 var params []string
1204 if len(msg.Params) == 2 {
1205 params = []string{upstreamName, msg.Params[1]}
1206 } else {
1207 params = []string{upstreamName}
1208 }
1209
[176]1210 uc.SendMessageLabeled(dc.id, &irc.Message{
[127]1211 Command: "WHO",
1212 Params: params,
1213 })
[128]1214 case "WHOIS":
1215 if len(msg.Params) == 0 {
1216 return ircError{&irc.Message{
1217 Command: irc.ERR_NONICKNAMEGIVEN,
1218 Params: []string{dc.nick, "No nickname given"},
1219 }}
1220 }
1221
1222 var target, mask string
1223 if len(msg.Params) == 1 {
1224 target = ""
1225 mask = msg.Params[0]
1226 } else {
1227 target = msg.Params[0]
1228 mask = msg.Params[1]
1229 }
1230 // TODO: support multiple WHOIS users
1231 if i := strings.IndexByte(mask, ','); i >= 0 {
1232 mask = mask[:i]
1233 }
1234
[142]1235 if mask == dc.nick {
1236 dc.SendMessage(&irc.Message{
1237 Prefix: dc.srv.prefix(),
1238 Command: irc.RPL_WHOISUSER,
[184]1239 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
[142]1240 })
1241 dc.SendMessage(&irc.Message{
1242 Prefix: dc.srv.prefix(),
1243 Command: irc.RPL_WHOISSERVER,
1244 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1245 })
1246 dc.SendMessage(&irc.Message{
1247 Prefix: dc.srv.prefix(),
1248 Command: irc.RPL_ENDOFWHOIS,
1249 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1250 })
1251 return nil
1252 }
1253
[128]1254 // TODO: support WHOIS masks
1255 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1256 if err != nil {
1257 return err
1258 }
1259
1260 var params []string
1261 if target != "" {
1262 params = []string{target, upstreamNick}
1263 } else {
1264 params = []string{upstreamNick}
1265 }
1266
[176]1267 uc.SendMessageLabeled(dc.id, &irc.Message{
[128]1268 Command: "WHOIS",
1269 Params: params,
1270 })
[58]1271 case "PRIVMSG":
1272 var targetsStr, text string
1273 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1274 return err
1275 }
1276
1277 for _, name := range strings.Split(targetsStr, ",") {
[117]1278 if name == serviceNick {
1279 handleServicePRIVMSG(dc, text)
1280 continue
1281 }
1282
[127]1283 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]1284 if err != nil {
1285 return err
1286 }
1287
[95]1288 if upstreamName == "NickServ" {
1289 dc.handleNickServPRIVMSG(uc, text)
1290 }
1291
[69]1292 uc.SendMessage(&irc.Message{
[58]1293 Command: "PRIVMSG",
[69]1294 Params: []string{upstreamName, text},
[60]1295 })
[105]1296
[113]1297 echoMsg := &irc.Message{
1298 Prefix: &irc.Prefix{
1299 Name: uc.nick,
1300 User: uc.username,
1301 },
[114]1302 Command: "PRIVMSG",
[113]1303 Params: []string{upstreamName, text},
1304 }
[105]1305 dc.lock.Lock()
[113]1306 dc.ourMessages[echoMsg] = struct{}{}
[105]1307 dc.lock.Unlock()
1308
[143]1309 uc.network.ring.Produce(echoMsg)
[58]1310 }
[164]1311 case "NOTICE":
1312 var targetsStr, text string
1313 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1314 return err
1315 }
1316
1317 for _, name := range strings.Split(targetsStr, ",") {
1318 uc, upstreamName, err := dc.unmarshalEntity(name)
1319 if err != nil {
1320 return err
1321 }
1322
1323 uc.SendMessage(&irc.Message{
1324 Command: "NOTICE",
1325 Params: []string{upstreamName, text},
1326 })
1327 }
[163]1328 case "INVITE":
1329 var user, channel string
1330 if err := parseMessageParams(msg, &user, &channel); err != nil {
1331 return err
1332 }
1333
1334 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1335 if err != nil {
1336 return err
1337 }
1338
1339 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1340 if err != nil {
1341 return err
1342 }
1343
1344 if ucChannel != ucUser {
1345 return ircError{&irc.Message{
1346 Command: irc.ERR_USERNOTINCHANNEL,
1347 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
1348 }}
1349 }
1350 uc := ucChannel
1351
[176]1352 uc.SendMessageLabeled(dc.id, &irc.Message{
[163]1353 Command: "INVITE",
1354 Params: []string{upstreamUser, upstreamChannel},
1355 })
[13]1356 default:
[55]1357 dc.logger.Printf("unhandled message: %v", msg)
[13]1358 return newUnknownCommandError(msg.Command)
1359 }
[42]1360 return nil
[13]1361}
[95]1362
1363func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1364 username, password, ok := parseNickServCredentials(text, uc.nick)
1365 if !ok {
1366 return
1367 }
1368
1369 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1370 n := uc.network
1371 n.SASL.Mechanism = "PLAIN"
1372 n.SASL.Plain.Username = username
1373 n.SASL.Plain.Password = password
1374 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1375 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1376 }
1377}
1378
1379func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1380 fields := strings.Fields(text)
1381 if len(fields) < 2 {
1382 return "", "", false
1383 }
1384 cmd := strings.ToUpper(fields[0])
1385 params := fields[1:]
1386 switch cmd {
1387 case "REGISTER":
1388 username = nick
1389 password = params[0]
1390 case "IDENTIFY":
1391 if len(params) == 1 {
1392 username = nick
[182]1393 password = params[0]
[95]1394 } else {
1395 username = params[0]
[182]1396 password = params[1]
[95]1397 }
[182]1398 case "SET":
1399 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1400 username = nick
1401 password = params[1]
1402 }
[95]1403 }
1404 return username, password, true
1405}
Note: See TracBrowser for help on using the repository browser.