source: code/trunk/downstream.go@ 205

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

Set write deadlines

References: https://todo.sr.ht/~emersion/soju/26

File size: 32.0 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
[146]899 err = dc.srv.db.StoreChannel(uc.network.ID, &Channel{
900 Name: upstreamName,
901 Key: key,
902 })
903 if err != nil {
904 dc.logger.Printf("failed to create channel %q in DB: %v", upstreamName, err)
[89]905 }
906 }
[146]907 case "PART":
908 var namesStr string
909 if err := parseMessageParams(msg, &namesStr); err != nil {
910 return err
911 }
912
913 var reason string
914 if len(msg.Params) > 1 {
915 reason = msg.Params[1]
916 }
917
918 for _, name := range strings.Split(namesStr, ",") {
919 uc, upstreamName, err := dc.unmarshalEntity(name)
920 if err != nil {
[158]921 return err
[146]922 }
923
924 params := []string{upstreamName}
925 if reason != "" {
926 params = append(params, reason)
927 }
928 uc.SendMessage(&irc.Message{
929 Command: "PART",
930 Params: params,
931 })
932
933 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
934 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
935 }
936 }
[159]937 case "KICK":
938 var channelStr, userStr string
939 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
940 return err
941 }
942
943 channels := strings.Split(channelStr, ",")
944 users := strings.Split(userStr, ",")
945
946 var reason string
947 if len(msg.Params) > 2 {
948 reason = msg.Params[2]
949 }
950
951 if len(channels) != 1 && len(channels) != len(users) {
952 return ircError{&irc.Message{
953 Command: irc.ERR_BADCHANMASK,
954 Params: []string{dc.nick, channelStr, "Bad channel mask"},
955 }}
956 }
957
958 for i, user := range users {
959 var channel string
960 if len(channels) == 1 {
961 channel = channels[0]
962 } else {
963 channel = channels[i]
964 }
965
966 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
967 if err != nil {
968 return err
969 }
970
971 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
972 if err != nil {
973 return err
974 }
975
976 if ucChannel != ucUser {
977 return ircError{&irc.Message{
978 Command: irc.ERR_USERNOTINCHANNEL,
979 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
980 }}
981 }
982 uc := ucChannel
983
984 params := []string{upstreamChannel, upstreamUser}
985 if reason != "" {
986 params = append(params, reason)
987 }
988 uc.SendMessage(&irc.Message{
989 Command: "KICK",
990 Params: params,
991 })
992 }
[69]993 case "MODE":
[46]994 var name string
995 if err := parseMessageParams(msg, &name); err != nil {
996 return err
997 }
998
999 var modeStr string
1000 if len(msg.Params) > 1 {
1001 modeStr = msg.Params[1]
1002 }
1003
[139]1004 if name == dc.nick {
[46]1005 if modeStr != "" {
[73]1006 dc.forEachUpstream(func(uc *upstreamConn) {
[69]1007 uc.SendMessage(&irc.Message{
1008 Command: "MODE",
1009 Params: []string{uc.nick, modeStr},
1010 })
[46]1011 })
1012 } else {
[55]1013 dc.SendMessage(&irc.Message{
1014 Prefix: dc.srv.prefix(),
[46]1015 Command: irc.RPL_UMODEIS,
[129]1016 Params: []string{dc.nick, ""}, // TODO
[54]1017 })
[46]1018 }
[139]1019 return nil
[46]1020 }
[139]1021
1022 uc, upstreamName, err := dc.unmarshalEntity(name)
1023 if err != nil {
1024 return err
1025 }
1026
1027 if !uc.isChannel(upstreamName) {
1028 return ircError{&irc.Message{
1029 Command: irc.ERR_USERSDONTMATCH,
1030 Params: []string{dc.nick, "Cannot change mode for other users"},
1031 }}
1032 }
1033
1034 if modeStr != "" {
1035 params := []string{upstreamName, modeStr}
1036 params = append(params, msg.Params[2:]...)
1037 uc.SendMessage(&irc.Message{
1038 Command: "MODE",
1039 Params: params,
1040 })
1041 } else {
1042 ch, ok := uc.channels[upstreamName]
1043 if !ok {
1044 return ircError{&irc.Message{
1045 Command: irc.ERR_NOSUCHCHANNEL,
1046 Params: []string{dc.nick, name, "No such channel"},
1047 }}
1048 }
1049
1050 if ch.modes == nil {
1051 // we haven't received the initial RPL_CHANNELMODEIS yet
1052 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1053 return nil
1054 }
1055
1056 modeStr, modeParams := ch.modes.Format()
1057 params := []string{dc.nick, name, modeStr}
1058 params = append(params, modeParams...)
1059
1060 dc.SendMessage(&irc.Message{
1061 Prefix: dc.srv.prefix(),
1062 Command: irc.RPL_CHANNELMODEIS,
1063 Params: params,
1064 })
[162]1065 if ch.creationTime != "" {
1066 dc.SendMessage(&irc.Message{
1067 Prefix: dc.srv.prefix(),
1068 Command: rpl_creationtime,
1069 Params: []string{dc.nick, name, ch.creationTime},
1070 })
1071 }
[139]1072 }
[160]1073 case "TOPIC":
1074 var channel string
1075 if err := parseMessageParams(msg, &channel); err != nil {
1076 return err
1077 }
1078
1079 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1080 if err != nil {
1081 return err
1082 }
1083
1084 if len(msg.Params) > 1 { // setting topic
1085 topic := msg.Params[1]
1086 uc.SendMessage(&irc.Message{
1087 Command: "TOPIC",
1088 Params: []string{upstreamChannel, topic},
1089 })
1090 } else { // getting topic
1091 ch, ok := uc.channels[upstreamChannel]
1092 if !ok {
1093 return ircError{&irc.Message{
1094 Command: irc.ERR_NOSUCHCHANNEL,
1095 Params: []string{dc.nick, upstreamChannel, "No such channel"},
1096 }}
1097 }
1098 sendTopic(dc, ch)
1099 }
[177]1100 case "LIST":
1101 // TODO: support ELIST when supported by all upstreams
1102
1103 pl := pendingLIST{
1104 downstreamID: dc.id,
1105 pendingCommands: make(map[int64]*irc.Message),
1106 }
1107 var upstreamChannels map[int64][]string
1108 if len(msg.Params) > 0 {
1109 upstreamChannels = make(map[int64][]string)
1110 channels := strings.Split(msg.Params[0], ",")
1111 for _, channel := range channels {
1112 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1113 if err != nil {
1114 return err
1115 }
1116 upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
1117 }
1118 }
1119
1120 dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
1121 dc.forEachUpstream(func(uc *upstreamConn) {
1122 var params []string
1123 if upstreamChannels != nil {
1124 if channels, ok := upstreamChannels[uc.network.ID]; ok {
1125 params = []string{strings.Join(channels, ",")}
1126 } else {
1127 return
1128 }
1129 }
1130 pl.pendingCommands[uc.network.ID] = &irc.Message{
1131 Command: "LIST",
1132 Params: params,
1133 }
[181]1134 uc.trySendLIST(dc.id)
[177]1135 })
[140]1136 case "NAMES":
1137 if len(msg.Params) == 0 {
1138 dc.SendMessage(&irc.Message{
1139 Prefix: dc.srv.prefix(),
1140 Command: irc.RPL_ENDOFNAMES,
1141 Params: []string{dc.nick, "*", "End of /NAMES list"},
1142 })
1143 return nil
1144 }
1145
1146 channels := strings.Split(msg.Params[0], ",")
1147 for _, channel := range channels {
1148 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1149 if err != nil {
1150 return err
1151 }
1152
1153 ch, ok := uc.channels[upstreamChannel]
1154 if ok {
1155 sendNames(dc, ch)
1156 } else {
1157 // NAMES on a channel we have not joined, ask upstream
[176]1158 uc.SendMessageLabeled(dc.id, &irc.Message{
[140]1159 Command: "NAMES",
1160 Params: []string{upstreamChannel},
1161 })
1162 }
1163 }
[127]1164 case "WHO":
1165 if len(msg.Params) == 0 {
1166 // TODO: support WHO without parameters
1167 dc.SendMessage(&irc.Message{
1168 Prefix: dc.srv.prefix(),
1169 Command: irc.RPL_ENDOFWHO,
[140]1170 Params: []string{dc.nick, "*", "End of /WHO list"},
[127]1171 })
1172 return nil
1173 }
1174
1175 // TODO: support WHO masks
1176 entity := msg.Params[0]
1177
[142]1178 if entity == dc.nick {
1179 // TODO: support AWAY (H/G) in self WHO reply
1180 dc.SendMessage(&irc.Message{
1181 Prefix: dc.srv.prefix(),
1182 Command: irc.RPL_WHOREPLY,
[184]1183 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
[142]1184 })
1185 dc.SendMessage(&irc.Message{
1186 Prefix: dc.srv.prefix(),
1187 Command: irc.RPL_ENDOFWHO,
1188 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1189 })
1190 return nil
1191 }
1192
[127]1193 uc, upstreamName, err := dc.unmarshalEntity(entity)
1194 if err != nil {
1195 return err
1196 }
1197
1198 var params []string
1199 if len(msg.Params) == 2 {
1200 params = []string{upstreamName, msg.Params[1]}
1201 } else {
1202 params = []string{upstreamName}
1203 }
1204
[176]1205 uc.SendMessageLabeled(dc.id, &irc.Message{
[127]1206 Command: "WHO",
1207 Params: params,
1208 })
[128]1209 case "WHOIS":
1210 if len(msg.Params) == 0 {
1211 return ircError{&irc.Message{
1212 Command: irc.ERR_NONICKNAMEGIVEN,
1213 Params: []string{dc.nick, "No nickname given"},
1214 }}
1215 }
1216
1217 var target, mask string
1218 if len(msg.Params) == 1 {
1219 target = ""
1220 mask = msg.Params[0]
1221 } else {
1222 target = msg.Params[0]
1223 mask = msg.Params[1]
1224 }
1225 // TODO: support multiple WHOIS users
1226 if i := strings.IndexByte(mask, ','); i >= 0 {
1227 mask = mask[:i]
1228 }
1229
[142]1230 if mask == dc.nick {
1231 dc.SendMessage(&irc.Message{
1232 Prefix: dc.srv.prefix(),
1233 Command: irc.RPL_WHOISUSER,
[184]1234 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
[142]1235 })
1236 dc.SendMessage(&irc.Message{
1237 Prefix: dc.srv.prefix(),
1238 Command: irc.RPL_WHOISSERVER,
1239 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1240 })
1241 dc.SendMessage(&irc.Message{
1242 Prefix: dc.srv.prefix(),
1243 Command: irc.RPL_ENDOFWHOIS,
1244 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1245 })
1246 return nil
1247 }
1248
[128]1249 // TODO: support WHOIS masks
1250 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1251 if err != nil {
1252 return err
1253 }
1254
1255 var params []string
1256 if target != "" {
1257 params = []string{target, upstreamNick}
1258 } else {
1259 params = []string{upstreamNick}
1260 }
1261
[176]1262 uc.SendMessageLabeled(dc.id, &irc.Message{
[128]1263 Command: "WHOIS",
1264 Params: params,
1265 })
[58]1266 case "PRIVMSG":
1267 var targetsStr, text string
1268 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1269 return err
1270 }
1271
1272 for _, name := range strings.Split(targetsStr, ",") {
[117]1273 if name == serviceNick {
1274 handleServicePRIVMSG(dc, text)
1275 continue
1276 }
1277
[127]1278 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]1279 if err != nil {
1280 return err
1281 }
1282
[95]1283 if upstreamName == "NickServ" {
1284 dc.handleNickServPRIVMSG(uc, text)
1285 }
1286
[69]1287 uc.SendMessage(&irc.Message{
[58]1288 Command: "PRIVMSG",
[69]1289 Params: []string{upstreamName, text},
[60]1290 })
[105]1291
[113]1292 echoMsg := &irc.Message{
1293 Prefix: &irc.Prefix{
1294 Name: uc.nick,
1295 User: uc.username,
1296 },
[114]1297 Command: "PRIVMSG",
[113]1298 Params: []string{upstreamName, text},
1299 }
[105]1300 dc.lock.Lock()
[113]1301 dc.ourMessages[echoMsg] = struct{}{}
[105]1302 dc.lock.Unlock()
1303
[143]1304 uc.network.ring.Produce(echoMsg)
[58]1305 }
[164]1306 case "NOTICE":
1307 var targetsStr, text string
1308 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1309 return err
1310 }
1311
1312 for _, name := range strings.Split(targetsStr, ",") {
1313 uc, upstreamName, err := dc.unmarshalEntity(name)
1314 if err != nil {
1315 return err
1316 }
1317
1318 uc.SendMessage(&irc.Message{
1319 Command: "NOTICE",
1320 Params: []string{upstreamName, text},
1321 })
1322 }
[163]1323 case "INVITE":
1324 var user, channel string
1325 if err := parseMessageParams(msg, &user, &channel); err != nil {
1326 return err
1327 }
1328
1329 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1330 if err != nil {
1331 return err
1332 }
1333
1334 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1335 if err != nil {
1336 return err
1337 }
1338
1339 if ucChannel != ucUser {
1340 return ircError{&irc.Message{
1341 Command: irc.ERR_USERNOTINCHANNEL,
1342 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
1343 }}
1344 }
1345 uc := ucChannel
1346
[176]1347 uc.SendMessageLabeled(dc.id, &irc.Message{
[163]1348 Command: "INVITE",
1349 Params: []string{upstreamUser, upstreamChannel},
1350 })
[13]1351 default:
[55]1352 dc.logger.Printf("unhandled message: %v", msg)
[13]1353 return newUnknownCommandError(msg.Command)
1354 }
[42]1355 return nil
[13]1356}
[95]1357
1358func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1359 username, password, ok := parseNickServCredentials(text, uc.nick)
1360 if !ok {
1361 return
1362 }
1363
1364 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1365 n := uc.network
1366 n.SASL.Mechanism = "PLAIN"
1367 n.SASL.Plain.Username = username
1368 n.SASL.Plain.Password = password
1369 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1370 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1371 }
1372}
1373
1374func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1375 fields := strings.Fields(text)
1376 if len(fields) < 2 {
1377 return "", "", false
1378 }
1379 cmd := strings.ToUpper(fields[0])
1380 params := fields[1:]
1381 switch cmd {
1382 case "REGISTER":
1383 username = nick
1384 password = params[0]
1385 case "IDENTIFY":
1386 if len(params) == 1 {
1387 username = nick
[182]1388 password = params[0]
[95]1389 } else {
1390 username = params[0]
[182]1391 password = params[1]
[95]1392 }
[182]1393 case "SET":
1394 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1395 username = nick
1396 password = params[1]
1397 }
[95]1398 }
1399 return username, password, true
1400}
Note: See TracBrowser for help on using the repository browser.