source: code/trunk/downstream.go@ 85

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

Add support for PASS to downstream

File size: 11.8 KB
RevLine 
[13]1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
[39]7 "strings"
[13]8
[85]9 "golang.org/x/crypto/bcrypt"
[13]10 "gopkg.in/irc.v3"
11)
12
13type ircError struct {
14 Message *irc.Message
15}
16
[85]17func (err ircError) Error() string {
18 return err.Message.String()
19}
20
[13]21func newUnknownCommandError(cmd string) ircError {
22 return ircError{&irc.Message{
23 Command: irc.ERR_UNKNOWNCOMMAND,
24 Params: []string{
25 "*",
26 cmd,
27 "Unknown command",
28 },
29 }}
30}
31
32func newNeedMoreParamsError(cmd string) ircError {
33 return ircError{&irc.Message{
34 Command: irc.ERR_NEEDMOREPARAMS,
35 Params: []string{
36 "*",
37 cmd,
38 "Not enough parameters",
39 },
40 }}
41}
42
[85]43var errAuthFailed = ircError{&irc.Message{
44 Command: irc.ERR_PASSWDMISMATCH,
45 Params: []string{"*", "Invalid username or password"},
46}}
[13]47
[69]48type consumption struct {
49 consumer *RingConsumer
50 upstreamConn *upstreamConn
51}
52
[13]53type downstreamConn struct {
[69]54 net net.Conn
55 irc *irc.Conn
56 srv *Server
57 logger Logger
58 messages chan *irc.Message
59 consumptions chan consumption
60 closed chan struct{}
[22]61
[13]62 registered bool
[37]63 user *user
[13]64 nick string
65 username string
66 realname string
[85]67 password string // empty after authentication
[77]68 network *network // can be nil
[13]69}
70
[22]71func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
[55]72 dc := &downstreamConn{
[69]73 net: netConn,
74 irc: irc.NewConn(netConn),
75 srv: srv,
76 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
77 messages: make(chan *irc.Message, 64),
78 consumptions: make(chan consumption),
79 closed: make(chan struct{}),
[22]80 }
[26]81
82 go func() {
[56]83 if err := dc.writeMessages(); err != nil {
84 dc.logger.Printf("failed to write message: %v", err)
[26]85 }
[55]86 if err := dc.net.Close(); err != nil {
87 dc.logger.Printf("failed to close connection: %v", err)
[45]88 } else {
[55]89 dc.logger.Printf("connection closed")
[45]90 }
[26]91 }()
92
[55]93 return dc
[22]94}
95
[55]96func (dc *downstreamConn) prefix() *irc.Prefix {
[27]97 return &irc.Prefix{
[55]98 Name: dc.nick,
99 User: dc.username,
[27]100 // TODO: fill the host?
101 }
102}
103
[69]104func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
105 return name
106}
107
[73]108func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
109 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]110 if dc.network != nil && uc.network != dc.network {
[73]111 return
112 }
113 f(uc)
114 })
115}
116
[69]117func (dc *downstreamConn) unmarshalChannel(name string) (*upstreamConn, string, error) {
[73]118 // TODO: extract network name from channel name if dc.upstream == nil
119 var channel *upstreamChannel
120 var err error
121 dc.forEachUpstream(func(uc *upstreamConn) {
122 if err != nil {
123 return
124 }
125 if ch, ok := uc.channels[name]; ok {
126 if channel != nil {
127 err = fmt.Errorf("ambiguous channel name %q", name)
128 } else {
129 channel = ch
130 }
131 }
132 })
133 if channel == nil {
134 return nil, "", ircError{&irc.Message{
135 Command: irc.ERR_NOSUCHCHANNEL,
136 Params: []string{name, "No such channel"},
137 }}
[69]138 }
[73]139 return channel.conn, channel.Name, nil
[69]140}
141
142func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
143 if nick == uc.nick {
144 return dc.nick
145 }
146 return nick
147}
148
149func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
150 if prefix.Name == uc.nick {
151 return dc.prefix()
152 }
153 return prefix
154}
155
[57]156func (dc *downstreamConn) isClosed() bool {
157 select {
158 case <-dc.closed:
159 return true
160 default:
161 return false
162 }
163}
164
[55]165func (dc *downstreamConn) readMessages() error {
166 dc.logger.Printf("new connection")
[22]167
168 for {
[55]169 msg, err := dc.irc.ReadMessage()
[22]170 if err == io.EOF {
171 break
172 } else if err != nil {
173 return fmt.Errorf("failed to read IRC command: %v", err)
174 }
175
[64]176 if dc.srv.Debug {
177 dc.logger.Printf("received: %v", msg)
178 }
179
[55]180 err = dc.handleMessage(msg)
[22]181 if ircErr, ok := err.(ircError); ok {
[55]182 ircErr.Message.Prefix = dc.srv.prefix()
183 dc.SendMessage(ircErr.Message)
[22]184 } else if err != nil {
185 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
186 }
187
[57]188 if dc.isClosed() {
[22]189 return nil
190 }
191 }
192
[45]193 return nil
[22]194}
195
[56]196func (dc *downstreamConn) writeMessages() error {
[57]197 for {
198 var err error
199 var closed bool
200 select {
201 case msg := <-dc.messages:
[64]202 if dc.srv.Debug {
203 dc.logger.Printf("sent: %v", msg)
204 }
[57]205 err = dc.irc.WriteMessage(msg)
[69]206 case consumption := <-dc.consumptions:
207 consumer, uc := consumption.consumer, consumption.upstreamConn
[57]208 for {
209 msg := consumer.Peek()
210 if msg == nil {
211 break
212 }
[69]213 msg = msg.Copy()
214 switch msg.Command {
215 case "PRIVMSG":
216 // TODO: detect whether it's a user or a channel
217 msg.Params[0] = dc.marshalChannel(uc, msg.Params[0])
218 default:
219 panic("expected to consume a PRIVMSG message")
220 }
[64]221 if dc.srv.Debug {
222 dc.logger.Printf("sent: %v", msg)
223 }
[57]224 err = dc.irc.WriteMessage(msg)
225 if err != nil {
226 break
227 }
228 consumer.Consume()
229 }
230 case <-dc.closed:
231 closed = true
232 }
233 if err != nil {
[56]234 return err
235 }
[57]236 if closed {
237 break
238 }
[56]239 }
240 return nil
241}
242
[55]243func (dc *downstreamConn) Close() error {
[57]244 if dc.isClosed() {
[26]245 return fmt.Errorf("downstream connection already closed")
246 }
[40]247
[55]248 if u := dc.user; u != nil {
[40]249 u.lock.Lock()
250 for i := range u.downstreamConns {
[55]251 if u.downstreamConns[i] == dc {
[40]252 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
[63]253 break
[40]254 }
255 }
256 u.lock.Unlock()
[13]257 }
[40]258
[57]259 close(dc.closed)
[45]260 return nil
[13]261}
262
[55]263func (dc *downstreamConn) SendMessage(msg *irc.Message) {
264 dc.messages <- msg
[54]265}
266
[55]267func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]268 switch msg.Command {
[28]269 case "QUIT":
[55]270 return dc.Close()
[13]271 case "PING":
[55]272 dc.SendMessage(&irc.Message{
273 Prefix: dc.srv.prefix(),
[13]274 Command: "PONG",
[68]275 Params: msg.Params,
[54]276 })
[26]277 return nil
[13]278 default:
[55]279 if dc.registered {
280 return dc.handleMessageRegistered(msg)
[13]281 } else {
[55]282 return dc.handleMessageUnregistered(msg)
[13]283 }
284 }
285}
286
[55]287func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]288 switch msg.Command {
289 case "NICK":
[55]290 if err := parseMessageParams(msg, &dc.nick); err != nil {
[43]291 return err
[13]292 }
293 case "USER":
[43]294 var username string
[55]295 if err := parseMessageParams(msg, &username, nil, nil, &dc.realname); err != nil {
[43]296 return err
[13]297 }
[55]298 dc.username = "~" + username
[85]299 case "PASS":
300 if err := parseMessageParams(msg, &dc.password); err != nil {
301 return err
302 }
[13]303 default:
[55]304 dc.logger.Printf("unhandled message: %v", msg)
[13]305 return newUnknownCommandError(msg.Command)
306 }
[55]307 if dc.username != "" && dc.nick != "" {
308 return dc.register()
[13]309 }
310 return nil
311}
312
[55]313func (dc *downstreamConn) register() error {
[73]314 username := strings.TrimPrefix(dc.username, "~")
[77]315 var networkName string
[73]316 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
[77]317 networkName = username[i+1:]
[73]318 }
319 if i := strings.IndexAny(username, "/@"); i >= 0 {
320 username = username[:i]
321 }
322
[85]323 password := dc.password
324 dc.password = ""
325
[73]326 u := dc.srv.getUser(username)
[38]327 if u == nil {
[85]328 dc.logger.Printf("failed authentication for %q: unknown username", username)
329 return errAuthFailed
[37]330 }
331
[85]332 err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
333 if err != nil {
334 dc.logger.Printf("failed authentication for %q: %v", username, err)
335 return errAuthFailed
336 }
337
[77]338 if networkName != "" {
339 dc.network = dc.user.getNetwork(networkName)
340 if dc.network == nil {
341 dc.logger.Printf("failed registration: unknown network %q", networkName)
[73]342 dc.SendMessage(&irc.Message{
343 Prefix: dc.srv.prefix(),
344 Command: irc.ERR_PASSWDMISMATCH,
[77]345 Params: []string{"*", fmt.Sprintf("Unknown network %q", networkName)},
[73]346 })
347 return nil
348 }
349 }
350
[55]351 dc.registered = true
352 dc.user = u
[13]353
[40]354 u.lock.Lock()
[57]355 firstDownstream := len(u.downstreamConns) == 0
[55]356 u.downstreamConns = append(u.downstreamConns, dc)
[40]357 u.lock.Unlock()
358
[55]359 dc.SendMessage(&irc.Message{
360 Prefix: dc.srv.prefix(),
[13]361 Command: irc.RPL_WELCOME,
[55]362 Params: []string{dc.nick, "Welcome to jounce, " + dc.nick},
[54]363 })
[55]364 dc.SendMessage(&irc.Message{
365 Prefix: dc.srv.prefix(),
[13]366 Command: irc.RPL_YOURHOST,
[55]367 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]368 })
[55]369 dc.SendMessage(&irc.Message{
370 Prefix: dc.srv.prefix(),
[13]371 Command: irc.RPL_CREATED,
[55]372 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]373 })
[55]374 dc.SendMessage(&irc.Message{
375 Prefix: dc.srv.prefix(),
[13]376 Command: irc.RPL_MYINFO,
[55]377 Params: []string{dc.nick, dc.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
[54]378 })
[55]379 dc.SendMessage(&irc.Message{
380 Prefix: dc.srv.prefix(),
[13]381 Command: irc.ERR_NOMOTD,
[55]382 Params: []string{dc.nick, "No MOTD"},
[54]383 })
[13]384
[73]385 dc.forEachUpstream(func(uc *upstreamConn) {
[30]386 // TODO: fix races accessing upstream connection data
387 for _, ch := range uc.channels {
388 if ch.complete {
[55]389 forwardChannel(dc, ch)
[30]390 }
391 }
[50]392
[73]393 historyName := dc.username
[57]394
395 var seqPtr *uint64
396 if firstDownstream {
397 seq, ok := uc.history[historyName]
398 if ok {
399 seqPtr = &seq
[50]400 }
401 }
[57]402
[59]403 consumer, ch := uc.ring.NewConsumer(seqPtr)
[57]404 go func() {
405 for {
406 var closed bool
407 select {
408 case <-ch:
[69]409 dc.consumptions <- consumption{consumer, uc}
[57]410 case <-dc.closed:
411 closed = true
412 }
413 if closed {
414 break
415 }
416 }
417
418 seq := consumer.Close()
419
420 dc.user.lock.Lock()
421 lastDownstream := len(dc.user.downstreamConns) == 0
422 dc.user.lock.Unlock()
423
424 if lastDownstream {
425 uc.history[historyName] = seq
426 }
427 }()
[39]428 })
[50]429
[13]430 return nil
431}
432
[55]433func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]434 switch msg.Command {
[42]435 case "USER":
[13]436 return ircError{&irc.Message{
437 Command: irc.ERR_ALREADYREGISTERED,
[55]438 Params: []string{dc.nick, "You may not reregister"},
[13]439 }}
[42]440 case "NICK":
[73]441 dc.forEachUpstream(func(uc *upstreamConn) {
[60]442 uc.SendMessage(msg)
[42]443 })
[69]444 case "JOIN", "PART":
[48]445 var name string
446 if err := parseMessageParams(msg, &name); err != nil {
447 return err
448 }
449
[69]450 uc, upstreamName, err := dc.unmarshalChannel(name)
451 if err != nil {
452 return ircError{&irc.Message{
453 Command: irc.ERR_NOSUCHCHANNEL,
454 Params: []string{name, err.Error()},
455 }}
[48]456 }
457
[69]458 uc.SendMessage(&irc.Message{
459 Command: msg.Command,
460 Params: []string{upstreamName},
461 })
462 // TODO: add/remove channel from upstream config
463 case "MODE":
464 if msg.Prefix == nil {
465 return fmt.Errorf("missing prefix")
[49]466 }
467
[46]468 var name string
469 if err := parseMessageParams(msg, &name); err != nil {
470 return err
471 }
472
473 var modeStr string
474 if len(msg.Params) > 1 {
475 modeStr = msg.Params[1]
476 }
477
478 if msg.Prefix.Name != name {
[69]479 uc, upstreamName, err := dc.unmarshalChannel(name)
[46]480 if err != nil {
481 return err
482 }
483
484 if modeStr != "" {
[69]485 uc.SendMessage(&irc.Message{
486 Command: "MODE",
487 Params: []string{upstreamName, modeStr},
488 })
[46]489 } else {
[69]490 ch, ok := uc.channels[upstreamName]
491 if !ok {
492 return ircError{&irc.Message{
493 Command: irc.ERR_NOSUCHCHANNEL,
494 Params: []string{name, "No such channel"},
495 }}
496 }
497
[55]498 dc.SendMessage(&irc.Message{
499 Prefix: dc.srv.prefix(),
[46]500 Command: irc.RPL_CHANNELMODEIS,
[69]501 Params: []string{name, string(ch.modes)},
[54]502 })
[46]503 }
504 } else {
[55]505 if name != dc.nick {
[46]506 return ircError{&irc.Message{
507 Command: irc.ERR_USERSDONTMATCH,
[55]508 Params: []string{dc.nick, "Cannot change mode for other users"},
[46]509 }}
510 }
511
512 if modeStr != "" {
[73]513 dc.forEachUpstream(func(uc *upstreamConn) {
[69]514 uc.SendMessage(&irc.Message{
515 Command: "MODE",
516 Params: []string{uc.nick, modeStr},
517 })
[46]518 })
519 } else {
[55]520 dc.SendMessage(&irc.Message{
521 Prefix: dc.srv.prefix(),
[46]522 Command: irc.RPL_UMODEIS,
523 Params: []string{""}, // TODO
[54]524 })
[46]525 }
526 }
[58]527 case "PRIVMSG":
528 var targetsStr, text string
529 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
530 return err
531 }
532
533 for _, name := range strings.Split(targetsStr, ",") {
[69]534 uc, upstreamName, err := dc.unmarshalChannel(name)
[58]535 if err != nil {
536 return err
537 }
538
[69]539 uc.SendMessage(&irc.Message{
[58]540 Command: "PRIVMSG",
[69]541 Params: []string{upstreamName, text},
[60]542 })
[58]543 }
[13]544 default:
[55]545 dc.logger.Printf("unhandled message: %v", msg)
[13]546 return newUnknownCommandError(msg.Command)
547 }
[42]548 return nil
[13]549}
Note: See TracBrowser for help on using the repository browser.