source: code/trunk/downstream.go@ 206

Last change on this file since 206 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
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "net"
9 "strconv"
10 "strings"
11 "sync"
12 "time"
13
14 "github.com/emersion/go-sasl"
15 "golang.org/x/crypto/bcrypt"
16 "gopkg.in/irc.v3"
17)
18
19type ircError struct {
20 Message *irc.Message
21}
22
23func (err ircError) Error() string {
24 return err.Message.String()
25}
26
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
49var errAuthFailed = ircError{&irc.Message{
50 Command: irc.ERR_PASSWDMISMATCH,
51 Params: []string{"*", "Invalid username or password"},
52}}
53
54type downstreamConn struct {
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{}
62
63 registered bool
64 user *user
65 nick string
66 rawUsername string
67 networkName string
68 clientName string
69 realname string
70 hostname string
71 password string // empty after authentication
72 network *network // can be nil
73
74 ringConsumers map[*network]*RingConsumer
75
76 negociatingCaps bool
77 capVersion int
78 caps map[string]bool
79
80 saslServer sasl.Server
81
82 lock sync.Mutex
83 ourMessages map[*irc.Message]struct{}
84}
85
86func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
87 dc := &downstreamConn{
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{}),
98 }
99 dc.hostname = netConn.RemoteAddr().String()
100 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
101 dc.hostname = host
102 }
103
104 go func() {
105 if err := dc.writeMessages(); err != nil {
106 dc.logger.Printf("failed to write message: %v", err)
107 }
108 if err := dc.net.Close(); err != nil {
109 dc.logger.Printf("failed to close connection: %v", err)
110 } else {
111 dc.logger.Printf("connection closed")
112 }
113 }()
114
115 dc.logger.Printf("new connection")
116 return dc
117}
118
119func (dc *downstreamConn) prefix() *irc.Prefix {
120 return &irc.Prefix{
121 Name: dc.nick,
122 User: dc.user.Username,
123 Host: dc.hostname,
124 }
125}
126
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
135func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
136 dc.user.forEachUpstream(func(uc *upstreamConn) {
137 if dc.network != nil && uc.network != dc.network {
138 return
139 }
140 f(uc)
141 })
142}
143
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 }
150 return dc.network.upstream()
151}
152
153func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
154 if uc.isChannel(entity) {
155 return dc.marshalChannel(uc, entity)
156 }
157 return dc.marshalNick(uc, entity)
158}
159
160func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
161 if dc.network != nil {
162 return name
163 }
164 return name + "/" + uc.network.GetName()
165}
166
167func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
168 if uc := dc.upstream(); uc != nil {
169 return uc, name, nil
170 }
171
172 var conn *upstreamConn
173 if i := strings.LastIndexByte(name, '/'); i >= 0 {
174 network := name[i+1:]
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
185 if conn == nil {
186 return nil, "", ircError{&irc.Message{
187 Command: irc.ERR_NOSUCHCHANNEL,
188 Params: []string{name, "No such channel"},
189 }}
190 }
191 return conn, name, nil
192}
193
194func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
195 if nick == uc.nick {
196 return dc.nick
197 }
198 if dc.network != nil {
199 return nick
200 }
201 return nick + "/" + uc.network.GetName()
202}
203
204func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
205 if prefix.Name == uc.nick {
206 return dc.prefix()
207 }
208 if dc.network != nil {
209 return prefix
210 }
211 return &irc.Prefix{
212 Name: prefix.Name + "/" + uc.network.GetName(),
213 User: prefix.User,
214 Host: prefix.Host,
215 }
216}
217
218func (dc *downstreamConn) isClosed() bool {
219 select {
220 case <-dc.closed:
221 return true
222 default:
223 return false
224 }
225}
226
227func (dc *downstreamConn) readMessages(ch chan<- event) error {
228 for {
229 msg, err := dc.irc.ReadMessage()
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
236 if dc.srv.Debug {
237 dc.logger.Printf("received: %v", msg)
238 }
239
240 ch <- eventDownstreamMessage{msg, dc}
241 }
242
243 return nil
244}
245
246func (dc *downstreamConn) writeMessages() error {
247 // TODO: any SendMessage call after the connection is closed will
248 // either block or drop
249 for {
250 var err error
251 var closed bool
252 select {
253 case msg := <-dc.outgoing:
254 if dc.srv.Debug {
255 dc.logger.Printf("sent: %v", msg)
256 }
257 dc.net.SetWriteDeadline(time.Now().Add(writeTimeout))
258 err = dc.irc.WriteMessage(msg)
259 case <-dc.closed:
260 closed = true
261 }
262 if err != nil {
263 return err
264 }
265 if closed {
266 break
267 }
268 }
269 return nil
270}
271
272// Close closes the connection. It is safe to call from any goroutine.
273func (dc *downstreamConn) Close() error {
274 if dc.isClosed() {
275 return fmt.Errorf("downstream connection already closed")
276 }
277 close(dc.closed)
278 return nil
279}
280
281// SendMessage queues a new outgoing message. It is safe to call from any
282// goroutine.
283func (dc *downstreamConn) SendMessage(msg *irc.Message) {
284 // TODO: strip tags if the client doesn't support them (see runNetwork)
285 dc.outgoing <- msg
286}
287
288func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
289 switch msg.Command {
290 case "QUIT":
291 return dc.Close()
292 default:
293 if dc.registered {
294 return dc.handleMessageRegistered(msg)
295 } else {
296 return dc.handleMessageUnregistered(msg)
297 }
298 }
299}
300
301func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
302 switch msg.Command {
303 case "NICK":
304 var nick string
305 if err := parseMessageParams(msg, &nick); err != nil {
306 return err
307 }
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
315 case "USER":
316 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
317 return err
318 }
319 case "PASS":
320 if err := parseMessageParams(msg, &dc.password); err != nil {
321 return err
322 }
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 }
331 case "AUTHENTICATE":
332 if !dc.caps["sasl"] {
333 return ircError{&irc.Message{
334 Command: irc.ERR_SASLFAIL,
335 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
336 }}
337 }
338 if len(msg.Params) == 0 {
339 return ircError{&irc.Message{
340 Command: irc.ERR_SASLFAIL,
341 Params: []string{"*", "Missing AUTHENTICATE argument"},
342 }}
343 }
344 if dc.nick == "" {
345 return ircError{&irc.Message{
346 Command: irc.ERR_SASLFAIL,
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{
361 Command: irc.ERR_SASLFAIL,
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{
368 Command: irc.ERR_SASLABORTED,
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{
380 Command: irc.ERR_SASLFAIL,
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{
391 Command: irc.ERR_SASLFAIL,
392 Params: []string{"*", ircErr.Message.Params[1]},
393 }}
394 }
395 dc.SendMessage(&irc.Message{
396 Prefix: dc.srv.prefix(),
397 Command: irc.ERR_SASLFAIL,
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(),
405 Command: irc.RPL_LOGGEDIN,
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(),
410 Command: irc.RPL_SASLSUCCESS,
411 Params: []string{dc.nick, "SASL authentication successful"},
412 })
413 } else {
414 challengeStr := "+"
415 if len(challenge) > 0 {
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 }
426 default:
427 dc.logger.Printf("unhandled message: %v", msg)
428 return newUnknownCommandError(msg.Command)
429 }
430 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
431 return dc.register()
432 }
433 return nil
434}
435
436func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
437 cmd = strings.ToUpper(cmd)
438
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
453 caps := []string{"message-tags", "server-time"}
454
455 if dc.capVersion >= 302 {
456 caps = append(caps, "sasl=PLAIN")
457 } else {
458 caps = append(caps, "sasl")
459 }
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 {
506 case "sasl", "message-tags", "server-time":
507 dc.caps[name] = enable
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
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
542func unmarshalUsername(rawUsername string) (username, client, network string) {
543 username = rawUsername
544
545 i := strings.IndexAny(username, "/@")
546 j := strings.LastIndexAny(username, "/@")
547 if i >= 0 {
548 username = rawUsername[:i]
549 }
550 if j >= 0 {
551 if rawUsername[j] == '@' {
552 client = rawUsername[j+1:]
553 } else {
554 network = rawUsername[j+1:]
555 }
556 }
557 if i >= 0 && j >= 0 && i < j {
558 if rawUsername[i] == '@' {
559 client = rawUsername[i+1 : j]
560 } else {
561 network = rawUsername[i+1 : j]
562 }
563 }
564
565 return username, client, network
566}
567
568func (dc *downstreamConn) authenticate(username, password string) error {
569 username, clientName, networkName := unmarshalUsername(username)
570
571 u, err := dc.srv.db.GetUser(username)
572 if err != nil {
573 dc.logger.Printf("failed authentication for %q: %v", username, err)
574 return errAuthFailed
575 }
576
577 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
578 if err != nil {
579 dc.logger.Printf("failed authentication for %q: %v", username, err)
580 return errAuthFailed
581 }
582
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 }
588 dc.clientName = clientName
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
606 if dc.clientName == "" && dc.networkName == "" {
607 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
608 }
609
610 dc.registered = true
611 dc.logger.Printf("registration complete for user %q", dc.user.Username)
612 return nil
613}
614
615func (dc *downstreamConn) loadNetwork() error {
616 if dc.networkName == "" {
617 return nil
618 }
619
620 network := dc.user.getNetwork(dc.networkName)
621 if network == nil {
622 addr := dc.networkName
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,
632 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
633 }}
634 }
635
636 dc.logger.Printf("auto-saving network %q", dc.networkName)
637 var err error
638 network, err = dc.user.createNetwork(&Network{
639 Addr: dc.networkName,
640 Nick: dc.nick,
641 })
642 if err != nil {
643 return err
644 }
645 }
646
647 dc.network = network
648 return nil
649}
650
651func (dc *downstreamConn) welcome() error {
652 if dc.user == nil || !dc.registered {
653 panic("tried to welcome an unregistered connection")
654 }
655
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
661 }
662
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 })
671
672 dc.SendMessage(&irc.Message{
673 Prefix: dc.srv.prefix(),
674 Command: irc.RPL_WELCOME,
675 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
676 })
677 dc.SendMessage(&irc.Message{
678 Prefix: dc.srv.prefix(),
679 Command: irc.RPL_YOURHOST,
680 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
681 })
682 dc.SendMessage(&irc.Message{
683 Prefix: dc.srv.prefix(),
684 Command: irc.RPL_CREATED,
685 Params: []string{dc.nick, "Who cares when the server was created?"},
686 })
687 dc.SendMessage(&irc.Message{
688 Prefix: dc.srv.prefix(),
689 Command: irc.RPL_MYINFO,
690 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
691 })
692 // TODO: RPL_ISUPPORT
693 dc.SendMessage(&irc.Message{
694 Prefix: dc.srv.prefix(),
695 Command: irc.ERR_NOMOTD,
696 Params: []string{dc.nick, "No MOTD"},
697 })
698
699 dc.forEachUpstream(func(uc *upstreamConn) {
700 for _, ch := range uc.channels {
701 if ch.complete {
702 dc.SendMessage(&irc.Message{
703 Prefix: dc.prefix(),
704 Command: "JOIN",
705 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
706 })
707
708 forwardChannel(dc, ch)
709 }
710 }
711 })
712
713 dc.forEachNetwork(func(net *network) {
714 dc.runNetwork(net, sendHistory)
715 })
716
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 {
731 seq, ok := net.history[dc.clientName]
732 if ok {
733 seqPtr = &seq
734 }
735 }
736
737 // TODO: can't be enabled/disabled on-the-fly
738 msgTagsEnabled := dc.caps["message-tags"]
739 serverTimeEnabled := dc.caps["server-time"]
740
741 consumer, ch := net.ring.NewConsumer(seqPtr)
742
743 if _, ok := dc.ringConsumers[net]; ok {
744 panic("network has been added twice")
745 }
746 dc.ringConsumers[net] = consumer
747
748 go func() {
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 {
759 break
760 }
761
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
771 }
772
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 }
781
782 if !msgTagsEnabled {
783 for name := range msg.Tags {
784 supported := false
785 switch name {
786 case "time":
787 supported = serverTimeEnabled
788 }
789 if !supported {
790 delete(msg.Tags, name)
791 }
792 }
793 }
794
795 dc.SendMessage(msg)
796 consumer.Consume()
797 }
798 }
799 }()
800}
801
802func (dc *downstreamConn) runUntilRegistered() error {
803 for !dc.registered {
804 msg, err := dc.irc.ReadMessage()
805 if err != nil {
806 return fmt.Errorf("failed to read IRC command: %v", err)
807 }
808
809 if dc.srv.Debug {
810 dc.logger.Printf("received: %v", msg)
811 }
812
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
825func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
826 switch msg.Command {
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 }
835 case "PING":
836 dc.SendMessage(&irc.Message{
837 Prefix: dc.srv.prefix(),
838 Command: "PONG",
839 Params: msg.Params,
840 })
841 return nil
842 case "USER":
843 return ircError{&irc.Message{
844 Command: irc.ERR_ALREADYREGISTERED,
845 Params: []string{dc.nick, "You may not reregister"},
846 }}
847 case "NICK":
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
865 dc.forEachUpstream(func(uc *upstreamConn) {
866 uc.SendMessage(msg)
867 })
868 case "JOIN":
869 var namesStr string
870 if err := parseMessageParams(msg, &namesStr); err != nil {
871 return err
872 }
873
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, ",") {
880 uc, upstreamName, err := dc.unmarshalEntity(name)
881 if err != nil {
882 return err
883 }
884
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 }
894 uc.SendMessage(&irc.Message{
895 Command: "JOIN",
896 Params: params,
897 })
898
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)
905 }
906 }
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 {
921 return err
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 }
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 }
993 case "MODE":
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
1004 if name == dc.nick {
1005 if modeStr != "" {
1006 dc.forEachUpstream(func(uc *upstreamConn) {
1007 uc.SendMessage(&irc.Message{
1008 Command: "MODE",
1009 Params: []string{uc.nick, modeStr},
1010 })
1011 })
1012 } else {
1013 dc.SendMessage(&irc.Message{
1014 Prefix: dc.srv.prefix(),
1015 Command: irc.RPL_UMODEIS,
1016 Params: []string{dc.nick, ""}, // TODO
1017 })
1018 }
1019 return nil
1020 }
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 })
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 }
1072 }
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 }
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 }
1134 uc.trySendLIST(dc.id)
1135 })
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
1158 uc.SendMessageLabeled(dc.id, &irc.Message{
1159 Command: "NAMES",
1160 Params: []string{upstreamChannel},
1161 })
1162 }
1163 }
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,
1170 Params: []string{dc.nick, "*", "End of /WHO list"},
1171 })
1172 return nil
1173 }
1174
1175 // TODO: support WHO masks
1176 entity := msg.Params[0]
1177
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,
1183 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
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
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
1205 uc.SendMessageLabeled(dc.id, &irc.Message{
1206 Command: "WHO",
1207 Params: params,
1208 })
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
1230 if mask == dc.nick {
1231 dc.SendMessage(&irc.Message{
1232 Prefix: dc.srv.prefix(),
1233 Command: irc.RPL_WHOISUSER,
1234 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
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
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
1262 uc.SendMessageLabeled(dc.id, &irc.Message{
1263 Command: "WHOIS",
1264 Params: params,
1265 })
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, ",") {
1273 if name == serviceNick {
1274 handleServicePRIVMSG(dc, text)
1275 continue
1276 }
1277
1278 uc, upstreamName, err := dc.unmarshalEntity(name)
1279 if err != nil {
1280 return err
1281 }
1282
1283 if upstreamName == "NickServ" {
1284 dc.handleNickServPRIVMSG(uc, text)
1285 }
1286
1287 uc.SendMessage(&irc.Message{
1288 Command: "PRIVMSG",
1289 Params: []string{upstreamName, text},
1290 })
1291
1292 echoMsg := &irc.Message{
1293 Prefix: &irc.Prefix{
1294 Name: uc.nick,
1295 User: uc.username,
1296 },
1297 Command: "PRIVMSG",
1298 Params: []string{upstreamName, text},
1299 }
1300 dc.lock.Lock()
1301 dc.ourMessages[echoMsg] = struct{}{}
1302 dc.lock.Unlock()
1303
1304 uc.network.ring.Produce(echoMsg)
1305 }
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 }
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
1347 uc.SendMessageLabeled(dc.id, &irc.Message{
1348 Command: "INVITE",
1349 Params: []string{upstreamUser, upstreamChannel},
1350 })
1351 default:
1352 dc.logger.Printf("unhandled message: %v", msg)
1353 return newUnknownCommandError(msg.Command)
1354 }
1355 return nil
1356}
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
1388 password = params[0]
1389 } else {
1390 username = params[0]
1391 password = params[1]
1392 }
1393 case "SET":
1394 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1395 username = nick
1396 password = params[1]
1397 }
1398 }
1399 return username, password, true
1400}
Note: See TracBrowser for help on using the repository browser.