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
24 * $Id: commio.c 3354 2007-04-03 09:21:31Z nenolod $
27 #include "libcharybdis.h"
29 #ifndef IN_LOOPBACKNET
30 #define IN_LOOPBACKNET 0x7f
34 #define INADDR_NONE ((unsigned int) 0xffffffff)
37 const char *const NONB_ERROR_MSG
= "set_non_blocking failed for %s:%s";
38 const char *const SETBUF_ERROR_MSG
= "set_sock_buffers failed for server %s:%s";
40 static const char *comm_err_str
[] = { "Comm OK", "Error during bind()",
41 "Error during DNS lookup", "connect timeout",
42 "Error during connect()",
46 #define FD_HASH_SIZE 128
47 static dlink_list fd_table
[FD_HASH_SIZE
];
49 static void fdlist_update_biggest(int fd
, int opening
);
51 /* Highest FD and number of open FDs .. */
52 int highest_fd
= -1; /* Its -1 because we haven't started yet -- adrian */
55 static void comm_connect_callback(int fd
, int status
);
56 static PF comm_connect_timeout
;
57 static void comm_connect_dns_callback(void *vptr
, struct DNSReply
*reply
);
58 static PF comm_connect_tryconnect
;
59 static int comm_max_connections
= 0;
62 comm_read_raw(fde_t
*F
, void *buf
, size_t count
)
65 s_assert(buf
!= NULL
);
68 return read(F
->fd
, buf
, count
);
72 comm_write_raw(fde_t
*F
, const void *buf
, size_t count
)
75 s_assert(buf
!= NULL
);
78 return write(F
->fd
, buf
, count
);
82 comm_locate_fd(int fd
)
84 int bucket
= fd
% FD_HASH_SIZE
;
85 dlink_list
*list
= &fd_table
[bucket
];
88 DLINK_FOREACH(n
, list
->head
)
90 fde_t
*F
= (fde_t
*) n
->data
;
102 fde_t
*F
= comm_locate_fd(fd
);
108 F
= MyMalloc(sizeof(fde_t
));
111 F
->read_impl
= comm_read_raw
;
112 F
->write_impl
= comm_write_raw
;
114 list
= &fd_table
[fd
% FD_HASH_SIZE
];
115 dlinkAdd(F
, &F
->node
, list
);
121 comm_remove_fd(int fd
)
123 int bucket
= fd
% FD_HASH_SIZE
;
125 dlink_list
*list
= &fd_table
[bucket
];
127 F
= comm_locate_fd(fd
);
131 dlinkDelete(&F
->node
, list
);
135 /* 32bit solaris is kinda slow and stdio only supports fds < 256
136 * so we got to do this crap below.
137 * (BTW Fuck you Sun, I hate your guts and I hope you go bankrupt soon)
138 * XXX: this is no longer needed in Solaris 10. --nenolod
140 #if defined (__SVR4) && defined (__sun)
141 static void comm_fd_hack(int *fd
)
144 if(*fd
> 256 || *fd
< 0)
146 if((newfd
= fcntl(*fd
, F_DUPFD
, 256)) != -1)
154 #define comm_fd_hack(fd)
158 /* close_all_connections() can be used *before* the system come up! */
169 * we start at 4 to avoid giving fds where malloc messages
170 * could be written --nenolod
172 for (i
= 4; i
< comm_max_connections
; ++i
)
174 fde_t
*F
= comm_locate_fd(i
);
176 if(F
!= NULL
&& F
->flags
.open
)
182 /* XXX should his hack be done in all cases? */
184 /* fugly hack to reserve fd == 2 */
186 fd
= open("stderr.log", O_WRONLY
| O_CREAT
| O_APPEND
, 0644);
196 * get_sockerr - get the error value from the socket or the current errno
198 * Get the *real* error from the socket (well try to anyway..).
199 * This may only work when SO_DEBUG is enabled but its worth the
203 comm_get_sockerr(int fd
)
208 socklen_t len
= sizeof(err
);
210 if(-1 < fd
&& !getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, (char *) &err
, (socklen_t
*) & len
))
221 * set_sock_buffers - set send and receive buffers for socket
223 * inputs - fd file descriptor
225 * output - returns true (1) if successful, false (0) otherwise
229 comm_set_buffers(int fd
, int size
)
232 (fd
, SOL_SOCKET
, SO_RCVBUF
, (char *) &size
, sizeof(size
))
233 || setsockopt(fd
, SOL_SOCKET
, SO_SNDBUF
, (char *) &size
, sizeof(size
)))
239 * set_non_blocking - Set the client connection into non-blocking mode.
241 * inputs - fd to set into non blocking mode
242 * output - 1 if successful 0 if not
243 * side effects - use POSIX compliant non blocking and
251 fde_t
*F
= comm_locate_fd(fd
);
254 res
= fcntl(fd
, F_GETFL
, 0);
255 if(-1 == res
|| fcntl(fd
, F_SETFL
, res
| nonb
) == -1)
259 F
->flags
.nonblocking
= 1;
266 * stolen from squid - its a neat (but overused! :) routine which we
267 * can use to see whether we can ignore this errno or not. It is
268 * generally useful for non-blocking network IO related errnos.
272 ignoreErrno(int ierrno
)
278 #if EAGAIN != EWOULDBLOCK
294 * comm_settimeout() - set the socket timeout
296 * Set the timeout for the fd
299 comm_settimeout(int fd
, time_t timeout
, PF
* callback
, void *cbdata
)
303 F
= comm_locate_fd(fd
);
304 s_assert(F
->flags
.open
);
306 F
->timeout
= CurrentTime
+ (timeout
/ 1000);
307 F
->timeout_handler
= callback
;
308 F
->timeout_data
= cbdata
;
313 * comm_setflush() - set a flush function
315 * A flush function is simply a function called if found during
316 * comm_timeouts(). Its basically a second timeout, except in this case
317 * I'm too lazy to implement multiple timeout functions! :-)
318 * its kinda nice to have it seperate, since this is designed for
319 * flush functions, and when comm_close() is implemented correctly
320 * with close functions, we _actually_ don't call comm_close() here ..
323 comm_setflush(int fd
, time_t timeout
, PF
* callback
, void *cbdata
)
327 F
= comm_locate_fd(fd
);
328 s_assert(F
->flags
.open
);
330 F
->flush_timeout
= CurrentTime
+ (timeout
/ 1000);
331 F
->flush_handler
= callback
;
332 F
->flush_data
= cbdata
;
337 * comm_checktimeouts() - check the socket timeouts
339 * All this routine does is call the given callback/cbdata, without closing
340 * down the file descriptor. When close handlers have been implemented,
344 comm_checktimeouts(void *notused
)
353 for (i
= 0; i
<= FD_HASH_SIZE
; i
++)
355 bucket
= &fd_table
[i
];
357 if (dlink_list_length(bucket
) <= 0)
360 DLINK_FOREACH_SAFE(n
, n2
, bucket
->head
)
362 F
= (fde_t
*) n
->data
;
371 /* check flush functions */
372 if(F
->flush_handler
&&
373 F
->flush_timeout
> 0 && F
->flush_timeout
< CurrentTime
)
375 hdl
= F
->flush_handler
;
376 data
= F
->flush_data
;
377 comm_setflush(F
->fd
, 0, NULL
, NULL
);
382 if(F
->timeout_handler
&&
383 F
->timeout
> 0 && F
->timeout
< CurrentTime
)
385 /* Call timeout handler */
386 hdl
= F
->timeout_handler
;
387 data
= F
->timeout_data
;
388 comm_settimeout(F
->fd
, 0, NULL
, NULL
);
396 * void comm_connect_tcp(int fd, const char *host, u_short port,
397 * struct sockaddr *clocal, int socklen,
398 * CNCB *callback, void *data, int aftype, int timeout)
399 * Input: An fd to connect with, a host and port to connect to,
400 * a local sockaddr to connect from + length(or NULL to use the
401 * default), a callback, the data to pass into the callback, the
404 * Side-effects: A non-blocking connection to the host is started, and
405 * if necessary, set up for selection. The callback given
406 * may be called now, or it may be called later.
409 comm_connect_tcp(int fd
, const char *host
, u_short port
,
410 struct sockaddr
*clocal
, int socklen
, CNCB
* callback
,
411 void *data
, int aftype
, int timeout
)
416 F
= comm_locate_fd(fd
);
417 F
->flags
.called_connect
= 1;
419 F
->connect
.callback
= callback
;
420 F
->connect
.data
= data
;
422 memset(&F
->connect
.hostaddr
, 0, sizeof(F
->connect
.hostaddr
));
424 if(aftype
== AF_INET6
)
426 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)&F
->connect
.hostaddr
;
427 SET_SS_LEN(F
->connect
.hostaddr
, sizeof(struct sockaddr_in6
));
428 in6
->sin6_port
= htons(port
);
429 in6
->sin6_family
= AF_INET6
;
430 ipptr
= &in6
->sin6_addr
;
434 struct sockaddr_in
*in
= (struct sockaddr_in
*)&F
->connect
.hostaddr
;
435 SET_SS_LEN(F
->connect
.hostaddr
, sizeof(struct sockaddr_in
));
436 in
->sin_port
= htons(port
);
437 in
->sin_family
= AF_INET
;
438 ipptr
= &in
->sin_addr
;
441 /* Note that we're using a passed sockaddr here. This is because
442 * generally you'll be bind()ing to a sockaddr grabbed from
443 * getsockname(), so this makes things easier.
444 * XXX If NULL is passed as local, we should later on bind() to the
445 * virtual host IP, for completeness.
448 if((clocal
!= NULL
) && (bind(F
->fd
, clocal
, socklen
) < 0))
450 /* Failure, call the callback with COMM_ERR_BIND */
451 comm_connect_callback(F
->fd
, COMM_ERR_BIND
);
456 /* Next, if we have been given an IP, get the addr and skip the
457 * DNS check (and head direct to comm_connect_tryconnect().
459 if(inetpton(aftype
, host
, ipptr
) <= 0)
461 /* Send the DNS request, for the next level */
462 F
->dns_query
= MyMalloc(sizeof(struct DNSQuery
));
463 F
->dns_query
->ptr
= F
;
464 F
->dns_query
->callback
= comm_connect_dns_callback
;
466 if (aftype
== AF_INET6
)
467 gethost_byname_type(host
, F
->dns_query
, T_AAAA
);
470 gethost_byname_type(host
, F
->dns_query
, T_A
);
474 /* We have a valid IP, so we just call tryconnect */
475 /* Make sure we actually set the timeout here .. */
476 comm_settimeout(F
->fd
, timeout
* 1000, comm_connect_timeout
, NULL
);
477 comm_connect_tryconnect(F
->fd
, NULL
);
482 * comm_connect_callback() - call the callback, and continue with life
485 comm_connect_callback(int fd
, int status
)
488 fde_t
*F
= comm_locate_fd(fd
);
490 /* This check is gross..but probably necessary */
491 if(F
== NULL
|| F
->connect
.callback
== NULL
)
494 /* Clear the connect flag + handler */
495 hdl
= F
->connect
.callback
;
496 F
->connect
.callback
= NULL
;
497 F
->flags
.called_connect
= 0;
499 /* Clear the timeout handler */
500 comm_settimeout(F
->fd
, 0, NULL
, NULL
);
502 /* Call the handler */
503 hdl(F
->fd
, status
, F
->connect
.data
);
508 * comm_connect_timeout() - this gets called when the socket connection
509 * times out. This *only* can be called once connect() is initially
513 comm_connect_timeout(int fd
, void *notused
)
516 comm_connect_callback(fd
, COMM_ERR_TIMEOUT
);
521 * comm_connect_dns_callback() - called at the completion of the DNS request
523 * The DNS request has completed, so if we've got an error, return it,
524 * otherwise we initiate the connect()
527 comm_connect_dns_callback(void *vptr
, struct DNSReply
*reply
)
531 /* Free dns_query now to avoid double reslist free -- jilles */
532 MyFree(F
->dns_query
);
537 comm_connect_callback(F
->fd
, COMM_ERR_DNS
);
541 /* No error, set a 10 second timeout */
542 comm_settimeout(F
->fd
, 30 * 1000, comm_connect_timeout
, NULL
);
544 /* Copy over the DNS reply info so we can use it in the connect() */
546 if(reply
->addr
.ss_family
== AF_INET6
)
548 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)&F
->connect
.hostaddr
;
549 memcpy(&in6
->sin6_addr
, &((struct sockaddr_in6
*)&reply
->addr
)->sin6_addr
, sizeof(struct in6_addr
));
554 struct sockaddr_in
*in
= (struct sockaddr_in
*)&F
->connect
.hostaddr
;
555 in
->sin_addr
.s_addr
= ((struct sockaddr_in
*)&reply
->addr
)->sin_addr
.s_addr
;
558 /* Now, call the tryconnect() routine to try a connect() */
559 comm_connect_tryconnect(F
->fd
, NULL
);
563 /* static void comm_connect_tryconnect(int fd, void *notused)
564 * Input: The fd, the handler data(unused).
566 * Side-effects: Try and connect with pending connect data for the FD. If
567 * we succeed or get a fatal error, call the callback.
568 * Otherwise, it is still blocking or something, so register
569 * to select for a write event on this FD.
572 comm_connect_tryconnect(int fd
, void *notused
)
575 fde_t
*F
= comm_locate_fd(fd
);
577 if(F
->connect
.callback
== NULL
)
579 /* Try the connect() */
580 retval
= connect(fd
, (struct sockaddr
*) &F
->connect
.hostaddr
,
581 GET_SS_LEN(F
->connect
.hostaddr
));
586 * If we get EISCONN, then we've already connect()ed the socket,
587 * which is a good thing.
591 comm_connect_callback(F
->fd
, COMM_OK
);
592 else if(ignoreErrno(errno
))
593 /* Ignore error? Reschedule */
594 comm_setselect(F
->fd
, FDLIST_SERVER
, COMM_SELECT_WRITE
|COMM_SELECT_RETRY
,
595 comm_connect_tryconnect
, NULL
, 0);
597 /* Error? Fail with COMM_ERR_CONNECT */
598 comm_connect_callback(F
->fd
, COMM_ERR_CONNECT
);
601 /* If we get here, we've suceeded, so call with COMM_OK */
602 comm_connect_callback(F
->fd
, COMM_OK
);
606 * comm_error_str() - return an error string for the given error condition
609 comm_errstr(int error
)
611 if(error
< 0 || error
>= COMM_ERR_MAX
)
612 return "Invalid error number!";
613 return comm_err_str
[error
];
618 * comm_socket() - open a socket
620 * This is a highly highly cut down version of squid's comm_open() which
621 * for the most part emulates socket(), *EXCEPT* it fails if we're about
622 * to run out of file descriptors.
625 comm_socket(int family
, int sock_type
, int proto
, const char *note
)
628 /* First, make sure we aren't going to run out of file descriptors */
629 if(number_fd
>= comm_max_connections
)
636 * Next, we try to open the socket. We *should* drop the reserved FD
637 * limit if/when we get an error, but we can deal with that later.
640 fd
= socket(family
, sock_type
, proto
);
643 return -1; /* errno will be passed through, yay.. */
645 #if defined(IPV6) && defined(IPV6_V6ONLY)
647 * Make sure we can take both IPv4 and IPv6 connections
648 * on an AF_INET6 socket
650 if(family
== AF_INET6
)
653 if(setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &off
, sizeof(off
)) == -1)
655 libcharybdis_log("comm_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
656 fd
, strerror(errno
));
663 /* Set the socket non-blocking, and other wonderful bits */
666 libcharybdis_log("comm_open: Couldn't set FD %d non blocking: %s", fd
, strerror(errno
));
671 /* Next, update things in our fd tracking */
672 comm_open(fd
, FD_SOCKET
, note
);
678 * comm_accept() - accept an incoming connection
680 * This is a simple wrapper for accept() which enforces FD limits like
684 comm_accept(int fd
, struct sockaddr
*pn
, socklen_t
*addrlen
)
687 if(number_fd
>= comm_max_connections
)
694 * Next, do the accept(). if we get an error, we should drop the
695 * reserved fd limit, but we can deal with that when comm_open()
696 * also does it. XXX -- adrian
698 newfd
= accept(fd
, (struct sockaddr
*) pn
, addrlen
);
699 comm_fd_hack(&newfd
);
704 /* Set the socket non-blocking, and other wonderful bits */
705 if(!comm_set_nb(newfd
))
707 libcharybdis_log("comm_accept: Couldn't set FD %d non blocking!", newfd
);
712 /* Next, tag the FD as an incoming connection */
713 comm_open(newfd
, FD_SOCKET
, "Incoming connection");
720 * If a sockaddr_storage is AF_INET6 but is a mapped IPv4
721 * socket manged the sockaddr.
723 #ifndef mangle_mapped_sockaddr
725 mangle_mapped_sockaddr(struct sockaddr
*in
)
727 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)in
;
729 if(in
->sa_family
== AF_INET
)
732 if(in
->sa_family
== AF_INET6
&& IN6_IS_ADDR_V4MAPPED(&in6
->sin6_addr
))
734 struct sockaddr_in in4
;
735 memset(&in4
, 0, sizeof(struct sockaddr_in
));
736 in4
.sin_family
= AF_INET
;
737 in4
.sin_port
= in6
->sin6_port
;
738 in4
.sin_addr
.s_addr
= ((uint32_t *)&in6
->sin6_addr
)[3];
739 memcpy(in
, &in4
, sizeof(struct sockaddr_in
));
747 fdlist_update_biggest(int fd
, int opening
)
751 s_assert(fd
< comm_max_connections
);
756 * s_assert that we are not closing a FD bigger than
757 * our known biggest FD
763 /* if we are here, then fd == Biggest_FD */
765 * s_assert that we are closing the biggest FD; we can't be
769 while (highest_fd
>= 0 && comm_locate_fd(fd
) != NULL
)
777 static int initialized
= 0;
782 memset(&fd_table
, '\0', sizeof(dlink_list
) * FD_HASH_SIZE
);
784 /* set up comm_max_connections. */
785 if(!getrlimit(RLIMIT_NOFILE
, &limit
))
786 comm_max_connections
= limit
.rlim_cur
;
792 /* Called to open a given filedescriptor */
794 comm_open(int fd
, unsigned int type
, const char *desc
)
796 fde_t
*F
= comm_add_fd(fd
);
803 s_assert(!F
->flags
.open
);
810 F
->defer
.handler
= NULL
;
812 fdlist_update_biggest(fd
, 1);
814 F
->list
= FDLIST_NONE
;
816 strlcpy(F
->desc
, desc
, sizeof(F
->desc
));
821 /* Called to close a given filedescriptor */
825 fde_t
*F
= comm_locate_fd(fd
);
826 s_assert(F
->flags
.open
);
827 /* All disk fd's MUST go through file_close() ! */
828 s_assert(F
->type
!= FD_FILE
);
829 if(F
->type
== FD_FILE
)
831 s_assert(F
->read_handler
== NULL
);
832 s_assert(F
->write_handler
== NULL
);
834 comm_setselect(F
->fd
, FDLIST_NONE
, COMM_SELECT_WRITE
| COMM_SELECT_READ
, NULL
, NULL
, 0);
835 comm_setflush(F
->fd
, 0, NULL
, NULL
);
838 if (F
->dns_query
!= NULL
)
840 delete_resolver_queries(F
->dns_query
);
841 MyFree(F
->dns_query
);
846 fdlist_update_biggest(fd
, 0);
850 /* Unlike squid, we're actually closing the FD here! -- adrian */
855 * comm_dump() - dump the list of active filedescriptors
858 comm_dump(struct Client
*source_p
)
862 for (i
= 0; i
<= FD_HASH_SIZE
; i
++)
866 if (dlink_list_length(&fd_table
[i
]) <= 0)
869 DLINK_FOREACH(n
, fd_table
[i
].head
)
871 fde_t
*F
= (fde_t
*) n
->data
;
873 if(F
== NULL
|| !F
->flags
.open
)
876 sendto_one_numeric(source_p
, RPL_STATSDEBUG
,
877 "F :fd %-3d bucket %-3d desc '%s'",
884 * comm_note() - set the fd note
886 * Note: must be careful not to overflow fd_table[fd].desc when
890 comm_note(int fd
, const char *format
, ...)
893 fde_t
*F
= comm_add_fd(fd
); /* XXX: epoll, kqueue. */
897 va_start(args
, format
);
898 ircvsnprintf(F
->desc
, FD_DESC_SZ
, format
, args
);
906 comm_get_maxconnections(void)
910 return comm_max_connections
;