X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jbsockets.c;h=5691988d71473c3a973c03682f7db1b026ee291a;hp=15772f05122fba7fb9c7b2c4c04cd1f1da9db0e8;hb=ae286185f6afeafd979ae30bbdae9f4afe378bd3;hpb=48c3349cf8ef334a30f9ceb535f64b0ee140a4e6 diff --git a/jbsockets.c b/jbsockets.c index 15772f05..5691988d 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -1,4 +1,4 @@ -const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.66 2009/09/06 15:22:31 fabiankeil Exp $"; +const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.101 2011/05/03 09:55:35 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ @@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.66 2009/09/06 15:22:31 fabian * OS-independent. Contains #ifdefs to make this work * on many platforms. * - * Copyright : Written by and Copyright (C) 2001-2009 the + * Copyright : Written by and Copyright (C) 2001-2011 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -101,7 +101,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.66 2009/09/06 15:22:31 fabian #include "jbsockets.h" #include "filters.h" #include "errlog.h" -#include "miscutil.h" /* Mac OSX doesn't define AI_NUMERICSESRV */ #ifndef AI_NUMERICSERV @@ -119,6 +118,11 @@ 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 /********************************************************************* * @@ -131,37 +135,83 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; * 1 : host = hostname to connect to * 2 : portnum = port to connent on (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, host); + } + + } while (forwarded_connect_retries < csp->config->forwarded_connect_retries); + + return fd; +} + #ifdef HAVE_RFC2553 /* Getaddrinfo implementation */ -jb_socket connect_to(const char *host, int portnum, struct client_state *csp) +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 tv[1]; -#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) + struct timeval timeout; +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) int flags; -#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ +#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); } @@ -169,15 +219,30 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) memset((char *)&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; /* avoid service look-up */ + 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(NI_MAXHOST); + if (NULL == csp->http->host_ip_addr_str) + { + freeaddrinfo(result); + log_error(LOG_LEVEL_ERROR, + "Out of memory while getting the server IP address."); + return JB_INVALID_SOCKET; + } + for (rp = result; rp != NULL; rp = rp->ai_next) { @@ -187,38 +252,29 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) if (block_acl(dst, csp)) { #ifdef __OS2__ - errno = SOCEPERM; + socket_error = errno = SOCEPERM; #else - errno = EPERM; + socket_error = errno = EPERM; #endif continue; } #endif /* def FEATURE_ACL */ - csp->http->host_ip_addr_str = malloc(NI_MAXHOST); - if (NULL == csp->http->host_ip_addr_str) - { - log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the server IP address."); - return JB_INVALID_SOCKET; - } retval = getnameinfo(rp->ai_addr, rp->ai_addrlen, csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (!csp->http->host_ip_addr_str || retval) + if (retval) { log_error(LOG_LEVEL_ERROR, - "Can not save csp->http->host_ip_addr_str: %s", - (csp->http->host_ip_addr_str) ? - gai_strerror(retval) : "Insufficient memory"); - freez(csp->http->host_ip_addr_str); + "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 = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == - JB_INVALID_SOCKET) + if (fd == JB_INVALID_SOCKET) #else - if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) + if (fd < 0) #endif { continue; @@ -242,10 +298,12 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) 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) -#elif __OS2__ - if (sock_errno() == EINPROGRESS) #else /* ifndef _WIN32 */ if (errno == EINPROGRESS) #endif /* ndef _WIN32 || __OS2__ */ @@ -253,11 +311,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) break; } -#ifdef __OS2__ - if (sock_errno() != EINTR) -#else if (errno != EINTR) -#endif /* __OS2__ */ { close_socket(fd); connect_failed = 1; @@ -281,31 +335,36 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) FD_ZERO(&wfds); FD_SET(fd, &wfds); - tv->tv_sec = 30; - tv->tv_usec = 0; + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = 30; - /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ - if ((select((int)fd + 1, NULL, &wfds, NULL, tv) > 0) + /* 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)) { - /* - * See Linux connect(2) man page for more info - * about connecting on non-blocking socket. - */ - int socket_in_error; - socklen_t optlen = sizeof(socket_in_error); - if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_in_error, &optlen)) + socklen_t optlen = sizeof(socket_error); + if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen)) { - if (!socket_in_error) + if (!socket_error) { /* Connection established, no need to try other addresses. */ break; } - log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.", - csp->http->host_ip_addr_str, service, strerror(socket_in_error)); + 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)); @@ -319,8 +378,9 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) freeaddrinfo(result); if (!rp) { - log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s.", - host, service); + 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.", @@ -333,21 +393,24 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) #else /* ndef HAVE_RFC2553 */ /* Pre-getaddrinfo implementation */ -jb_socket connect_to(const char *host, int portnum, struct client_state *csp) +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) @@ -388,10 +451,11 @@ 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); @@ -451,7 +515,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); @@ -494,7 +558,7 @@ int write_socket(jb_socket fd, const char *buf, size_t len) 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); @@ -507,7 +571,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) @@ -555,18 +619,27 @@ 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((int)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; } @@ -688,7 +761,7 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) } memset(&hints, 0, sizeof(struct addrinfo)); - if ((hostnam == NULL) || !strcmpic(hostnam, "localhost")) + if (hostnam == NULL) { /* * XXX: This is a hack. The right thing to do @@ -703,8 +776,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) hints.ai_family = AF_UNSPEC; } hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - hints.ai_protocol = 0; /* Realy any stream protocol or TCP only */ + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif + hints.ai_protocol = 0; /* Really any stream protocol or TCP only */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; @@ -1037,6 +1113,12 @@ 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); if (afd < 0) @@ -1083,14 +1165,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) @@ -1110,6 +1191,7 @@ 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) @@ -1196,9 +1278,9 @@ unsigned long resolve_hostname_to_ip(const char *host) #ifdef FEATURE_CONNECTION_KEEP_ALIVE /********************************************************************* * - * Function : socket_is_still_usable + * Function : socket_is_still_alive * - * Description : Decides whether or not an open socket is still usable. + * Description : Figures out whether or not a socket is still alive. * * Parameters : * 1 : sfd = The socket to check. @@ -1206,7 +1288,7 @@ unsigned long resolve_hostname_to_ip(const char *host) * Returns : TRUE for yes, otherwise FALSE. * *********************************************************************/ -int socket_is_still_usable(jb_socket sfd) +int socket_is_still_alive(jb_socket sfd) { char buf[10]; int no_data_waiting;