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 21654 2006-01-13 16:33:18Z androsyn $
34 #include "irc_string.h"
35 #include "sprintf_irc.h"
53 #ifndef IN_LOOPBACKNET
54 #define IN_LOOPBACKNET 0x7f
58 #define INADDR_NONE ((unsigned int) 0xffffffff)
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()",
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 */
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
)
96 if(*fd
> 256 || *fd
< 0)
98 if((newfd
= fcntl(*fd
, F_DUPFD
, 256)) != -1)
106 #define comm_fd_hack(fd)
110 /* close_all_connections() can be used *before* the system come up! */
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
)
131 /* XXX should his hack be done in all cases? */
133 /* fugly hack to reserve fd == 2 */
135 fd
= open("stderr.log", O_WRONLY
| O_CREAT
| O_APPEND
, 0644);
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
152 comm_get_sockerr(int fd
)
157 socklen_t len
= sizeof(err
);
159 if(-1 < fd
&& !getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, (char *) &err
, (socklen_t
*) & len
))
170 * set_sock_buffers - set send and receive buffers for socket
172 * inputs - fd file descriptor
174 * output - returns true (1) if successful, false (0) otherwise
178 comm_set_buffers(int fd
, int size
)
181 (fd
, SOL_SOCKET
, SO_RCVBUF
, (char *) &size
, sizeof(size
))
182 || setsockopt(fd
, SOL_SOCKET
, SO_SNDBUF
, (char *) &size
, sizeof(size
)))
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
200 if(comm_setup_fd(fd
))
204 res
= fcntl(fd
, F_GETFL
, 0);
205 if(-1 == res
|| fcntl(fd
, F_SETFL
, res
| nonb
) == -1)
208 fd_table
[fd
].flags
.nonblocking
= 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.
220 ignoreErrno(int ierrno
)
226 #if EAGAIN != EWOULDBLOCK
242 * comm_settimeout() - set the socket timeout
244 * Set the timeout for the fd
247 comm_settimeout(int fd
, time_t timeout
, PF
* callback
, void *cbdata
)
250 struct timeout_data
*td
;
253 s_assert(F
->flags
.open
);
256 if(callback
== NULL
) /* user wants to remove */
260 dlinkDelete(&td
->node
, &timeout_list
);
266 if(F
->timeout
== NULL
)
267 td
= F
->timeout
= MyMalloc(sizeof(struct timeout_data
));
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,
284 comm_checktimeouts(void *notused
)
286 dlink_node
*ptr
, *next
;
287 struct timeout_data
*td
;
292 DLINK_FOREACH_SAFE(ptr
, next
, timeout_list
.head
)
296 if(F
== NULL
|| F
->flags
.closing
|| !F
->flags
.open
)
299 if(td
->timeout
< CurrentTime
)
301 hdl
= td
->timeout_handler
;
302 data
= td
->timeout_data
;
303 dlinkDelete(&td
->node
, &timeout_list
);
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
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.
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
)
334 F
->flags
.called_connect
= 1;
336 F
->connect
.callback
= callback
;
337 F
->connect
.data
= data
;
339 memset(&F
->connect
.hostaddr
, 0, sizeof(F
->connect
.hostaddr
));
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
;
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.
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
);
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
);
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
397 comm_connect_callback(int fd
, int status
)
400 fde_t
*F
= &fd_table
[fd
];
401 /* This check is gross..but probably necessary */
402 if(F
->connect
.callback
== NULL
)
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
423 comm_connect_timeout(int fd
, void *notused
)
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()
437 comm_connect_dns_callback(void *vptr
, adns_answer
* reply
)
443 comm_connect_callback(F
->fd
, COMM_ERR_DNS
);
447 if(reply
->status
!= adns_s_ok
)
449 /* Yes, callback + return */
450 comm_connect_callback(F
->fd
, COMM_ERR_DNS
);
452 MyFree(F
->dns_query
);
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..
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
));
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() */
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).
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.
494 comm_connect_tryconnect(int fd
, void *notused
)
497 fde_t
*F
= &fd_table
[fd
];
499 if(F
->connect
.callback
== NULL
)
501 /* Try the connect() */
503 (struct sockaddr
*) &fd_table
[fd
].connect
.hostaddr
,
504 GET_SS_LEN(fd_table
[fd
].connect
.hostaddr
));
509 * If we get EISCONN, then we've already connect()ed the socket,
510 * which is a good thing.
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
);
520 /* Error? Fail with COMM_ERR_CONNECT */
521 comm_connect_callback(F
->fd
, COMM_ERR_CONNECT
);
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
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
)
551 /* First, make sure we aren't going to run out of file descriptors */
552 if(number_fd
>= maxconnections
)
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.
563 fd
= socket(family
, sock_type
, proto
);
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
)
576 if(setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &off
, sizeof(off
)) == -1)
579 "comm_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
580 fd
, strerror(errno
));
587 /* Set the socket non-blocking, and other wonderful bits */
590 ilog(L_IOERROR
, "comm_open: Couldn't set FD %d non blocking: %s", fd
, strerror(errno
));
595 /* Next, update things in our fd tracking */
596 comm_open(fd
, FD_SOCKET
, note
);
602 * comm_accept() - accept an incoming connection
604 * This is a simple wrapper for accept() which enforces FD limits like
608 comm_accept(int fd
, struct sockaddr
*pn
, socklen_t
*addrlen
)
611 if(number_fd
>= maxconnections
)
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
);
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
);
636 /* Next, tag the FD as an incoming connection */
637 comm_open(newfd
, FD_SOCKET
, "Incoming connection");
644 * If a sockaddr_storage is AF_INET6 but is a mapped IPv4
645 * socket manged the sockaddr.
647 #ifndef mangle_mapped_sockaddr
649 mangle_mapped_sockaddr(struct sockaddr
*in
)
651 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)in
;
653 if(in
->sa_family
== AF_INET
)
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
));
671 fdlist_update_biggest(int fd
, int opening
)
676 * s_assert that we are not closing a FD bigger than
677 * our known biggest FD
683 /* if we are here, then fd == Biggest_FD */
685 * s_assert that we are closing the biggest FD; we can't be
689 while (highest_fd
>= 0 && !fd_table
[highest_fd
].flags
.open
)
697 static int initialized
= 0;
701 /* Since we're doing this once .. */
702 fd_table
= MyMalloc((maxconnections
+ 1) * sizeof(fde_t
));
707 /* Called to open a given filedescriptor */
709 comm_open(int fd
, unsigned int type
, const char *desc
)
711 fde_t
*F
= &fd_table
[fd
];
718 s_assert(!F
->flags
.open
);
725 F
->defer
.handler
= NULL
;
727 fdlist_update_biggest(fd
, 1);
728 F
->list
= FDLIST_NONE
;
730 strlcpy(F
->desc
, desc
, sizeof(F
->desc
));
735 /* Called to close a given filedescriptor */
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
);
758 fdlist_update_biggest(fd
, 0);
760 memset(F
, '\0', sizeof(fde_t
));
762 /* Unlike squid, we're actually closing the FD here! -- adrian */
768 * comm_dump() - dump the list of active filedescriptors
771 comm_dump(struct Client
*source_p
)
775 for (i
= 0; i
<= highest_fd
; i
++)
777 if(!fd_table
[i
].flags
.open
)
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
793 comm_note(int fd
, const char *format
, ...)
799 va_start(args
, format
);
800 ircvsnprintf(fd_table
[fd
].desc
, FD_DESC_SZ
, format
, args
);
804 fd_table
[fd
].desc
[0] = '\0';