X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jbsockets.c;h=990c8e42c35eead086622113bf84a9e5ec7faa8b;hp=0693fe49b5b0131c62eadca363cffe816e4b4653;hb=2ef78e00152922cdb1c30f0d9ad0dfe31317b6f5;hpb=dd711f8cde9b89c23b19a4c5455b9bfe61975091 diff --git a/jbsockets.c b/jbsockets.c index 0693fe49..990c8e42 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.53 2009/04/17 11:39:52 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ @@ -35,6 +35,23 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabian * * Revisions : * $Log: jbsockets.c,v $ + * Revision 1.53 2009/04/17 11:39:52 fabiankeil + * If the hostname is 'localhost' or not specified, request an AF_INET address. + * + * Revision 1.52 2009/04/17 11:34:34 fabiankeil + * Style cosmetics for the IPv6 code. + * + * Revision 1.51 2009/04/17 11:27:49 fabiankeil + * Petr Pisar's privoxy-3.0.12-ipv6-3.diff. + * + * Revision 1.50 2008/12/20 14:53:55 fabiankeil + * Add config option socket-timeout to control the time + * Privoxy waits for data to arrive on a socket. Useful + * in case of stale ssh tunnels or when fuzz-testing. + * + * Revision 1.49 2008/11/10 17:03:57 fabiankeil + * Fix a gcc44 warning and remove a now-obsolete cast. + * * 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. @@ -317,6 +334,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.48 2008/09/04 08:13:58 fabian #include "jbsockets.h" #include "filters.h" #include "errlog.h" +#include "miscutil.h" const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; @@ -347,6 +365,181 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; * file descriptor. * *********************************************************************/ +#ifdef HAVE_RFC2553 +/* Getaddrinfo implementation */ +jb_socket 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) + int flags; +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ + int connect_failed; + +#ifdef FEATURE_ACL + struct access_control_addr dst[1]; +#endif /* def FEATURE_ACL */ + + 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->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_ADDRCONFIG | AI_NUMERICSERV; /* avoid service look-up */ + if ((retval = getaddrinfo(host, service, &hints, &result))) + { + log_error(LOG_LEVEL_INFO, + "Can not resolve %s: %s", host, gai_strerror(retval)); + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + + 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__ + errno = SOCEPERM; +#else + errno = EPERM; +#endif + continue; + } +#endif /* def FEATURE_ACL */ + + csp->http->host_ip_addr_str = malloc(NI_MAXHOST); + 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) + { + 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); + continue; + } + +#ifdef _WIN32 + if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == + JB_INVALID_SOCKET) +#else + if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) +#endif + { + continue; + } + +#ifdef TCP_NODELAY + { /* turn off TCP coalescence */ + int mi = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); + } +#endif /* def TCP_NODELAY */ + +#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 _WIN32 + if (errno == WSAEINPROGRESS) +#elif __OS2__ + if (sock_errno() == EINPROGRESS) +#else /* ifndef _WIN32 */ + if (errno == EINPROGRESS) +#endif /* ndef _WIN32 || __OS2__ */ + { + break; + } + +#ifdef __OS2__ + if (sock_errno() != EINTR) +#else + if (errno != EINTR) +#endif /* __OS2__ */ + { + 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); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ + if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) + { + close_socket(fd); + continue; + } + + break; /* for; Connection established; don't try other addresses */ + } + + freeaddrinfo(result); + if (!rp) + { + log_error(LOG_LEVEL_INFO, + "Could not connect to TCP/[%s]:%s", host, service); + return(JB_INVALID_SOCKET); + } + /* + * XXX: Current connection verification (EINPROGRESS && select() + * for writing) is not sufficient. E.g. on Linux-2.6.27 with glibc-2.6 + * select returns socket ready for writing, however subsequential + * write(2) fails with ENOCONNECT. Read Linux connect(2) man page + * about non-blocking sockets. + * Thus we can't log here that the socket is connected. + */ + /* log_error(LOG_LEVEL_INFO, "Connected to TCP/[%s]:%s", host, service); */ + + return(fd); + +} + +#else /* ndef HAVE_RFC2553 */ +/* Pre-getaddrinfo implementation */ + jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { struct sockaddr_in inaddr; @@ -474,6 +667,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) return(fd); } +#endif /* ndef HAVE_RFC2553 */ /********************************************************************* @@ -583,6 +777,46 @@ int read_socket(jb_socket fd, char *buf, int len) } +/********************************************************************* + * + * 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) +{ + 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); +} + + /********************************************************************* * * Function : close_socket @@ -629,7 +863,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 +883,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) || !strcmpic(hostnam, "localhost")) + { + /* + * 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 | AI_ADDRCONFIG; + hints.ai_protocol = 0; /* Realy 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 +944,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,7 +960,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (fd < 0) #endif { +#ifdef HAVE_RFC2553 + continue; +#else return(-1); +#endif } #ifndef _WIN32 @@ -687,7 +983,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,15 +996,41 @@ 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) { @@ -744,14 +1070,20 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) *********************************************************************/ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { +#ifdef HAVE_RFC2553 + struct sockaddr_storage server; + int retval; +#else struct sockaddr_in server; struct hostent *host = NULL; +#endif /* HAVE_RFC2553 */ #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || 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,7 +1093,8 @@ 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) { @@ -771,8 +1104,25 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) if (!getsockname(afd, (struct sockaddr *) &server, &s_length)) { + if (s_length > s_length_provided) + { + log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address"); + return; + } +#ifdef HAVE_RFC2553 + *ip_address = malloc(NI_MAXHOST); + retval = getnameinfo((struct sockaddr *) &server, s_length, + *ip_address, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (retval) + { + log_error(LOG_LEVEL_ERROR, + "Unable to print my own IP address: %s", gai_strerror(retval)); + freez(*ip_address); + return; + } +#else *ip_address = strdup(inet_ntoa(server.sin_addr)); - +#endif /* HAVE_RFC2553 */ if (NULL == hostname) { /* @@ -781,6 +1131,18 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) */ return; } + +#ifdef HAVE_RFC2553 + *hostname = malloc(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, @@ -818,6 +1180,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { *hostname = strdup(host->h_name); } +#endif /* else def HAVE_RFC2553 */ } return; @@ -842,7 +1205,13 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) *********************************************************************/ int accept_connection(struct client_state * csp, jb_socket fd) { +#ifdef HAVE_RFC2553 + /* XXX: client is stored directly into csp->tcp_addr */ +#define client (csp->tcp_addr) + int retval; +#else struct sockaddr_in client; +#endif jb_socket afd; #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) /* Wierdness - fix a warning. */ @@ -871,8 +1240,21 @@ int accept_connection(struct client_state * csp, jb_socket fd) #endif csp->cfd = afd; +#ifdef HAVE_RFC2553 + csp->ip_addr_str = malloc(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 */ return 1;