source: code/trunk/downstream.go@ 66

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

Properly handle PING messages

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