1 |
/* |
2 |
* connection.C: Connection and I/O management |
3 |
* Rights to this code are documented in doc/pod/license.pod. |
4 |
* |
5 |
* Copyright © 2005-2007 Atheme Project (http://www.atheme.org) |
6 |
*/ |
7 |
|
8 |
static char const rcsid[] = "$Id: connection.C,v 1.3 2007-07-21 13:23:21 pippijn Exp $"; |
9 |
|
10 |
#include "atheme.h" |
11 |
#include "internal.h" |
12 |
#include "datastream.h" |
13 |
|
14 |
connection_vector connlist; |
15 |
connection_t::callbacks connection_t::callback; |
16 |
|
17 |
void |
18 |
init_netio (void) |
19 |
{ |
20 |
#if 0 |
21 |
connection_heap = BlockHeapCreate (sizeof (connection_t), 16); |
22 |
sa_heap = BlockHeapCreate (sizeof (struct sockaddr_in), 16); |
23 |
#endif |
24 |
} |
25 |
|
26 |
static int |
27 |
socket_setnonblocking (int sck) |
28 |
{ |
29 |
int flags; |
30 |
|
31 |
flags = fcntl (sck, F_GETFL, 0); |
32 |
flags |= O_NONBLOCK; |
33 |
|
34 |
if (fcntl (sck, F_SETFL, flags)) |
35 |
return -1; |
36 |
|
37 |
return 0; |
38 |
} |
39 |
|
40 |
/* |
41 |
* connection_add() |
42 |
* |
43 |
* inputs: |
44 |
* connection name, file descriptor, flag bitmask, |
45 |
* read handler, write handler. |
46 |
* |
47 |
* outputs: |
48 |
* a connection object for the connection information given. |
49 |
* |
50 |
* side effects: |
51 |
* a connection is added to the socket queue. |
52 |
*/ |
53 |
connection_t * |
54 |
connection_add (char const * const name, int fd, unsigned int flags, void (*read_handler) (connection_t *), void (*write_handler) (connection_t *)) |
55 |
{ |
56 |
connection_t *cptr; |
57 |
|
58 |
if ((cptr = connection_find (fd))) |
59 |
{ |
60 |
slog (LG_DEBUG, "connection_add(): connection %d is already registered as %s", fd, cptr->name); |
61 |
return NULL; |
62 |
} |
63 |
|
64 |
slog (LG_DEBUG, "connection_add(): adding connection '%s', fd = %d", name, fd); |
65 |
|
66 |
cptr = new connection_t; |
67 |
|
68 |
cptr->fd = fd; |
69 |
cptr->pollslot = -1; |
70 |
cptr->flags = flags; |
71 |
cptr->first_recv = NOW; |
72 |
cptr->last_recv = NOW; |
73 |
|
74 |
cptr->read_handler = read_handler; |
75 |
cptr->write_handler = write_handler; |
76 |
|
77 |
/* set connection name */ |
78 |
strlcpy (cptr->name, name, HOSTLEN); |
79 |
|
80 |
/* XXX */ |
81 |
cptr->saddr_size = sizeof (cptr->saddr); |
82 |
getpeername (cptr->fd, &cptr->saddr, &cptr->saddr_size); |
83 |
cptr->sa = (struct sockaddr_in *) &cptr->saddr; |
84 |
|
85 |
inet_ntop (AF_INET, &cptr->sa->sin_addr, cptr->hbuf, BUFSIZE); |
86 |
|
87 |
connlist.insert (cptr); |
88 |
|
89 |
return cptr; |
90 |
} |
91 |
|
92 |
/* |
93 |
* connection_find() |
94 |
* |
95 |
* inputs: |
96 |
* the file descriptor to search by |
97 |
* |
98 |
* outputs: |
99 |
* the connection_t object associated with that fd |
100 |
* |
101 |
* side effects: |
102 |
* none |
103 |
*/ |
104 |
connection_t * |
105 |
connection_find (int fd) |
106 |
{ |
107 |
connection_t *cptr; |
108 |
connection_vector::iterator it = connlist.begin (); |
109 |
connection_vector::iterator et = connlist.end (); |
110 |
|
111 |
while (it != et) |
112 |
{ |
113 |
cptr = *it; |
114 |
|
115 |
if (cptr->fd == fd) |
116 |
return cptr; |
117 |
|
118 |
++it; |
119 |
} |
120 |
|
121 |
return NULL; |
122 |
} |
123 |
|
124 |
/* |
125 |
* connection_count() |
126 |
* |
127 |
* inputs: |
128 |
* none |
129 |
* |
130 |
* outputs: |
131 |
* number of connections tracked |
132 |
* |
133 |
* side effects: |
134 |
* none |
135 |
*/ |
136 |
int |
137 |
connection_count (void) |
138 |
{ |
139 |
return connlist.size (); |
140 |
} |
141 |
|
142 |
/* |
143 |
* connection_close() |
144 |
* |
145 |
* inputs: |
146 |
* the connection being closed. |
147 |
* |
148 |
* outputs: |
149 |
* none |
150 |
* |
151 |
* side effects: |
152 |
* the connection is closed. |
153 |
*/ |
154 |
void |
155 |
connection_close (connection_t *cptr) |
156 |
{ |
157 |
connection_vector::iterator it; |
158 |
int errno1, errno2; |
159 |
#ifdef SO_ERROR |
160 |
socklen_t len = sizeof (errno2); |
161 |
#endif |
162 |
|
163 |
it = connlist.find (cptr); |
164 |
if (it == connlist.end ()) |
165 |
{ |
166 |
slog (LG_DEBUG, "connection_close(): connection %p is not registered!", cptr); |
167 |
return; |
168 |
} |
169 |
|
170 |
errno1 = errno; |
171 |
#ifdef SO_ERROR |
172 |
if (!getsockopt (cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &errno2, (socklen_t *) &len)) |
173 |
{ |
174 |
if (errno2 != 0) |
175 |
errno1 = errno2; |
176 |
} |
177 |
#endif |
178 |
|
179 |
if (errno1) |
180 |
slog (cptr->flags & CF_UPLINK ? LG_ERROR : LG_INFO, "connection_close(): connection %s[%d] closed due to error %d (%s)", cptr->name, cptr->fd, errno1, strerror (errno1)); |
181 |
|
182 |
if (cptr->close_handler) |
183 |
cptr->close_handler (cptr); |
184 |
|
185 |
/* close the fd */ |
186 |
close (cptr->fd); |
187 |
|
188 |
connlist.erase (cptr); |
189 |
|
190 |
sendqrecvq_free (cptr); |
191 |
|
192 |
delete cptr; |
193 |
} |
194 |
|
195 |
/* This one is only safe for use by connection_close_soon(), |
196 |
* it will cause infinite loops otherwise |
197 |
*/ |
198 |
static void |
199 |
empty_handler (connection_t *cptr) |
200 |
{ |
201 |
} |
202 |
|
203 |
/* |
204 |
* connection_close_soon() |
205 |
* |
206 |
* inputs: |
207 |
* the connection being closed. |
208 |
* |
209 |
* outputs: |
210 |
* none |
211 |
* |
212 |
* side effects: |
213 |
* the connection is marked to be closed soon |
214 |
* handlers reset |
215 |
* close_handler called |
216 |
*/ |
217 |
void |
218 |
connection_close_soon (connection_t *cptr) |
219 |
{ |
220 |
cptr->flags |= CF_DEAD; |
221 |
/* these two cannot be NULL */ |
222 |
cptr->read_handler = empty_handler; |
223 |
cptr->write_handler = empty_handler; |
224 |
cptr->recvq_handler = NULL; |
225 |
if (cptr->close_handler) |
226 |
cptr->close_handler (cptr); |
227 |
cptr->close_handler = NULL; |
228 |
cptr->listener = NULL; |
229 |
} |
230 |
|
231 |
/* |
232 |
* connection_close_soon_children() |
233 |
* |
234 |
* inputs: |
235 |
* a listener. |
236 |
* |
237 |
* outputs: |
238 |
* none |
239 |
* |
240 |
* side effects: |
241 |
* connection_close_soon() called on the connection itself and |
242 |
* for all connections accepted on this listener |
243 |
*/ |
244 |
void |
245 |
connection_close_soon_children (connection_t *cptr) |
246 |
{ |
247 |
connection_vector::iterator it = connlist.begin (); |
248 |
connection_vector::iterator et = connlist.end (); |
249 |
connection_t *cptr2; |
250 |
|
251 |
return_if_fail (cptr != NULL); |
252 |
|
253 |
if (CF_IS_LISTENING (cptr)) |
254 |
{ |
255 |
while (it != et) |
256 |
{ |
257 |
cptr2 = *it; |
258 |
if (cptr2->listener == cptr) |
259 |
connection_close_soon (cptr2); |
260 |
++it; |
261 |
} |
262 |
} |
263 |
|
264 |
connection_close_soon (cptr); |
265 |
} |
266 |
|
267 |
/* |
268 |
* connection_close_all() |
269 |
* |
270 |
* inputs: |
271 |
* none |
272 |
* |
273 |
* outputs: |
274 |
* none |
275 |
* |
276 |
* side effects: |
277 |
* connection_close() called on all registered connections |
278 |
*/ |
279 |
void |
280 |
connection_close_all (void) |
281 |
{ |
282 |
while (!connlist.empty ()) |
283 |
connection_close (connlist.back ()); |
284 |
} |
285 |
|
286 |
/* |
287 |
* connection_open_tcp() |
288 |
* |
289 |
* inputs: |
290 |
* hostname to connect to, vhost to use, port, |
291 |
* read handler, write handler |
292 |
* |
293 |
* outputs: |
294 |
* the connection_t on success, NULL on failure. |
295 |
* |
296 |
* side effects: |
297 |
* a TCP/IP connection is opened to the host, |
298 |
* and interest is registered in read/write events. |
299 |
*/ |
300 |
connection_t * |
301 |
connection_open_tcp (char const * const host, char const * const vhost, unsigned int port, void (*read_handler) (connection_t *), void (*write_handler) (connection_t *)) |
302 |
{ |
303 |
connection_t *cptr; |
304 |
char buf[BUFSIZE]; |
305 |
struct hostent *hp; |
306 |
struct sockaddr_in sa; |
307 |
struct in_addr *in; |
308 |
unsigned s; |
309 |
unsigned int optval; |
310 |
|
311 |
if (!(s = socket (AF_INET, SOCK_STREAM, 0))) |
312 |
{ |
313 |
slog (LG_ERROR, "connection_open_tcp(): unable to create socket."); |
314 |
return NULL; |
315 |
} |
316 |
|
317 |
/* Has the highest fd gotten any higher yet? */ |
318 |
if (s > system_state.maxfd) |
319 |
system_state.maxfd = s; |
320 |
|
321 |
snprintf (buf, BUFSIZE, "tcp connection: %s", host); |
322 |
|
323 |
if (vhost) |
324 |
{ |
325 |
memset (&sa, 0, sizeof (sa)); |
326 |
sa.sin_family = AF_INET; |
327 |
|
328 |
if ((hp = gethostbyname (vhost)) == NULL) |
329 |
{ |
330 |
slog (LG_ERROR, "connection_open_tcp(): cant resolve vhost %s", vhost); |
331 |
close (s); |
332 |
return NULL; |
333 |
} |
334 |
|
335 |
in = (struct in_addr *) (hp->h_addr_list[0]); |
336 |
sa.sin_addr.s_addr = in->s_addr; |
337 |
sa.sin_port = 0; |
338 |
|
339 |
optval = 1; |
340 |
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof (optval)); |
341 |
|
342 |
if (bind (s, (struct sockaddr *) &sa, sizeof (sa)) < 0) |
343 |
{ |
344 |
close (s); |
345 |
slog (LG_ERROR, "connection_open_tcp(): unable to bind to vhost %s", vhost); |
346 |
return NULL; |
347 |
} |
348 |
} |
349 |
|
350 |
/* resolve it */ |
351 |
if ((hp = gethostbyname (host)) == NULL) |
352 |
{ |
353 |
slog (LG_ERROR, "connection_open_tcp(): unable to resolve %s", host); |
354 |
close (s); |
355 |
return NULL; |
356 |
} |
357 |
|
358 |
memset (&sa, '\0', sizeof (sa)); |
359 |
sa.sin_family = AF_INET; |
360 |
sa.sin_port = htons (port); |
361 |
memcpy (&sa.sin_addr, hp->h_addr, hp->h_length); |
362 |
|
363 |
socket_setnonblocking (s); |
364 |
|
365 |
if (connect (s, (struct sockaddr *) &sa, sizeof (sa)) == -1 && errno != EINPROGRESS && errno != EINTR) |
366 |
{ |
367 |
slog (LG_ERROR, "connection_open_tcp(): failed to connect to %s: %s", host, strerror (errno)); |
368 |
close (s); |
369 |
return NULL; |
370 |
} |
371 |
|
372 |
cptr = connection_add (buf, s, CF_CONNECTING, read_handler, write_handler); |
373 |
|
374 |
return cptr; |
375 |
} |
376 |
|
377 |
/* |
378 |
* connection_open_listener_tcp() |
379 |
* |
380 |
* inputs: |
381 |
* host and port to listen on, |
382 |
* accept handler |
383 |
* |
384 |
* outputs: |
385 |
* the connection_t on success, NULL on failure. |
386 |
* |
387 |
* side effects: |
388 |
* a TCP/IP connection is opened to the host, |
389 |
* and interest is registered in read/write events. |
390 |
*/ |
391 |
connection_t * |
392 |
connection_open_listener_tcp (char const * const host, unsigned int port, void (*read_handler) (connection_t *)) |
393 |
{ |
394 |
connection_t *cptr; |
395 |
char buf[BUFSIZE]; |
396 |
struct hostent *hp; |
397 |
struct sockaddr_in sa; |
398 |
struct in_addr *in; |
399 |
unsigned s; |
400 |
unsigned int optval; |
401 |
|
402 |
if (!(s = socket (AF_INET, SOCK_STREAM, 0))) |
403 |
{ |
404 |
slog (LG_ERROR, "connection_open_listener_tcp(): unable to create socket."); |
405 |
return NULL; |
406 |
} |
407 |
|
408 |
/* Has the highest fd gotten any higher yet? */ |
409 |
if (s > system_state.maxfd) |
410 |
system_state.maxfd = s; |
411 |
|
412 |
snprintf (buf, BUFSIZE, "listener: %s[%d]", host, port); |
413 |
|
414 |
memset (&sa, 0, sizeof (sa)); |
415 |
sa.sin_family = AF_INET; |
416 |
|
417 |
if ((hp = gethostbyname (host)) == NULL) |
418 |
{ |
419 |
slog (LG_ERROR, "connection_open_listener_tcp(): cant resolve host %s", host); |
420 |
close (s); |
421 |
return NULL; |
422 |
} |
423 |
|
424 |
in = (struct in_addr *) (hp->h_addr_list[0]); |
425 |
sa.sin_family = AF_INET; |
426 |
sa.sin_addr.s_addr = in->s_addr; |
427 |
sa.sin_port = htons (port); |
428 |
|
429 |
optval = 1; |
430 |
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof (optval)); |
431 |
|
432 |
if (bind (s, (struct sockaddr *) &sa, sizeof (sa)) < 0) |
433 |
{ |
434 |
close (s); |
435 |
slog (LG_ERROR, "connection_open_listener_tcp(): unable to bind listener %s[%d], errno [%d]", host, port, errno); |
436 |
return NULL; |
437 |
} |
438 |
|
439 |
socket_setnonblocking (s); |
440 |
|
441 |
/* XXX we need to have some sort of handling for SOMAXCONN */ |
442 |
if (listen (s, 5) < 0) |
443 |
{ |
444 |
close (s); |
445 |
slog (LG_ERROR, "connection_open_listener_tcp(): error: %s", strerror (errno)); |
446 |
return NULL; |
447 |
} |
448 |
|
449 |
cptr = connection_add (buf, s, CF_LISTENING, read_handler, NULL); |
450 |
|
451 |
return cptr; |
452 |
} |
453 |
|
454 |
/* |
455 |
* connection_accept_tcp() |
456 |
* |
457 |
* inputs: |
458 |
* listener to accept from, read handler, write handler |
459 |
* |
460 |
* outputs: |
461 |
* the connection_t on success, NULL on failure. |
462 |
* |
463 |
* side effects: |
464 |
* a TCP/IP connection is accepted from the host, |
465 |
* and interest is registered in read/write events. |
466 |
*/ |
467 |
connection_t * |
468 |
connection_accept_tcp (connection_t *cptr, void (*read_handler) (connection_t *), void (*write_handler) (connection_t *)) |
469 |
{ |
470 |
char buf[BUFSIZE]; |
471 |
connection_t *newptr; |
472 |
unsigned s; |
473 |
|
474 |
if (!(s = accept (cptr->fd, NULL, NULL))) |
475 |
{ |
476 |
slog (LG_INFO, "connection_accept_tcp(): accept failed"); |
477 |
return NULL; |
478 |
} |
479 |
|
480 |
/* Has the highest fd gotten any higher yet? */ |
481 |
if (s > system_state.maxfd) |
482 |
system_state.maxfd = s; |
483 |
|
484 |
socket_setnonblocking (s); |
485 |
|
486 |
strlcpy (buf, "incoming connection", BUFSIZE); |
487 |
newptr = connection_add (buf, s, 0, read_handler, write_handler); |
488 |
newptr->listener = cptr; |
489 |
return newptr; |
490 |
} |
491 |
|
492 |
/* |
493 |
* connection_setselect() |
494 |
* |
495 |
* inputs: |
496 |
* connection_t to register/deregister interest on, |
497 |
* replacement handlers (NULL = no interest) |
498 |
* |
499 |
* outputs: |
500 |
* none |
501 |
* |
502 |
* side effects: |
503 |
* the handlers are changed for the connection_t. |
504 |
*/ |
505 |
void |
506 |
connection_setselect (connection_t *cptr, void (*read_handler) (connection_t *), void (*write_handler) (connection_t *)) |
507 |
{ |
508 |
cptr->read_handler = read_handler; |
509 |
cptr->write_handler = write_handler; |
510 |
} |
511 |
|
512 |
/* |
513 |
* connection_stats() |
514 |
* |
515 |
* inputs: |
516 |
* callback function, data for callback function |
517 |
* |
518 |
* outputs: |
519 |
* none |
520 |
* |
521 |
* side effects: |
522 |
* callback function is called with a series of lines |
523 |
*/ |
524 |
void |
525 |
connection_stats (void (*stats_cb) (char const * const , void *), void *privdata) |
526 |
{ |
527 |
char buf[160]; |
528 |
char buf2[20]; |
529 |
connection_vector::iterator it = connlist.begin (); |
530 |
connection_vector::iterator et = connlist.end (); |
531 |
|
532 |
while (it != et) |
533 |
{ |
534 |
connection_t *c = *it; |
535 |
|
536 |
snprintf (buf, sizeof buf, "fd %-3d desc '%s'", c->fd, c->flags & CF_UPLINK ? "uplink" : c->flags & CF_LISTENING ? "listener" : "misc"); |
537 |
if (c->listener != NULL) |
538 |
{ |
539 |
snprintf (buf2, sizeof buf2, " listener %d", c->listener->fd); |
540 |
strlcat (buf, buf2, sizeof buf); |
541 |
} |
542 |
if (c->flags & (CF_CONNECTING | CF_DEAD | CF_NONEWLINE | CF_SEND_EOF | CF_SEND_DEAD)) |
543 |
{ |
544 |
strlcat (buf, " status", sizeof buf); |
545 |
if (c->flags & CF_CONNECTING) |
546 |
strlcat (buf, " connecting", sizeof buf); |
547 |
if (c->flags & CF_DEAD) |
548 |
strlcat (buf, " dead", sizeof buf); |
549 |
if (c->flags & CF_NONEWLINE) |
550 |
strlcat (buf, " nonewline", sizeof buf); |
551 |
if (c->flags & CF_SEND_DEAD) |
552 |
strlcat (buf, " send_dead", sizeof buf); |
553 |
else if (c->flags & CF_SEND_EOF) |
554 |
strlcat (buf, " send_eof", sizeof buf); |
555 |
} |
556 |
stats_cb (buf, privdata); |
557 |
|
558 |
++it; |
559 |
} |
560 |
} |
561 |
|
562 |
/* |
563 |
* connection_write() |
564 |
* |
565 |
* inputs: |
566 |
* connection_t to write to, format string, parameters |
567 |
* |
568 |
* outputs: |
569 |
* none |
570 |
* |
571 |
* side effects: |
572 |
* data is added to the connection_t sendq cache. |
573 |
*/ |
574 |
void |
575 |
connection_write (connection_t *to, char *format, ...) |
576 |
{ |
577 |
char buf[BUFSIZE * 12]; |
578 |
va_list args; |
579 |
unsigned int len; |
580 |
|
581 |
va_start (args, format); |
582 |
vsnprintf (buf, BUFSIZE * 12, format, args); |
583 |
va_end (args); |
584 |
|
585 |
len = strlen (buf); |
586 |
buf[len++] = '\r'; |
587 |
buf[len++] = '\n'; |
588 |
buf[len] = '\0'; |
589 |
|
590 |
sendq_add (to, buf, len); |
591 |
} |
592 |
|
593 |
/* |
594 |
* connection_write_raw() |
595 |
* |
596 |
* inputs: |
597 |
* connection_t to write to, raw string to write |
598 |
* |
599 |
* outputs: |
600 |
* none |
601 |
* |
602 |
* side effects: |
603 |
* data is added to the connection_t sendq cache. |
604 |
*/ |
605 |
void |
606 |
connection_write_raw (connection_t *to, char *data) |
607 |
{ |
608 |
sendq_add (to, data, strlen (data)); |
609 |
} |