X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jbsockets.c;h=c1a40809c4dba6f303512925f97dd7e96fe821fe;hp=0693fe49b5b0131c62eadca363cffe816e4b4653;hb=1a723fce2e34538ed80b695803089d1c8b8f456f;hpb=dd711f8cde9b89c23b19a4c5455b9bfe61975091 diff --git a/jbsockets.c b/jbsockets.c index 0693fe49..c1a40809 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -1,4 +1,4 @@ -const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabiankeil Exp $"; +const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.135 2016/01/16 12:33:35 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ @@ -8,14 +8,14 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabian * OS-independent. Contains #ifdefs to make this work * on many platforms. * - * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge + * Copyright : Written by and Copyright (C) 2001-2016 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written - * by and Copyright (C) 1997 Anonymous Coders and + * by and Copyright (C) 1997 Anonymous Coders and * Junkbusters Corporation. http://www.junkbusters.com * - * This program is free software; you can redistribute it + * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or (at @@ -33,232 +33,8 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabian * or write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Revisions : - * $Log: jbsockets.c,v $ - * Revision 1.48 2008/09/04 08:13:58 fabiankeil - * Prepare for critical sections on Windows by adding a - * layer of indirection before the pthread mutex functions. - * - * Revision 1.47 2008/03/26 18:07:07 fabiankeil - * Add hostname directive. Closes PR#1918189. - * - * Revision 1.46 2008/03/21 11:13:57 fabiankeil - * Only gather host information if it's actually needed. - * Also move the code out of accept_connection() so it's less likely - * to delay other incoming connections if the host is misconfigured. - * - * Revision 1.45 2007/09/30 16:59:22 fabiankeil - * Set the maximum listen() backlog to 128. Apparently SOMAXCONN is - * neither high enough, nor a hard limit on mingw32. Again for BR#1795281. - * - * Revision 1.44 2007/09/15 13:01:31 fabiankeil - * Increase listen() backlog to SOMAXCONN (or 128) to decrease - * chances of dropped connections under load. Problem reported - * and fix suggested by nobody in BR#1795281. - * - * Revision 1.43 2007/06/01 18:16:36 fabiankeil - * Use the same mutex for gethostbyname() and gethostbyaddr() to prevent - * deadlocks and crashes on OpenBSD and possibly other OS with neither - * gethostbyname_r() nor gethostaddr_r(). Closes BR#1729174. - * Thanks to Ralf Horstmann for report and solution. - * - * Revision 1.42 2007/04/01 17:37:07 fabiankeil - * - Add DNS retries for Solaris and other systems - * whose gethostbyname_r version takes five arguments. - * - Move maximum number of DNS retries into a macro. - * - * Revision 1.41 2006/11/13 19:05:51 fabiankeil - * Make pthread mutex locking more generic. Instead of - * checking for OSX and OpenBSD, check for FEATURE_PTHREAD - * and use mutex locking unless there is an _r function - * available. Better safe than sorry. - * - * Fixes "./configure --disable-pthread" and should result - * in less threading-related problems on pthread-using platforms, - * but it still doesn't fix BR#1122404. - * - * Revision 1.40 2006/09/02 15:36:42 fabiankeil - * Follow the OpenBSD port's lead and protect the resolve - * functions on OpenBSD as well. - * - * Revision 1.39 2006/08/03 02:46:41 david__schmidt - * Incorporate Fabian Keil's patch work: http://www.fabiankeil.de/sourcecode/privoxy/ - * - * Revision 1.38 2006/07/18 14:48:46 david__schmidt - * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch) - * with what was really the latest development (the v_3_0_branch branch) - * - * Revision 1.35.2.8 2006/01/21 16:16:08 david__schmidt - * Thanks to Edward Carrel for his patch to modernize OSX's pthreads support. See bug #1409623. - * - * Revision 1.35.2.7 2005/05/07 21:50:55 david__schmidt - * A few memory leaks plugged (mostly on error paths) - * - * Revision 1.35.2.6 2003/12/17 16:34:40 oes - * Cosmetics - * - * Revision 1.35.2.5 2003/04/29 11:32:54 oes - * Don't rely on h_addr being non-NULL after gethostbyname. - * Works around an oddness in Max OSX and closes bug #724796 - * - * Revision 1.35.2.4 2003/04/04 12:40:20 oes - * Made sure the errno set by bind, not close[socket] is used in - * bind_port. Probably fixes bugs #713777, #705562. - * - * Revision 1.35.2.3 2003/03/07 03:41:04 david__schmidt - * Wrapping all *_r functions (the non-_r versions of them) with mutex semaphores for OSX. Hopefully this will take care of all of those pesky crash reports. - * - * Revision 1.35.2.2 2002/11/20 14:37:24 oes - * Fixed Win32 error logging in bind_port. - * Thanks to Oliver Stoeneberg for the hint. - * - * Revision 1.35.2.1 2002/05/26 23:41:27 joergs - * AmigaOS: Fixed wrong type of len in write_socket() - * - * Revision 1.35 2002/04/26 15:50:04 joergs - * AmigaOS: No socklen_t, added AMIGA to the systems using int instead. - * - * Revision 1.34 2002/04/08 20:31:41 swa - * fixed JB spelling - * - * Revision 1.33 2002/04/03 16:02:18 gliptak - * Correcting compile warning with older gcc - * - * Revision 1.32 2002/03/31 17:18:59 jongfoster - * Win32 only: Enabling STRICT to fix a VC++ compile warning. - * - * Revision 1.31 2002/03/29 03:33:13 david__schmidt - * Fix Mac OSX compiler warnings - * - * Revision 1.30 2002/03/27 14:32:43 david__schmidt - * More compiler warning message maintenance - * - * Revision 1.29 2002/03/26 22:29:54 swa - * we have a new homepage! - * - * Revision 1.28 2002/03/24 13:25:43 swa - * name change related issues - * - * Revision 1.27 2002/03/13 00:27:05 jongfoster - * Killing warnings - * - * Revision 1.26 2002/03/11 22:07:02 david__schmidt - * OS/2 port maintenance: - * - Fixed EMX build - it had decayed a little - * - Fixed inexplicable crash during FD_ZERO - must be due to a bad macro. - * substituted a memset for now. - * - * Revision 1.25 2002/03/09 20:03:52 jongfoster - * - Making various functions return int rather than size_t. - * (Undoing a recent change). Since size_t is unsigned on - * Windows, functions like read_socket that return -1 on - * error cannot return a size_t. - * - * THIS WAS A MAJOR BUG - it caused frequent, unpredictable - * crashes, and also frequently caused JB to jump to 100% - * CPU and stay there. (Because it thought it had just - * read ((unsigned)-1) == 4Gb of data...) - * - * - The signature of write_socket has changed, it now simply - * returns success=0/failure=nonzero. - * - * - Trying to get rid of a few warnings --with-debug on - * Windows, I've introduced a new type "jb_socket". This is - * used for the socket file descriptors. On Windows, this - * is SOCKET (a typedef for unsigned). Everywhere else, it's - * an int. The error value can't be -1 any more, so it's - * now JB_INVALID_SOCKET (which is -1 on UNIX, and in - * Windows it maps to the #define INVALID_SOCKET.) - * - * - The signature of bind_port has changed. - * - * Revision 1.24 2002/03/07 03:51:36 oes - * - Improved handling of failed DNS lookups - * - Fixed compiler warnings etc - * - * Revision 1.23 2002/03/05 00:36:01 jongfoster - * Fixing bug 514988 - unable to restart Junkbuster - * - * Revision 1.22 2002/03/04 02:08:02 david__schmidt - * Enable web editing of actions file on OS/2 (it had been broken all this time!) - * - * Revision 1.21 2002/01/09 14:32:33 oes - * Added support for gethostbyname_r and gethostbyaddr_r. - * - * Revision 1.20 2001/11/16 00:48:48 jongfoster - * Enabling duplicate-socket detection for all platforms, not - * just Win32. - * - * Revision 1.19 2001/10/25 03:40:47 david__schmidt - * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple - * threads to call select() simultaneously. So, it's time to do a real, live, - * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ - * (native). Both versions will work, but using __OS2__ offers multi-threading. - * - * Revision 1.18 2001/09/21 23:02:02 david__schmidt - * Cleaning up 2 compiler warnings on OS/2. - * - * Revision 1.17 2001/09/13 20:11:46 jongfoster - * Fixing 2 compiler warnings under Win32 - * - * Revision 1.16 2001/07/30 22:08:36 jongfoster - * Tidying up #defines: - * - All feature #defines are now of the form FEATURE_xxx - * - Permanently turned off WIN_GUI_EDIT - * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS - * - * Revision 1.15 2001/07/29 17:40:43 jongfoster - * Fixed compiler warning by adding a cast - * - * Revision 1.14 2001/07/18 13:47:59 oes - * Eliminated dirty hack for getsockbyname() - * - * Revision 1.13 2001/07/15 13:56:57 jongfoster - * Removing unused local variable. - * - * Revision 1.12 2001/07/01 17:04:11 oes - * Bugfix: accept_connection no longer uses the obsolete hstrerror() function - * - * Revision 1.11 2001/06/29 21:45:41 oes - * Indentation, CRLF->LF, Tab-> Space - * - * Revision 1.10 2001/06/29 13:29:15 oes - * - Added remote (server) host IP to csp->http->host_ip_addr_str - * - Added detection of local socket IP and fqdn - * - Removed logentry from cancelled commit - * - * Revision 1.9 2001/06/07 23:06:09 jongfoster - * The host parameter to connect_to() is now const. - * - * Revision 1.8 2001/06/03 19:12:07 oes - * filled comment - * - * Revision 1.7 2001/05/28 16:14:00 jongfoster - * Fixing bug in LOG_LEVEL_LOG - * - * Revision 1.6 2001/05/26 17:28:32 jongfoster - * Fixed LOG_LEVEL_LOG - * - * Revision 1.5 2001/05/26 15:26:15 jongfoster - * ACL feature now provides more security by immediately dropping - * connections from untrusted hosts. - * - * Revision 1.4 2001/05/26 00:37:42 jongfoster - * Cosmetic indentation correction. - * - * Revision 1.3 2001/05/25 21:57:54 jongfoster - * Now gives a warning under Windows if you try to bind - * it to a port that's already in use. - * - * Revision 1.2 2001/05/17 23:01:01 oes - * - Cleaned CRLF's from the sources and related files - * - * Revision 1.1.1.1 2001/05/15 13:58:54 oes - * Initial import of version 2.9.3 source tree - * - * *********************************************************************/ - + #include "config.h" @@ -307,16 +83,28 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabian #endif +#ifdef HAVE_POLL +#ifdef __GLIBC__ +#include +#else +#include +#endif /* def __GLIBC__ */ +#endif /* HAVE_POLL */ + #include "project.h" -#ifdef FEATURE_PTHREAD +/* For mutex semaphores only */ #include "jcc.h" -/* jcc.h is for mutex semaphores only */ -#endif /* def FEATURE_PTHREAD */ #include "jbsockets.h" #include "filters.h" #include "errlog.h" +#include "miscutil.h" + +/* Mac OSX doesn't define AI_NUMERICSESRV */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; @@ -329,6 +117,38 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; #define MAX_LISTEN_BACKLOG 128 +#ifdef HAVE_RFC2553 +static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client_state *csp); +#else +static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp); +#endif + +/********************************************************************* + * + * Function : set_no_delay_flag + * + * Description : Disables TCP coalescence for the given socket. + * + * Parameters : + * 1 : fd = The file descriptor to operate on + * + * Returns : void + * + *********************************************************************/ +static void set_no_delay_flag(int fd) +{ +#ifdef TCP_NODELAY + int mi = 1; + + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof(int))) + { + log_error(LOG_LEVEL_ERROR, + "Failed to disable TCP coalescence for socket %d", fd); + } +#else +#warning set_no_delay_flag() is a nop due to lack of TCP_NODELAY +#endif /* def TCP_NODELAY */ +} /********************************************************************* * @@ -339,29 +159,289 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; * * Parameters : * 1 : host = hostname to connect to - * 2 : portnum = port to connent on (XXX: should be unsigned) + * 2 : portnum = port to connect to (XXX: should be unsigned) * 3 : csp = Current client state (buffers, headers, etc...) - * Not modified, only used for source IP and ACL. * * Returns : JB_INVALID_SOCKET => failure, else it is the socket * file descriptor. * *********************************************************************/ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) +{ + jb_socket fd; + int forwarded_connect_retries = 0; + + do + { + /* + * XXX: The whole errno overloading is ridiculous and should + * be replaced with something sane and thread safe + */ + /* errno = 0;*/ +#ifdef HAVE_RFC2553 + fd = rfc2553_connect_to(host, portnum, csp); +#else + fd = no_rfc2553_connect_to(host, portnum, csp); +#endif + if ((fd != JB_INVALID_SOCKET) || (errno == EINVAL) + || (csp->fwd == NULL) + || ((csp->fwd->forward_host == NULL) && (csp->fwd->type == SOCKS_NONE))) + { + break; + } + forwarded_connect_retries++; + if (csp->config->forwarded_connect_retries != 0) + { + log_error(LOG_LEVEL_ERROR, + "Attempt %d of %d to connect to %s failed. Trying again.", + forwarded_connect_retries, csp->config->forwarded_connect_retries + 1, host); + } + + } while (forwarded_connect_retries < csp->config->forwarded_connect_retries); + + return fd; +} + +#ifdef HAVE_RFC2553 +/* Getaddrinfo implementation */ +static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client_state *csp) +{ + struct addrinfo hints, *result, *rp; + char service[6]; + int retval; + jb_socket fd; + fd_set wfds; + struct timeval timeout; +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + int flags; +#endif + int connect_failed; + /* + * XXX: Initializeing it here is only necessary + * because not all situations are properly + * covered yet. + */ + int socket_error = 0; + +#ifdef FEATURE_ACL + struct access_control_addr dst[1]; +#endif /* def FEATURE_ACL */ + + /* Don't leak memory when retrying. */ + freez(csp->error_message); + freez(csp->http->host_ip_addr_str); + + retval = snprintf(service, sizeof(service), "%d", portnum); + if ((-1 == retval) || (sizeof(service) <= retval)) + { + log_error(LOG_LEVEL_ERROR, + "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes", + portnum); + csp->error_message = strdup("Invalid port number"); + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + + memset((char *)&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; /* avoid service look-up */ +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif + if ((retval = getaddrinfo(host, service, &hints, &result))) + { + log_error(LOG_LEVEL_INFO, + "Can not resolve %s: %s", host, gai_strerror(retval)); + /* XXX: Should find a better way to propagate this error. */ + errno = EINVAL; + csp->error_message = strdup(gai_strerror(retval)); + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + + csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST); + + for (rp = result; rp != NULL; rp = rp->ai_next) + { + +#ifdef FEATURE_ACL + memcpy(&dst->addr, rp->ai_addr, rp->ai_addrlen); + + if (block_acl(dst, csp)) + { +#ifdef __OS2__ + socket_error = errno = SOCEPERM; +#else + socket_error = errno = EPERM; +#endif + continue; + } +#endif /* def FEATURE_ACL */ + + retval = getnameinfo(rp->ai_addr, rp->ai_addrlen, + csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (retval) + { + log_error(LOG_LEVEL_ERROR, + "Failed to get the host name from the socket structure: %s", + gai_strerror(retval)); + continue; + } + + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#ifdef _WIN32 + if (fd == JB_INVALID_SOCKET) +#else + if (fd < 0) +#endif + { + continue; + } + +#ifndef _WIN32 + if (fd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Server socket number too high to use select(): %d >= %d", + fd, FD_SETSIZE); + close_socket(fd); + freeaddrinfo(result); + return JB_INVALID_SOCKET; + } +#endif + +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + + set_no_delay_flag(fd); + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if ((flags = fcntl(fd, F_GETFL, 0)) != -1) + { + flags |= O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + connect_failed = 0; + while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET) + { +#ifdef __OS2__ + errno = sock_errno(); +#endif /* __OS2__ */ + +#ifdef _WIN32 + if (errno == WSAEINPROGRESS) +#else /* ifndef _WIN32 */ + if (errno == EINPROGRESS) +#endif /* ndef _WIN32 || __OS2__ */ + { + break; + } + + if (errno != EINTR) + { + socket_error = errno; + close_socket(fd); + connect_failed = 1; + break; + } + } + if (connect_failed) + { + continue; + } + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if (flags != -1) + { + flags &= ~O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + /* wait for connection to complete */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = 30; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */ + if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0) + && FD_ISSET(fd, &wfds)) + { + socklen_t optlen = sizeof(socket_error); + if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen)) + { + if (!socket_error) + { + /* Connection established, no need to try other addresses. */ + break; + } + if (rp->ai_next != NULL) + { + /* + * There's another address we can try, so log that this + * one didn't work out. If the last one fails, too, + * it will get logged outside the loop body so we don't + * have to mention it here. + */ + log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.", + csp->http->host_ip_addr_str, service, strerror(socket_error)); + } + } + else + { + socket_error = errno; + log_error(LOG_LEVEL_ERROR, "Could not get the state of " + "the connection to [%s]:%s: %s; dropping connection.", + csp->http->host_ip_addr_str, service, strerror(errno)); + } + } + + /* Connection failed, try next address */ + close_socket(fd); + } + + freeaddrinfo(result); + if (!rp) + { + log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.", + host, service, strerror(socket_error)); + csp->error_message = strdup(strerror(socket_error)); + return(JB_INVALID_SOCKET); + } + log_error(LOG_LEVEL_CONNECT, "Connected to %s[%s]:%s.", + host, csp->http->host_ip_addr_str, service); + + return(fd); + +} + +#else /* ndef HAVE_RFC2553 */ +/* Pre-getaddrinfo implementation */ + +static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp) { struct sockaddr_in inaddr; jb_socket fd; unsigned int addr; fd_set wfds; struct timeval tv[1]; -#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) int flags; -#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ +#endif #ifdef FEATURE_ACL struct access_control_addr dst[1]; #endif /* def FEATURE_ACL */ + /* Don't leak memory when retrying. */ + freez(csp->http->host_ip_addr_str); + memset((char *)&inaddr, 0, sizeof inaddr); if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE) @@ -402,27 +482,37 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) } #endif /* ndef _WIN32 */ + fd = socket(inaddr.sin_family, SOCK_STREAM, 0); #ifdef _WIN32 - if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET) + if (fd == JB_INVALID_SOCKET) #else - if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) < 0) + if (fd < 0) #endif { return(JB_INVALID_SOCKET); } -#ifdef TCP_NODELAY - { /* turn off TCP coalescence */ - int mi = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); +#ifndef _WIN32 + if (fd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Server socket number too high to use select(): %d >= %d", + fd, FD_SETSIZE); + close_socket(fd); + return JB_INVALID_SOCKET; } -#endif /* def TCP_NODELAY */ +#endif + + set_no_delay_flag(fd); #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ @@ -430,7 +520,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { #ifdef _WIN32 if (errno == WSAEINPROGRESS) -#elif __OS2__ +#elif __OS2__ if (sock_errno() == EINPROGRESS) #else /* ifndef _WIN32 */ if (errno == EINPROGRESS) @@ -439,7 +529,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) break; } -#ifdef __OS2__ +#ifdef __OS2__ if (sock_errno() != EINTR) #else if (errno != EINTR) @@ -465,7 +555,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) tv->tv_sec = 30; tv->tv_usec = 0; - /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */ if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) { close_socket(fd); @@ -474,6 +564,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) return(fd); } +#endif /* ndef HAVE_RFC2553 */ /********************************************************************* @@ -502,12 +593,7 @@ int write_socket(jb_socket fd, const char *buf, size_t len) return 0; } - if (len < 0) /* constant condition - size_t isn't ever negative */ - { - return 1; - } - - log_error(LOG_LEVEL_LOG, "%N", len, buf); + log_error(LOG_LEVEL_WRITING, "to socket %d: %N", fd, len, buf); #if defined(_WIN32) return (send(fd, buf, (int)len, 0) != (int)len); @@ -520,7 +606,7 @@ int write_socket(jb_socket fd, const char *buf, size_t len) */ #define SOCKET_SEND_MAX 65000 { - int write_len = 0, send_len, send_rc = 0, i = 0; + int send_len, send_rc = 0, i = 0; while ((i < len) && (send_rc != -1)) { if ((i + SOCKET_SEND_MAX) > len) @@ -568,18 +654,68 @@ int write_socket(jb_socket fd, const char *buf, size_t len) *********************************************************************/ int read_socket(jb_socket fd, char *buf, int len) { + int ret; + if (len <= 0) { return(0); } #if defined(_WIN32) - return(recv(fd, buf, len, 0)); + ret = recv(fd, buf, len, 0); #elif defined(__BEOS__) || defined(AMIGA) || defined(__OS2__) - return(recv(fd, buf, (size_t)len, 0)); + ret = recv(fd, buf, (size_t)len, 0); #else - return(read(fd, buf, (size_t)len)); + ret = (int)read(fd, buf, (size_t)len); #endif + + if (ret > 0) + { + log_error(LOG_LEVEL_RECEIVED, "from socket %d: %N", fd, ret, buf); + } + + return ret; +} + + +/********************************************************************* + * + * Function : data_is_available + * + * Description : Waits for data to arrive on a socket. + * + * Parameters : + * 1 : fd = file descriptor of the socket to read + * 2 : seconds_to_wait = number of seconds after which we give up. + * + * Returns : TRUE if data arrived in time, + * FALSE otherwise. + * + *********************************************************************/ +int data_is_available(jb_socket fd, int seconds_to_wait) +{ + char buf[10]; + fd_set rfds; + struct timeval timeout; + int n; + + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = seconds_to_wait; + +#ifdef __OS2__ + /* Copy and pasted from jcc.c ... */ + memset(&rfds, 0, sizeof(fd_set)); +#else + FD_ZERO(&rfds); +#endif + FD_SET(fd, &rfds); + + n = select(fd+1, &rfds, NULL, NULL, &timeout); + + /* + * XXX: Do we care about the different error conditions? + */ + return ((n == 1) && (1 == recv(fd, buf, 1, MSG_PEEK))); } @@ -600,12 +736,83 @@ void close_socket(jb_socket fd) #if defined(_WIN32) || defined(__BEOS__) closesocket(fd); #elif defined(AMIGA) - CloseSocket(fd); + CloseSocket(fd); #elif defined(__OS2__) soclose(fd); #else close(fd); #endif +} + + +/********************************************************************* + * + * Function : drain_and_close_socket + * + * Description : Closes a TCP/IP socket after draining unread data + * + * Parameters : + * 1 : fd = file descriptor of the socket to be closed + * + * Returns : void + * + *********************************************************************/ +void drain_and_close_socket(jb_socket fd) +{ +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (socket_is_still_alive(fd)) +#endif + { + int bytes_drained_total = 0; + int bytes_drained; + +#ifdef HAVE_SHUTDOWN +/* Apparently Windows has shutdown() but not SHUT_WR. */ +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + if (0 != shutdown(fd, SHUT_WR)) + { + log_error(LOG_LEVEL_CONNECT, "Failed to shutdown socket %d: %E", fd); + } +#endif +#define ARBITRARY_DRAIN_LIMIT 10000 + do + { + char drainage[500]; + + if (!data_is_available(fd, 0)) + { + /* + * If there is no data available right now, don't try + * to drain the socket as read_socket() could block. + */ + break; + } + + bytes_drained = read_socket(fd, drainage, sizeof(drainage)); + if (bytes_drained < 0) + { + log_error(LOG_LEVEL_CONNECT, "Failed to drain socket %d: %E", fd); + } + else if (bytes_drained > 0) + { + bytes_drained_total += bytes_drained; + if (bytes_drained_total > ARBITRARY_DRAIN_LIMIT) + { + log_error(LOG_LEVEL_CONNECT, "Giving up draining socket %d", fd); + break; + } + } + } while (bytes_drained > 0); + if (bytes_drained_total != 0) + { + log_error(LOG_LEVEL_CONNECT, + "Drained %d bytes before closing socket %d", bytes_drained_total, fd); + } + } + + close_socket(fd); } @@ -629,7 +836,19 @@ void close_socket(jb_socket fd) *********************************************************************/ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { +#ifdef HAVE_RFC2553 + struct addrinfo hints; + struct addrinfo *result, *rp; + /* + * XXX: portnum should be a string to allow symbolic service + * names in the configuration file and to avoid the following + * int2string. + */ + char servnam[6]; + int retval; +#else struct sockaddr_in inaddr; +#endif /* def HAVE_RFC2553 */ jb_socket fd; #ifndef _WIN32 int one = 1; @@ -637,6 +856,45 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) *pfd = JB_INVALID_SOCKET; +#ifdef HAVE_RFC2553 + retval = snprintf(servnam, sizeof(servnam), "%d", portnum); + if ((-1 == retval) || (sizeof(servnam) <= retval)) + { + log_error(LOG_LEVEL_ERROR, + "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes", + portnum); + return -1; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + if (hostnam == NULL) + { + /* + * XXX: This is a hack. The right thing to do + * would be to bind to both AF_INET and AF_INET6. + * This will also fail if there is no AF_INET + * version available. + */ + hints.ai_family = AF_INET; + } + else + { + hints.ai_family = AF_UNSPEC; + } + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; /* Really any stream protocol or TCP only */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if ((retval = getaddrinfo(hostnam, servnam, &hints, &result))) + { + log_error(LOG_LEVEL_ERROR, + "Can not resolve %s: %s", hostnam, gai_strerror(retval)); + return -2; + } +#else memset((char *)&inaddr, '\0', sizeof inaddr); inaddr.sin_family = AF_INET; @@ -659,8 +917,15 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) inaddr.sin_port = htonl((unsigned long) portnum); } #endif /* ndef _WIN32 */ +#endif /* def HAVE_RFC2553 */ +#ifdef HAVE_RFC2553 + for (rp = result; rp != NULL; rp = rp->ai_next) + { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#else fd = socket(AF_INET, SOCK_STREAM, 0); +#endif /* def HAVE_RFC2553 */ #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) @@ -668,9 +933,17 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (fd < 0) #endif { +#ifdef HAVE_RFC2553 + continue; +#else return(-1); +#endif } +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + #ifndef _WIN32 /* * This is not needed for Win32 - in fact, it stops @@ -687,7 +960,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); #endif /* ndef _WIN32 */ +#ifdef HAVE_RFC2553 + if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0) +#else if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) +#endif { #ifdef _WIN32 errno = WSAGetLastError(); @@ -696,20 +973,47 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (errno == EADDRINUSE) #endif { +#ifdef HAVE_RFC2553 + freeaddrinfo(result); +#endif close_socket(fd); return(-3); } else { close_socket(fd); +#ifndef HAVE_RFC2553 return(-1); } } +#else + } + } + else + { + /* bind() succeeded, escape from for-loop */ + /* + * XXX: Support multiple listening sockets (e.g. localhost + * resolves to AF_INET and AF_INET6, but only the first address + * is used + */ + break; + } + } + + freeaddrinfo(result); + if (rp == NULL) + { + /* All bind()s failed */ + return(-1); + } +#endif /* ndef HAVE_RFC2553 */ while (listen(fd, MAX_LISTEN_BACKLOG) == -1) { if (errno != EINTR) { + close_socket(fd); return(-1); } } @@ -735,23 +1039,32 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) * 1 : afd = File descriptor returned from accept(). * 2 : ip_address = Pointer to return the pointer to * the ip address string. - * 3 : hostname = Pointer to return the pointer to + * 3 : port = Pointer to return the pointer to + * the TCP port string. + * 4 : hostname = Pointer to return the pointer to * the hostname or NULL if the caller * isn't interested in it. * * Returns : void. * *********************************************************************/ -void get_host_information(jb_socket afd, char **ip_address, char **hostname) +void get_host_information(jb_socket afd, char **ip_address, char **port, + char **hostname) { +#ifdef HAVE_RFC2553 + struct sockaddr_storage server; + int retval; +#else struct sockaddr_in server; struct hostent *host = NULL; -#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) +#endif /* HAVE_RFC2553 */ +#if defined(_WIN32) || defined(__OS2__) || defined(AMIGA) /* according to accept_connection() this fixes a warning. */ - int s_length; + int s_length, s_length_provided; #else - socklen_t s_length; + socklen_t s_length, s_length_provided; #endif +#ifndef HAVE_RFC2553 #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) || defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS) struct hostent result; #if defined(HAVE_GETHOSTBYADDR_R_5_ARGS) @@ -761,18 +1074,52 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) int thd_err; #endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */ #endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */ - s_length = sizeof(server); +#endif /* ifndef HAVE_RFC2553 */ + s_length = s_length_provided = sizeof(server); if (NULL != hostname) { *hostname = NULL; } *ip_address = NULL; + *port = NULL; if (!getsockname(afd, (struct sockaddr *) &server, &s_length)) { + if (s_length > s_length_provided) + { + log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address"); + return; + } +/* + * XXX: Workaround for missing header on Windows when + * configured with --disable-ipv6-support. + * The proper fix is to not use NI_MAXSERV in + * that case. It works by accident on other platforms + * as is included unconditionally there. + */ +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + *port = malloc_or_die(NI_MAXSERV); + +#ifdef HAVE_RFC2553 + *ip_address = malloc_or_die(NI_MAXHOST); + retval = getnameinfo((struct sockaddr *) &server, s_length, + *ip_address, NI_MAXHOST, *port, NI_MAXSERV, + NI_NUMERICHOST|NI_NUMERICSERV); + if (retval) + { + log_error(LOG_LEVEL_ERROR, + "Unable to print my own IP address: %s", gai_strerror(retval)); + freez(*ip_address); + freez(*port); + return; + } +#else *ip_address = strdup(inet_ntoa(server.sin_addr)); - + snprintf(*port, NI_MAXSERV, "%hu", ntohs(server.sin_port)); +#endif /* HAVE_RFC2553 */ if (NULL == hostname) { /* @@ -781,6 +1128,18 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) */ return; } + +#ifdef HAVE_RFC2553 + *hostname = malloc_or_die(NI_MAXHOST); + retval = getnameinfo((struct sockaddr *) &server, s_length, + *hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD); + if (retval) + { + log_error(LOG_LEVEL_ERROR, + "Unable to resolve my own IP address: %s", gai_strerror(retval)); + freez(*hostname); + } +#else #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) gethostbyaddr_r((const char *)&server.sin_addr, sizeof(server.sin_addr), AF_INET, @@ -801,13 +1160,13 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { host = NULL; } -#elif FEATURE_PTHREAD +#elif defined(MUTEX_LOCKS_AVAILABLE) privoxy_mutex_lock(&resolver_mutex); - host = gethostbyaddr((const char *)&server.sin_addr, + host = gethostbyaddr((const char *)&server.sin_addr, sizeof(server.sin_addr), AF_INET); privoxy_mutex_unlock(&resolver_mutex); #else - host = gethostbyaddr((const char *)&server.sin_addr, + host = gethostbyaddr((const char *)&server.sin_addr, sizeof(server.sin_addr), AF_INET); #endif if (host == NULL) @@ -818,6 +1177,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { *hostname = strdup(host->h_name); } +#endif /* else def HAVE_RFC2553 */ } return; @@ -828,31 +1188,98 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) * * Function : accept_connection * - * Description : Accepts a connection on a socket. Socket must have - * been created using bind_port(). + * Description : Accepts a connection on one of possibly multiple + * sockets. The socket(s) to check must have been + * created using bind_port(). * * Parameters : - * 1 : csp = Client state, cfd, ip_addr_str, and - * ip_addr_long will be set by this routine. - * 2 : fd = file descriptor returned from bind_port + * 1 : csp = Client state, cfd, ip_addr_str, and + * ip_addr_long will be set by this routine. + * 2 : fds = File descriptors returned from bind_port * * Returns : when a connection is accepted, it returns 1 (TRUE). * On an error it returns 0 (FALSE). * *********************************************************************/ -int accept_connection(struct client_state * csp, jb_socket fd) +int accept_connection(struct client_state * csp, jb_socket fds[]) { +#ifdef HAVE_RFC2553 + /* XXX: client is stored directly into csp->tcp_addr */ +#define client (csp->tcp_addr) +#else struct sockaddr_in client; +#endif jb_socket afd; -#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) +#if defined(_WIN32) || defined(__OS2__) || defined(AMIGA) /* Wierdness - fix a warning. */ int c_length; #else socklen_t c_length; #endif + int retval; + int i; + int max_selected_socket; + fd_set selected_fds; + jb_socket fd; + size_t listen_addr_size; c_length = sizeof(client); + /* + * Wait for a connection on any socket. + * Return immediately if no socket is listening. + * XXX: Why not treat this as fatal error? + */ + FD_ZERO(&selected_fds); + max_selected_socket = 0; + for (i = 0; i < MAX_LISTENING_SOCKETS; i++) + { + if (JB_INVALID_SOCKET != fds[i]) + { + FD_SET(fds[i], &selected_fds); + if (max_selected_socket < fds[i] + 1) + { + max_selected_socket = fds[i] + 1; + } + } + } + if (0 == max_selected_socket) + { + return 0; + } + do + { + retval = select(max_selected_socket, &selected_fds, NULL, NULL, NULL); + } while (retval < 0 && errno == EINTR); + if (retval <= 0) + { + if (0 == retval) + { + log_error(LOG_LEVEL_ERROR, + "Waiting on new client failed because select(2) returned 0." + " This should not happen."); + } + else + { + log_error(LOG_LEVEL_ERROR, + "Waiting on new client failed because of problems in select(2): " + "%s.", strerror(errno)); + } + return 0; + } + for (i = 0; i < MAX_LISTENING_SOCKETS && !FD_ISSET(fds[i], &selected_fds); + i++); + if (i >= MAX_LISTENING_SOCKETS) + { + log_error(LOG_LEVEL_ERROR, + "select(2) reported connected clients (number = %u, " + "descriptor boundary = %u), but none found.", + retval, max_selected_socket); + return 0; + } + fd = fds[i]; + + /* Accept selected connection */ #ifdef _WIN32 afd = accept (fd, (struct sockaddr *) &client, &c_length); if (afd == JB_INVALID_SOCKET) @@ -862,17 +1289,84 @@ int accept_connection(struct client_state * csp, jb_socket fd) #else do { +#if defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER) + struct accept_filter_arg af_options; + bzero(&af_options, sizeof(af_options)); + strlcpy(af_options.af_name, "httpready", sizeof(af_options.af_name)); + setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &af_options, sizeof(af_options)); +#endif afd = accept (fd, (struct sockaddr *) &client, &c_length); - } while (afd < 1 && errno == EINTR); + } while (afd < 0 && errno == EINTR); if (afd < 0) { return 0; } #endif +#ifdef SO_LINGER + { + struct linger linger_options; + linger_options.l_onoff = 1; + linger_options.l_linger = 5; + if (0 != setsockopt(afd, SOL_SOCKET, SO_LINGER, &linger_options, sizeof(linger_options))) + { + log_error(LOG_LEVEL_ERROR, "Setting SO_LINGER on socket %d failed.", afd); + } + } +#endif + +#ifndef _WIN32 + if (afd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Client socket number too high to use select(): %d >= %d", + afd, FD_SETSIZE); + close_socket(afd); + return 0; + } +#endif + +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(afd); +#endif + + set_no_delay_flag(afd); + csp->cfd = afd; +#ifdef HAVE_RFC2553 + csp->ip_addr_str = malloc_or_die(NI_MAXHOST); + retval = getnameinfo((struct sockaddr *) &client, c_length, + csp->ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (!csp->ip_addr_str || retval) + { + log_error(LOG_LEVEL_ERROR, "Can not save csp->ip_addr_str: %s", + (csp->ip_addr_str) ? gai_strerror(retval) : "Insuffcient memory"); + freez(csp->ip_addr_str); + } +#undef client +#else csp->ip_addr_str = strdup(inet_ntoa(client.sin_addr)); csp->ip_addr_long = ntohl(client.sin_addr.s_addr); +#endif /* def HAVE_RFC2553 */ + + /* + * Save the name and port of the accepting socket for later lookup. + * + * The string needs space for strlen(...) + 7 characters: + * strlen(haddr[i]) + 1 (':') + 5 (port digits) + 1 ('\0') + */ + listen_addr_size = strlen(csp->config->haddr[i]) + 7; + csp->listen_addr_str = malloc_or_die(listen_addr_size); + retval = snprintf(csp->listen_addr_str, listen_addr_size, + "%s:%d", csp->config->haddr[i], csp->config->hport[i]); + if ((-1 == retval) || listen_addr_size <= retval) + { + log_error(LOG_LEVEL_ERROR, + "Server name (%s) and port number (%d) ASCII decimal representation" + "don't fit into %d bytes", + csp->config->haddr[i], csp->config->hport[i], listen_addr_size); + return 0; + } return 1; @@ -889,14 +1383,13 @@ int accept_connection(struct client_state * csp, jb_socket fd) * Parameters : * 1 : host = hostname to resolve * - * Returns : INADDR_NONE => failure, INADDR_ANY or tcp/ip address if succesful. + * Returns : INADDR_NONE => failure, INADDR_ANY or tcp/ip address if successful. * *********************************************************************/ unsigned long resolve_hostname_to_ip(const char *host) { struct sockaddr_in inaddr; struct hostent *hostp; - unsigned int dns_retries = 0; #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS) struct hostent result; #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) @@ -916,11 +1409,12 @@ unsigned long resolve_hostname_to_ip(const char *host) if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1) { + unsigned int dns_retries = 0; #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) while (gethostbyname_r(host, &result, hbuf, HOSTENT_BUFFER_SIZE, &hostp, &thd_err) && (thd_err == TRY_AGAIN) && (dns_retries++ < MAX_DNS_RETRIES)) - { + { log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.", dns_retries, host); @@ -929,7 +1423,7 @@ unsigned long resolve_hostname_to_ip(const char *host) while (NULL == (hostp = gethostbyname_r(host, &result, hbuf, HOSTENT_BUFFER_SIZE, &thd_err)) && (thd_err == TRY_AGAIN) && (dns_retries++ < MAX_DNS_RETRIES)) - { + { log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.", dns_retries, host); @@ -947,11 +1441,11 @@ unsigned long resolve_hostname_to_ip(const char *host) { hostp = NULL; } -#elif FEATURE_PTHREAD +#elif defined(MUTEX_LOCKS_AVAILABLE) privoxy_mutex_lock(&resolver_mutex); while (NULL == (hostp = gethostbyname(host)) && (h_errno == TRY_AGAIN) && (dns_retries++ < MAX_DNS_RETRIES)) - { + { log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.", dns_retries, host); @@ -984,21 +1478,108 @@ unsigned long resolve_hostname_to_ip(const char *host) errno = WSAEPROTOTYPE; #else errno = EPROTOTYPE; -#endif +#endif log_error(LOG_LEVEL_ERROR, "hostname %s resolves to unknown address type.", host); return(INADDR_NONE); } - memcpy( - (char *) &inaddr.sin_addr, - (char *) hostp->h_addr, - sizeof(inaddr.sin_addr) - ); + memcpy((char *)&inaddr.sin_addr, (char *)hostp->h_addr, sizeof(inaddr.sin_addr)); } return(inaddr.sin_addr.s_addr); } +/********************************************************************* + * + * Function : socket_is_still_alive + * + * Description : Figures out whether or not a socket is still alive. + * + * Parameters : + * 1 : sfd = The socket to check. + * + * Returns : TRUE for yes, otherwise FALSE. + * + *********************************************************************/ +int socket_is_still_alive(jb_socket sfd) +{ + char buf[10]; + int no_data_waiting; + +#ifdef HAVE_POLL + int poll_result; + struct pollfd poll_fd[1]; + + memset(poll_fd, 0, sizeof(poll_fd)); + poll_fd[0].fd = sfd; + poll_fd[0].events = POLLIN; + + poll_result = poll(poll_fd, 1, 0); + + if (-1 == poll_result) + { + log_error(LOG_LEVEL_CONNECT, "Polling socket %d failed.", sfd); + return FALSE; + } + no_data_waiting = !(poll_fd[0].revents & POLLIN); +#else + fd_set readable_fds; + struct timeval timeout; + int ret; + + memset(&timeout, '\0', sizeof(timeout)); + FD_ZERO(&readable_fds); + FD_SET(sfd, &readable_fds); + + ret = select((int)sfd+1, &readable_fds, NULL, NULL, &timeout); + if (ret < 0) + { + log_error(LOG_LEVEL_CONNECT, "select() on socket %d failed: %E", sfd); + return FALSE; + } + no_data_waiting = !FD_ISSET(sfd, &readable_fds); +#endif /* def HAVE_POLL */ + + return (no_data_waiting || (1 == recv(sfd, buf, 1, MSG_PEEK))); +} + + +#ifdef FEATURE_EXTERNAL_FILTERS +/********************************************************************* + * + * Function : mark_socket_for_close_on_execute + * + * Description : Marks a socket for close on execute. + * + * Used so that external filters have no direct + * access to sockets they shouldn't care about. + * + * Not implemented for all platforms. + * + * Parameters : + * 1 : fd = The socket to mark + * + * Returns : void. + * + *********************************************************************/ +void mark_socket_for_close_on_execute(jb_socket fd) +{ +#ifdef FEATURE_PTHREAD + int ret; + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (ret == -1) + { + log_error(LOG_LEVEL_ERROR, + "fcntl(%d, F_SETFD, FD_CLOEXEC) failed", fd); + } +#else +#warning "Sockets will be visible to external filters" +#endif +} +#endif /* def FEATURE_EXTERNAL_FILTERS */ + /* Local Variables: tab-width: 3