ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/src/connection.C
Revision: 1.4
Committed: Tue Aug 28 17:08:12 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.3: +76 -99 lines
Log Message:
- changed name
- updated the example config to the new system
- added more documentation
- enhanced documentation generators
- added a link to the pdf to the website
- added an RSS feed generator
- transitioned hooks to c++ callbacks
- did various merges with upstream along the way
- added const where appropriate
- removed the old block allocator
- fixed most memory leaks
- transitioned some dictionaries to std::map
- transitioned some lists to std::vector
- made some free functions members where appropriate
- renamed string to dynstr and added a static string ststr
- use NOW instead of time (NULL) if possible
- completely reworked database backends, crypto handlers and protocol handlers
  to use an object factory
- removed the old module system. ermyth does not do any dynamic loading anymore
- fixed most of the build system
- reworked how protocol commands work

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: 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 }