ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/connection.C
Revision: 1.3
Committed: Sat Jul 21 13:23:21 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.2: +1 -1 lines
Log Message:
- added rcsid to some files
- more documentation tweaks
- made most protocol commands local to phandler.C
- added ircd metadata (inspircd only for now)
- added inspircd swhois support

File Contents

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