Initial import
[ratbox-ambernet.git] / src / commio.c
blob51b097e51c3eb9df957060abea92fba9954c419e
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * commio.c: Network/file related functions
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
24 * $Id: commio.c 21654 2006-01-13 16:33:18Z androsyn $
27 #include "stdinc.h"
28 #include "config.h"
29 #include "commio.h"
30 #include "class.h"
31 #include "client.h"
32 #include "common.h"
33 #include "event.h"
34 #include "irc_string.h"
35 #include "sprintf_irc.h"
36 #include "ircd.h"
37 #include "linebuf.h"
38 #include "listener.h"
39 #include "numeric.h"
40 #include "packet.h"
41 #include "res.h"
42 #include "restart.h"
43 #include "s_auth.h"
44 #include "s_conf.h"
45 #include "s_log.h"
46 #include "s_serv.h"
47 #include "s_stats.h"
48 #include "send.h"
49 #include "reject.h"
50 #include "memory.h"
53 #ifndef IN_LOOPBACKNET
54 #define IN_LOOPBACKNET 0x7f
55 #endif
57 #ifndef INADDR_NONE
58 #define INADDR_NONE ((unsigned int) 0xffffffff)
59 #endif
61 /* 3.x compat macro */
62 #define find_fd(x) (&fd_table[x])
64 const char *const NONB_ERROR_MSG = "set_non_blocking failed for %s:%s";
65 const char *const SETBUF_ERROR_MSG = "set_sock_buffers failed for server %s:%s";
67 static const char *comm_err_str[] = { "Comm OK", "Error during bind()",
68 "Error during DNS lookup", "connect timeout",
69 "Error during connect()",
70 "Comm Error"
73 fde_t *fd_table = NULL;
75 static void fdlist_update_biggest(int fd, int opening);
76 static dlink_list timeout_list;
78 /* Highest FD and number of open FDs .. */
79 int highest_fd = -1; /* Its -1 because we haven't started yet -- adrian */
80 int number_fd = 0;
83 static void comm_connect_callback(int fd, int status);
84 static PF comm_connect_timeout;
85 static void comm_connect_dns_callback(void *vptr, adns_answer * reply);
86 static PF comm_connect_tryconnect;
88 /* 32bit solaris is kinda slow and stdio only supports fds < 256
89 * so we got to do this crap below.
90 * (BTW Fuck you Sun, I hate your guts and I hope you go bankrupt soon)
92 #if defined (__SVR4) && defined (__sun)
93 static void comm_fd_hack(int *fd)
95 int newfd;
96 if(*fd > 256 || *fd < 0)
97 return;
98 if((newfd = fcntl(*fd, F_DUPFD, 256)) != -1)
100 close(*fd);
101 *fd = newfd;
103 return;
105 #else
106 #define comm_fd_hack(fd)
107 #endif
110 /* close_all_connections() can be used *before* the system come up! */
112 void
113 comm_close_all(void)
115 int i;
116 #ifndef NDEBUG
117 int fd;
118 #endif
120 /* XXX someone tell me why we care about 4 fd's ? */
121 /* XXX btw, fd 3 is used for profiler ! */
123 for (i = 4; i < maxconnections; ++i)
125 if(fd_table[i].flags.open)
126 comm_close(i);
127 else
128 close(i);
131 /* XXX should his hack be done in all cases? */
132 #ifndef NDEBUG
133 /* fugly hack to reserve fd == 2 */
134 (void) close(2);
135 fd = open("stderr.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
136 if(fd >= 0)
138 dup2(fd, 2);
139 close(fd);
141 #endif
145 * get_sockerr - get the error value from the socket or the current errno
147 * Get the *real* error from the socket (well try to anyway..).
148 * This may only work when SO_DEBUG is enabled but its worth the
149 * gamble anyway.
152 comm_get_sockerr(int fd)
154 int errtmp = errno;
155 #ifdef SO_ERROR
156 int err = 0;
157 socklen_t len = sizeof(err);
159 if(-1 < fd && !getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &err, (socklen_t *) & len))
161 if(err)
162 errtmp = err;
164 errno = errtmp;
165 #endif
166 return errtmp;
170 * set_sock_buffers - set send and receive buffers for socket
172 * inputs - fd file descriptor
173 * - size to set
174 * output - returns true (1) if successful, false (0) otherwise
175 * side effects -
178 comm_set_buffers(int fd, int size)
180 if(setsockopt
181 (fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size))
182 || setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)))
183 return 0;
184 return 1;
188 * set_non_blocking - Set the client connection into non-blocking mode.
190 * inputs - fd to set into non blocking mode
191 * output - 1 if successful 0 if not
192 * side effects - use POSIX compliant non blocking and
193 * be done with it.
196 comm_set_nb(int fd)
198 int nonb = 0;
199 int res;
200 if(comm_setup_fd(fd))
201 return 1;
203 nonb |= O_NONBLOCK;
204 res = fcntl(fd, F_GETFL, 0);
205 if(-1 == res || fcntl(fd, F_SETFL, res | nonb) == -1)
206 return 0;
208 fd_table[fd].flags.nonblocking = 1;
209 return 1;
214 * stolen from squid - its a neat (but overused! :) routine which we
215 * can use to see whether we can ignore this errno or not. It is
216 * generally useful for non-blocking network IO related errnos.
217 * -- adrian
220 ignoreErrno(int ierrno)
222 switch (ierrno)
224 case EINPROGRESS:
225 case EWOULDBLOCK:
226 #if EAGAIN != EWOULDBLOCK
227 case EAGAIN:
228 #endif
229 case EALREADY:
230 case EINTR:
231 #ifdef ERESTART
232 case ERESTART:
233 #endif
234 return 1;
235 default:
236 return 0;
242 * comm_settimeout() - set the socket timeout
244 * Set the timeout for the fd
246 void
247 comm_settimeout(int fd, time_t timeout, PF * callback, void *cbdata)
249 fde_t *F;
250 struct timeout_data *td;
251 s_assert(fd >= 0);
252 F = find_fd(fd);
253 s_assert(F->flags.open);
254 td = F->timeout;
256 if(callback == NULL) /* user wants to remove */
258 if(td == NULL)
259 return;
260 dlinkDelete(&td->node, &timeout_list);
261 MyFree(td);
262 F->timeout = NULL;
263 return;
266 if(F->timeout == NULL)
267 td = F->timeout = MyMalloc(sizeof(struct timeout_data));
269 td->F = F;
270 td->timeout = CurrentTime + (timeout / 1000);
271 td->timeout_handler = callback;
272 td->timeout_data = cbdata;
273 dlinkAdd(td, &td->node, &timeout_list);
277 * comm_checktimeouts() - check the socket timeouts
279 * All this routine does is call the given callback/cbdata, without closing
280 * down the file descriptor. When close handlers have been implemented,
281 * this will happen.
283 void
284 comm_checktimeouts(void *notused)
286 dlink_node *ptr, *next;
287 struct timeout_data *td;
288 fde_t *F;
289 PF *hdl;
290 void *data;
292 DLINK_FOREACH_SAFE(ptr, next, timeout_list.head)
294 td = ptr->data;
295 F = td->F;
296 if(F == NULL || F->flags.closing || !F->flags.open)
297 continue;
299 if(td->timeout < CurrentTime)
301 hdl = td->timeout_handler;
302 data = td->timeout_data;
303 dlinkDelete(&td->node, &timeout_list);
304 F->timeout = NULL;
305 MyFree(td);
306 hdl(F->fd, data);
313 * void comm_connect_tcp(int fd, const char *host, u_short port,
314 * struct sockaddr *clocal, int socklen,
315 * CNCB *callback, void *data, int aftype, int timeout)
316 * Input: An fd to connect with, a host and port to connect to,
317 * a local sockaddr to connect from + length(or NULL to use the
318 * default), a callback, the data to pass into the callback, the
319 * address family.
320 * Output: None.
321 * Side-effects: A non-blocking connection to the host is started, and
322 * if necessary, set up for selection. The callback given
323 * may be called now, or it may be called later.
325 void
326 comm_connect_tcp(int fd, const char *host, u_short port,
327 struct sockaddr *clocal, int socklen, CNCB * callback,
328 void *data, int aftype, int timeout)
330 void *ipptr = NULL;
331 fde_t *F;
332 s_assert(fd >= 0);
333 F = &fd_table[fd];
334 F->flags.called_connect = 1;
335 s_assert(callback);
336 F->connect.callback = callback;
337 F->connect.data = data;
339 memset(&F->connect.hostaddr, 0, sizeof(F->connect.hostaddr));
340 #ifdef IPV6
341 if(aftype == AF_INET6)
343 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr;
344 SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in6));
345 in6->sin6_port = htons(port);
346 in6->sin6_family = AF_INET6;
347 ipptr = &in6->sin6_addr;
348 } else
349 #endif
351 struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr;
352 SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in));
353 in->sin_port = htons(port);
354 in->sin_family = AF_INET;
355 ipptr = &in->sin_addr;
358 /* Note that we're using a passed sockaddr here. This is because
359 * generally you'll be bind()ing to a sockaddr grabbed from
360 * getsockname(), so this makes things easier.
361 * XXX If NULL is passed as local, we should later on bind() to the
362 * virtual host IP, for completeness.
363 * -- adrian
365 if((clocal != NULL) && (bind(F->fd, clocal, socklen) < 0))
367 /* Failure, call the callback with COMM_ERR_BIND */
368 comm_connect_callback(F->fd, COMM_ERR_BIND);
369 /* ... and quit */
370 return;
373 /* Next, if we have been given an IP, get the addr and skip the
374 * DNS check (and head direct to comm_connect_tryconnect().
376 if(inetpton(aftype, host, ipptr) <= 0)
378 /* Send the DNS request, for the next level */
379 F->dns_query = MyMalloc(sizeof(struct DNSQuery));
380 F->dns_query->ptr = F;
381 F->dns_query->callback = comm_connect_dns_callback;
382 adns_gethost(host, aftype, F->dns_query);
384 else
386 /* We have a valid IP, so we just call tryconnect */
387 /* Make sure we actually set the timeout here .. */
388 comm_settimeout(F->fd, timeout * 1000, comm_connect_timeout, NULL);
389 comm_connect_tryconnect(F->fd, NULL);
394 * comm_connect_callback() - call the callback, and continue with life
396 static void
397 comm_connect_callback(int fd, int status)
399 CNCB *hdl;
400 fde_t *F = &fd_table[fd];
401 /* This check is gross..but probably necessary */
402 if(F->connect.callback == NULL)
403 return;
404 /* Clear the connect flag + handler */
405 hdl = F->connect.callback;
406 F->connect.callback = NULL;
407 F->flags.called_connect = 0;
409 /* Clear the timeout handler */
410 comm_settimeout(F->fd, 0, NULL, NULL);
412 /* Call the handler */
413 hdl(F->fd, status, F->connect.data);
418 * comm_connect_timeout() - this gets called when the socket connection
419 * times out. This *only* can be called once connect() is initially
420 * called ..
422 static void
423 comm_connect_timeout(int fd, void *notused)
425 /* error! */
426 comm_connect_callback(fd, COMM_ERR_TIMEOUT);
431 * comm_connect_dns_callback() - called at the completion of the DNS request
433 * The DNS request has completed, so if we've got an error, return it,
434 * otherwise we initiate the connect()
436 static void
437 comm_connect_dns_callback(void *vptr, adns_answer * reply)
439 fde_t *F = vptr;
441 if(!reply)
443 comm_connect_callback(F->fd, COMM_ERR_DNS);
444 return;
447 if(reply->status != adns_s_ok)
449 /* Yes, callback + return */
450 comm_connect_callback(F->fd, COMM_ERR_DNS);
451 MyFree(reply);
452 MyFree(F->dns_query);
453 F->dns_query = NULL;
454 return;
457 /* No error, set a 10 second timeout */
458 comm_settimeout(F->fd, 30 * 1000, comm_connect_timeout, NULL);
460 /* Copy over the DNS reply info so we can use it in the connect() */
462 * Note we don't fudge the refcount here, because we aren't keeping
463 * the DNS record around, and the DNS cache is gone anyway..
464 * -- adrian
466 #ifdef IPV6
467 if(reply->rrs.addr->addr.sa.sa_family == AF_INET6)
469 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr;
470 memcpy(&in6->sin6_addr, &reply->rrs.addr->addr.inet6.sin6_addr, sizeof(struct in6_addr));
472 else
473 #endif
475 struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr;
476 in->sin_addr.s_addr = reply->rrs.addr->addr.inet.sin_addr.s_addr;
479 /* Now, call the tryconnect() routine to try a connect() */
480 MyFree(reply);
481 comm_connect_tryconnect(F->fd, NULL);
485 /* static void comm_connect_tryconnect(int fd, void *notused)
486 * Input: The fd, the handler data(unused).
487 * Output: None.
488 * Side-effects: Try and connect with pending connect data for the FD. If
489 * we succeed or get a fatal error, call the callback.
490 * Otherwise, it is still blocking or something, so register
491 * to select for a write event on this FD.
493 static void
494 comm_connect_tryconnect(int fd, void *notused)
496 int retval;
497 fde_t *F = &fd_table[fd];
499 if(F->connect.callback == NULL)
500 return;
501 /* Try the connect() */
502 retval = connect(fd,
503 (struct sockaddr *) &fd_table[fd].connect.hostaddr,
504 GET_SS_LEN(fd_table[fd].connect.hostaddr));
505 /* Error? */
506 if(retval < 0)
509 * If we get EISCONN, then we've already connect()ed the socket,
510 * which is a good thing.
511 * -- adrian
513 if(errno == EISCONN)
514 comm_connect_callback(F->fd, COMM_OK);
515 else if(ignoreErrno(errno))
516 /* Ignore error? Reschedule */
517 comm_setselect(F->fd, FDLIST_SERVER, COMM_SELECT_WRITE|COMM_SELECT_RETRY,
518 comm_connect_tryconnect, NULL);
519 else
520 /* Error? Fail with COMM_ERR_CONNECT */
521 comm_connect_callback(F->fd, COMM_ERR_CONNECT);
522 return;
524 /* If we get here, we've suceeded, so call with COMM_OK */
525 comm_connect_callback(F->fd, COMM_OK);
529 * comm_error_str() - return an error string for the given error condition
531 const char *
532 comm_errstr(int error)
534 if(error < 0 || error >= COMM_ERR_MAX)
535 return "Invalid error number!";
536 return comm_err_str[error];
541 * comm_socket() - open a socket
543 * This is a highly highly cut down version of squid's comm_open() which
544 * for the most part emulates socket(), *EXCEPT* it fails if we're about
545 * to run out of file descriptors.
548 comm_socket(int family, int sock_type, int proto, const char *note)
550 int fd;
551 /* First, make sure we aren't going to run out of file descriptors */
552 if(number_fd >= maxconnections)
554 errno = ENFILE;
555 return -1;
559 * Next, we try to open the socket. We *should* drop the reserved FD
560 * limit if/when we get an error, but we can deal with that later.
561 * XXX !!! -- adrian
563 fd = socket(family, sock_type, proto);
564 comm_fd_hack(&fd);
565 if(fd < 0)
566 return -1; /* errno will be passed through, yay.. */
568 #if defined(IPV6) && defined(IPV6_V6ONLY)
570 * Make sure we can take both IPv4 and IPv6 connections
571 * on an AF_INET6 socket
573 if(family == AF_INET6)
575 int off = 1;
576 if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) == -1)
578 ilog(L_IOERROR,
579 "comm_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
580 fd, strerror(errno));
581 close(fd);
582 return -1;
585 #endif
587 /* Set the socket non-blocking, and other wonderful bits */
588 if(!comm_set_nb(fd))
590 ilog(L_IOERROR, "comm_open: Couldn't set FD %d non blocking: %s", fd, strerror(errno));
591 close(fd);
592 return -1;
595 /* Next, update things in our fd tracking */
596 comm_open(fd, FD_SOCKET, note);
597 return fd;
602 * comm_accept() - accept an incoming connection
604 * This is a simple wrapper for accept() which enforces FD limits like
605 * comm_open() does.
608 comm_accept(int fd, struct sockaddr *pn, socklen_t *addrlen)
610 int newfd;
611 if(number_fd >= maxconnections)
613 errno = ENFILE;
614 return -1;
618 * Next, do the accept(). if we get an error, we should drop the
619 * reserved fd limit, but we can deal with that when comm_open()
620 * also does it. XXX -- adrian
622 newfd = accept(fd, (struct sockaddr *) pn, addrlen);
623 comm_fd_hack(&newfd);
625 if(newfd < 0)
626 return -1;
628 /* Set the socket non-blocking, and other wonderful bits */
629 if(!comm_set_nb(newfd))
631 ilog(L_IOERROR, "comm_accept: Couldn't set FD %d non blocking!", newfd);
632 close(newfd);
633 return -1;
636 /* Next, tag the FD as an incoming connection */
637 comm_open(newfd, FD_SOCKET, "Incoming connection");
639 /* .. and return */
640 return newfd;
644 * If a sockaddr_storage is AF_INET6 but is a mapped IPv4
645 * socket manged the sockaddr.
647 #ifndef mangle_mapped_sockaddr
648 void
649 mangle_mapped_sockaddr(struct sockaddr *in)
651 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
653 if(in->sa_family == AF_INET)
654 return;
656 if(in->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
658 struct sockaddr_in in4;
659 memset(&in4, 0, sizeof(struct sockaddr_in));
660 in4.sin_family = AF_INET;
661 in4.sin_port = in6->sin6_port;
662 in4.sin_addr.s_addr = ((uint32_t *)&in6->sin6_addr)[3];
663 memcpy(in, &in4, sizeof(struct sockaddr_in));
665 return;
667 #endif
670 static void
671 fdlist_update_biggest(int fd, int opening)
673 if(fd > highest_fd)
676 * s_assert that we are not closing a FD bigger than
677 * our known biggest FD
679 s_assert(opening);
680 highest_fd = fd;
681 return;
683 /* if we are here, then fd == Biggest_FD */
685 * s_assert that we are closing the biggest FD; we can't be
686 * re-opening it
688 s_assert(!opening);
689 while (highest_fd >= 0 && !fd_table[highest_fd].flags.open)
690 highest_fd--;
694 void
695 fdlist_init(void)
697 static int initialized = 0;
699 if(!initialized)
701 /* Since we're doing this once .. */
702 fd_table = MyMalloc((maxconnections + 1) * sizeof(fde_t));
703 initialized = 1;
707 /* Called to open a given filedescriptor */
708 void
709 comm_open(int fd, unsigned int type, const char *desc)
711 fde_t *F = &fd_table[fd];
712 s_assert(fd >= 0);
714 if(F->flags.open)
716 comm_close(fd);
718 s_assert(!F->flags.open);
719 F->fd = fd;
720 F->type = type;
721 F->flags.open = 1;
722 #ifdef NOTYET
723 F->defer.until = 0;
724 F->defer.n = 0;
725 F->defer.handler = NULL;
726 #endif
727 fdlist_update_biggest(fd, 1);
728 F->list = FDLIST_NONE;
729 if(desc)
730 strlcpy(F->desc, desc, sizeof(F->desc));
731 number_fd++;
735 /* Called to close a given filedescriptor */
736 void
737 comm_close(int fd)
739 fde_t *F = &fd_table[fd];
740 s_assert(F->flags.open);
741 /* All disk fd's MUST go through file_close() ! */
742 s_assert(F->type != FD_FILE);
743 if(F->type == FD_FILE)
745 s_assert(F->read_handler == NULL);
746 s_assert(F->write_handler == NULL);
748 comm_setselect(F->fd, FDLIST_NONE, COMM_SELECT_WRITE | COMM_SELECT_READ, NULL, NULL);
750 if (F->dns_query != NULL)
752 delete_adns_queries(F->dns_query);
753 MyFree(F->dns_query);
754 F->dns_query = NULL;
757 F->flags.open = 0;
758 fdlist_update_biggest(fd, 0);
759 number_fd--;
760 memset(F, '\0', sizeof(fde_t));
761 F->timeout = 0;
762 /* Unlike squid, we're actually closing the FD here! -- adrian */
763 close(fd);
768 * comm_dump() - dump the list of active filedescriptors
770 void
771 comm_dump(struct Client *source_p)
773 int i;
775 for (i = 0; i <= highest_fd; i++)
777 if(!fd_table[i].flags.open)
778 continue;
780 sendto_one_numeric(source_p, RPL_STATSDEBUG,
781 "F :fd %-3d desc '%s'",
782 i, fd_table[i].desc);
787 * comm_note() - set the fd note
789 * Note: must be careful not to overflow fd_table[fd].desc when
790 * calling.
792 void
793 comm_note(int fd, const char *format, ...)
795 va_list args;
797 if(format)
799 va_start(args, format);
800 ircvsnprintf(fd_table[fd].desc, FD_DESC_SZ, format, args);
801 va_end(args);
803 else
804 fd_table[fd].desc[0] = '\0';