ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/src/connection.C
Revision: 1.8
Committed: Sat Sep 22 14:27:30 2007 UTC (16 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +3 -1 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

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