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
Line 
1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
7 "strings"
8
9 "golang.org/x/crypto/bcrypt"
10 "gopkg.in/irc.v3"
11)
12
13type ircError struct {
14 Message *irc.Message
15}
16
17func (err ircError) Error() string {
18 return err.Message.String()
19}
20
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
43var errAuthFailed = ircError{&irc.Message{
44 Command: irc.ERR_PASSWDMISMATCH,
45 Params: []string{"*", "Invalid username or password"},
46}}
47
48type consumption struct {
49 consumer *RingConsumer
50 upstreamConn *upstreamConn
51}
52
53type downstreamConn struct {
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{}
61
62 registered bool
63 user *user
64 nick string
65 username string
66 realname string
67 password string // empty after authentication
68 network *network // can be nil
69}
70
71func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
72 dc := &downstreamConn{
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{}),
80 }
81
82 go func() {
83 if err := dc.writeMessages(); err != nil {
84 dc.logger.Printf("failed to write message: %v", err)
85 }
86 if err := dc.net.Close(); err != nil {
87 dc.logger.Printf("failed to close connection: %v", err)
88 } else {
89 dc.logger.Printf("connection closed")
90 }
91 }()
92
93 return dc
94}
95
96func (dc *downstreamConn) prefix() *irc.Prefix {
97 return &irc.Prefix{
98 Name: dc.nick,
99 User: dc.username,
100 // TODO: fill the host?
101 }
102}
103
104func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
105 return name
106}
107
108func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
109 dc.user.forEachUpstream(func(uc *upstreamConn) {
110 if dc.network != nil && uc.network != dc.network {
111 return
112 }
113 f(uc)
114 })
115}
116
117func (dc *downstreamConn) unmarshalChannel(name string) (*upstreamConn, string, error) {
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 }}
138 }
139 return channel.conn, channel.Name, nil
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
156func (dc *downstreamConn) isClosed() bool {
157 select {
158 case <-dc.closed:
159 return true
160 default:
161 return false
162 }
163}
164
165func (dc *downstreamConn) readMessages() error {
166 dc.logger.Printf("new connection")
167
168 for {
169 msg, err := dc.irc.ReadMessage()
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
176 if dc.srv.Debug {
177 dc.logger.Printf("received: %v", msg)
178 }
179
180 err = dc.handleMessage(msg)
181 if ircErr, ok := err.(ircError); ok {
182 ircErr.Message.Prefix = dc.srv.prefix()
183 dc.SendMessage(ircErr.Message)
184 } else if err != nil {
185 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
186 }
187
188 if dc.isClosed() {
189 return nil
190 }
191 }
192
193 return nil
194}
195
196func (dc *downstreamConn) writeMessages() error {
197 for {
198 var err error
199 var closed bool
200 select {
201 case msg := <-dc.messages:
202 if dc.srv.Debug {
203 dc.logger.Printf("sent: %v", msg)
204 }
205 err = dc.irc.WriteMessage(msg)
206 case consumption := <-dc.consumptions:
207 consumer, uc := consumption.consumer, consumption.upstreamConn
208 for {
209 msg := consumer.Peek()
210 if msg == nil {
211 break
212 }
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 }
221 if dc.srv.Debug {
222 dc.logger.Printf("sent: %v", msg)
223 }
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 {
234 return err
235 }
236 if closed {
237 break
238 }
239 }
240 return nil
241}
242
243func (dc *downstreamConn) Close() error {
244 if dc.isClosed() {
245 return fmt.Errorf("downstream connection already closed")
246 }
247
248 if u := dc.user; u != nil {
249 u.lock.Lock()
250 for i := range u.downstreamConns {
251 if u.downstreamConns[i] == dc {
252 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
253 break
254 }
255 }
256 u.lock.Unlock()
257 }
258
259 close(dc.closed)
260 return nil
261}
262
263func (dc *downstreamConn) SendMessage(msg *irc.Message) {
264 dc.messages <- msg
265}
266
267func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
268 switch msg.Command {
269 case "QUIT":
270 return dc.Close()
271 case "PING":
272 dc.SendMessage(&irc.Message{
273 Prefix: dc.srv.prefix(),
274 Command: "PONG",
275 Params: msg.Params,
276 })
277 return nil
278 default:
279 if dc.registered {
280 return dc.handleMessageRegistered(msg)
281 } else {
282 return dc.handleMessageUnregistered(msg)
283 }
284 }
285}
286
287func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
288 switch msg.Command {
289 case "NICK":
290 if err := parseMessageParams(msg, &dc.nick); err != nil {
291 return err
292 }
293 case "USER":
294 var username string
295 if err := parseMessageParams(msg, &username, nil, nil, &dc.realname); err != nil {
296 return err
297 }
298 dc.username = "~" + username
299 case "PASS":
300 if err := parseMessageParams(msg, &dc.password); err != nil {
301 return err
302 }
303 default:
304 dc.logger.Printf("unhandled message: %v", msg)
305 return newUnknownCommandError(msg.Command)
306 }
307 if dc.username != "" && dc.nick != "" {
308 return dc.register()
309 }
310 return nil
311}
312
313func (dc *downstreamConn) register() error {
314 username := strings.TrimPrefix(dc.username, "~")
315 var networkName string
316 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
317 networkName = username[i+1:]
318 }
319 if i := strings.IndexAny(username, "/@"); i >= 0 {
320 username = username[:i]
321 }
322
323 password := dc.password
324 dc.password = ""
325
326 u := dc.srv.getUser(username)
327 if u == nil {
328 dc.logger.Printf("failed authentication for %q: unknown username", username)
329 return errAuthFailed
330 }
331
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
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)
342 dc.SendMessage(&irc.Message{
343 Prefix: dc.srv.prefix(),
344 Command: irc.ERR_PASSWDMISMATCH,
345 Params: []string{"*", fmt.Sprintf("Unknown network %q", networkName)},
346 })
347 return nil
348 }
349 }
350
351 dc.registered = true
352 dc.user = u
353
354 u.lock.Lock()
355 firstDownstream := len(u.downstreamConns) == 0
356 u.downstreamConns = append(u.downstreamConns, dc)
357 u.lock.Unlock()
358
359 dc.SendMessage(&irc.Message{
360 Prefix: dc.srv.prefix(),
361 Command: irc.RPL_WELCOME,
362 Params: []string{dc.nick, "Welcome to jounce, " + dc.nick},
363 })
364 dc.SendMessage(&irc.Message{
365 Prefix: dc.srv.prefix(),
366 Command: irc.RPL_YOURHOST,
367 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
368 })
369 dc.SendMessage(&irc.Message{
370 Prefix: dc.srv.prefix(),
371 Command: irc.RPL_CREATED,
372 Params: []string{dc.nick, "Who cares when the server was created?"},
373 })
374 dc.SendMessage(&irc.Message{
375 Prefix: dc.srv.prefix(),
376 Command: irc.RPL_MYINFO,
377 Params: []string{dc.nick, dc.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
378 })
379 dc.SendMessage(&irc.Message{
380 Prefix: dc.srv.prefix(),
381 Command: irc.ERR_NOMOTD,
382 Params: []string{dc.nick, "No MOTD"},
383 })
384
385 dc.forEachUpstream(func(uc *upstreamConn) {
386 // TODO: fix races accessing upstream connection data
387 for _, ch := range uc.channels {
388 if ch.complete {
389 forwardChannel(dc, ch)
390 }
391 }
392
393 historyName := dc.username
394
395 var seqPtr *uint64
396 if firstDownstream {
397 seq, ok := uc.history[historyName]
398 if ok {
399 seqPtr = &seq
400 }
401 }
402
403 consumer, ch := uc.ring.NewConsumer(seqPtr)
404 go func() {
405 for {
406 var closed bool
407 select {
408 case <-ch:
409 dc.consumptions <- consumption{consumer, uc}
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 }()
428 })
429
430 return nil
431}
432
433func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
434 switch msg.Command {
435 case "USER":
436 return ircError{&irc.Message{
437 Command: irc.ERR_ALREADYREGISTERED,
438 Params: []string{dc.nick, "You may not reregister"},
439 }}
440 case "NICK":
441 dc.forEachUpstream(func(uc *upstreamConn) {
442 uc.SendMessage(msg)
443 })
444 case "JOIN", "PART":
445 var name string
446 if err := parseMessageParams(msg, &name); err != nil {
447 return err
448 }
449
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 }}
456 }
457
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")
466 }
467
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 {
479 uc, upstreamName, err := dc.unmarshalChannel(name)
480 if err != nil {
481 return err
482 }
483
484 if modeStr != "" {
485 uc.SendMessage(&irc.Message{
486 Command: "MODE",
487 Params: []string{upstreamName, modeStr},
488 })
489 } else {
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
498 dc.SendMessage(&irc.Message{
499 Prefix: dc.srv.prefix(),
500 Command: irc.RPL_CHANNELMODEIS,
501 Params: []string{name, string(ch.modes)},
502 })
503 }
504 } else {
505 if name != dc.nick {
506 return ircError{&irc.Message{
507 Command: irc.ERR_USERSDONTMATCH,
508 Params: []string{dc.nick, "Cannot change mode for other users"},
509 }}
510 }
511
512 if modeStr != "" {
513 dc.forEachUpstream(func(uc *upstreamConn) {
514 uc.SendMessage(&irc.Message{
515 Command: "MODE",
516 Params: []string{uc.nick, modeStr},
517 })
518 })
519 } else {
520 dc.SendMessage(&irc.Message{
521 Prefix: dc.srv.prefix(),
522 Command: irc.RPL_UMODEIS,
523 Params: []string{""}, // TODO
524 })
525 }
526 }
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, ",") {
534 uc, upstreamName, err := dc.unmarshalChannel(name)
535 if err != nil {
536 return err
537 }
538
539 uc.SendMessage(&irc.Message{
540 Command: "PRIVMSG",
541 Params: []string{upstreamName, text},
542 })
543 }
544 default:
545 dc.logger.Printf("unhandled message: %v", msg)
546 return newUnknownCommandError(msg.Command)
547 }
548 return nil
549}
Note: See TracBrowser for help on using the repository browser.