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

# User Rev Content
1 pippijn 1.1 /*
2     * connection.C: Connection and I/O management
3 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
4 pippijn 1.1 *
5 pippijn 1.4 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
6 pippijn 1.1 */
7    
8 pippijn 1.4 static char const rcsid[] = "$Id: connection.C,v 1.3 2007-07-21 13:23:21 pippijn Exp $";
9 pippijn 1.1
10     #include "atheme.h"
11     #include "internal.h"
12     #include "datastream.h"
13    
14 pippijn 1.4 connection_vector connlist;
15     connection_t::callbacks connection_t::callback;
16 pippijn 1.1
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 pippijn 1.4 connection_add (char const * const name, int fd, unsigned int flags, void (*read_handler) (connection_t *), void (*write_handler) (connection_t *))
55 pippijn 1.1 {
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 pippijn 1.4 slog (LG_DEBUG, "connection_add(): adding connection '%s', fd = %d", name, fd);
65 pippijn 1.1
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 pippijn 1.4 connlist.insert (cptr);
88 pippijn 1.1
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 pippijn 1.4 connection_vector::iterator it = connlist.begin ();
109     connection_vector::iterator et = connlist.end ();
110 pippijn 1.1
111 pippijn 1.4 while (it != et)
112     {
113     cptr = *it;
114    
115     if (cptr->fd == fd)
116     return cptr;
117    
118     ++it;
119     }
120 pippijn 1.1
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 pippijn 1.4 return connlist.size ();
140 pippijn 1.1 }
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 pippijn 1.4 connection_vector::iterator it;
158 pippijn 1.1 int errno1, errno2;
159     #ifdef SO_ERROR
160     socklen_t len = sizeof (errno2);
161     #endif
162    
163 pippijn 1.4 it = connlist.find (cptr);
164     if (it == connlist.end ())
165 pippijn 1.1 {
166     slog (LG_DEBUG, "connection_close(): connection %p is not registered!", cptr);
167     return;
168     }
169    
170     errno1 = errno;
171     #ifdef SO_ERROR
172 pippijn 1.4 if (!getsockopt (cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &errno2, (socklen_t *) &len))
173 pippijn 1.1 {
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 pippijn 1.4 connlist.erase (cptr);
189 pippijn 1.1
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 pippijn 1.4 connection_vector::iterator it = connlist.begin ();
248     connection_vector::iterator et = connlist.end ();
249 pippijn 1.1 connection_t *cptr2;
250    
251 pippijn 1.4 return_if_fail (cptr != NULL);
252 pippijn 1.1
253     if (CF_IS_LISTENING (cptr))
254     {
255 pippijn 1.4 while (it != et)
256     {
257     cptr2 = *it;
258     if (cptr2->listener == cptr)
259     connection_close_soon (cptr2);
260     ++it;
261     }
262 pippijn 1.1 }
263 pippijn 1.4
264 pippijn 1.1 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 pippijn 1.4 while (!connlist.empty ())
283     connection_close (connlist.back ());
284 pippijn 1.1 }
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 pippijn 1.4 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 pippijn 1.1 {
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 pippijn 1.4 if (s > system_state.maxfd)
319     system_state.maxfd = s;
320 pippijn 1.1
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 pippijn 1.4 connection_open_listener_tcp (char const * const host, unsigned int port, void (*read_handler) (connection_t *))
393 pippijn 1.1 {
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 pippijn 1.4 if (s > system_state.maxfd)
410     system_state.maxfd = s;
411 pippijn 1.1
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 pippijn 1.4 if (s > system_state.maxfd)
482     system_state.maxfd = s;
483 pippijn 1.1
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 pippijn 1.4 connection_stats (void (*stats_cb) (char const * const , void *), void *privdata)
526 pippijn 1.1 {
527     char buf[160];
528     char buf2[20];
529 pippijn 1.4 connection_vector::iterator it = connlist.begin ();
530     connection_vector::iterator et = connlist.end ();
531 pippijn 1.1
532 pippijn 1.4 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 pippijn 1.1 }
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     }