X-Git-Url: http://www.privoxy.org/gitweb/misc.html?a=blobdiff_plain;f=jbsockets.c;h=187fbc4ddbf940079063d156a556cc81fdb111a9;hb=f67b3326138f428863c21c7738e0c8db87fa6f5c;hp=6ff9b3c4e201164234d9a0fad8d75f2b1e7dc6c7;hpb=333512b6d15f4b6ccd2c990eb71569f3b3e66880;p=privoxy.git
diff --git a/jbsockets.c b/jbsockets.c
index 6ff9b3c4..187fbc4d 100644
--- a/jbsockets.c
+++ b/jbsockets.c
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabiankeil Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.138 2016/09/27 22:48:28 ler762 Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabia
* OS-independent. Contains #ifdefs to make this work
* on many platforms.
*
- * Copyright : Written by and Copyright (C) 2001-2011 the
+ * Copyright : Written by and Copyright (C) 2001-2016 the
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
@@ -50,6 +50,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabia
#ifndef STRICT
#define STRICT
#endif
+#include
#include
#include
#include
@@ -83,7 +84,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabia
#endif
-#ifdef FEATURE_CONNECTION_KEEP_ALIVE
#ifdef HAVE_POLL
#ifdef __GLIBC__
#include
@@ -91,7 +91,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabia
#include
#endif /* def __GLIBC__ */
#endif /* HAVE_POLL */
-#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
#include "project.h"
@@ -101,6 +100,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.110 2011/11/18 23:53:26 fabia
#include "jbsockets.h"
#include "filters.h"
#include "errlog.h"
+#include "miscutil.h"
/* Mac OSX doesn't define AI_NUMERICSESRV */
#ifndef AI_NUMERICSERV
@@ -124,6 +124,33 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
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 */
+}
+
/*********************************************************************
*
* Function : connect_to
@@ -133,7 +160,7 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli
*
* 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...)
*
* Returns : JB_INVALID_SOCKET => failure, else it is the socket
@@ -234,14 +261,7 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
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;
- }
+ csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST);
for (rp = result; rp != NULL; rp = rp->ai_next)
{
@@ -280,12 +300,23 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
continue;
}
-#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);
+ freeaddrinfo(result);
+ return JB_INVALID_SOCKET;
}
-#endif /* def TCP_NODELAY */
+#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)
@@ -462,18 +493,27 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli
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__) */
@@ -554,6 +594,14 @@ int write_socket(jb_socket fd, const char *buf, size_t len)
return 0;
}
+#ifdef FUZZ
+ if (!daemon_mode && fd <= 3)
+ {
+ log_error(LOG_LEVEL_WRITING, "Pretending to write to socket %d: %N", fd, len, buf);
+ return 0;
+ }
+#endif
+
log_error(LOG_LEVEL_WRITING, "to socket %d: %N", fd, len, buf);
#if defined(_WIN32)
@@ -703,6 +751,77 @@ void close_socket(jb_socket 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);
}
@@ -830,6 +949,10 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
#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
@@ -899,6 +1022,7 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
{
if (errno != EINTR)
{
+ close_socket(fd);
return(-1);
}
}
@@ -943,7 +1067,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **port,
struct sockaddr_in server;
struct hostent *host = NULL;
#endif /* HAVE_RFC2553 */
-#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
+#if defined(_WIN32) || defined(__OS2__) || defined(AMIGA)
/* according to accept_connection() this fixes a warning. */
int s_length, s_length_provided;
#else
@@ -976,22 +1100,20 @@ void get_host_information(jb_socket afd, char **ip_address, char **port,
log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address");
return;
}
- *port = malloc(NI_MAXSERV);
- if (NULL == *port)
- {
- log_error(LOG_LEVEL_ERROR,
- "Out of memory while getting the client's port.");
- 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(NI_MAXHOST);
- if (NULL == *ip_address)
- {
- log_error(LOG_LEVEL_ERROR,
- "Out of memory while getting the client's IP address.");
- freez(*port);
- return;
- }
+ *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);
@@ -1017,13 +1139,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **port,
}
#ifdef HAVE_RFC2553
- *hostname = malloc(NI_MAXHOST);
- if (NULL == *hostname)
- {
- log_error(LOG_LEVEL_ERROR,
- "Out of memory while getting the client's hostname.");
- return;
- }
+ *hostname = malloc_or_die(NI_MAXHOST);
retval = getnameinfo((struct sockaddr *) &server, s_length,
*hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
if (retval)
@@ -1103,7 +1219,7 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
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
@@ -1114,6 +1230,8 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
int max_selected_socket;
fd_set selected_fds;
jb_socket fd;
+ const char *host_addr;
+ size_t listen_addr_size;
c_length = sizeof(client);
@@ -1188,22 +1306,45 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
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
- csp->cfd = afd;
-#ifdef HAVE_RFC2553
- csp->ip_addr_str = malloc(NI_MAXHOST);
- if (NULL == csp->ip_addr_str)
+#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,
- "Out of memory while getting the client's IP address.");
+ "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)
@@ -1218,6 +1359,26 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
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')
+ */
+ host_addr = (csp->config->haddr[i] != NULL) ? csp->config->haddr[i] : "";
+ listen_addr_size = strlen(host_addr) + 7;
+ csp->listen_addr_str = malloc_or_die(listen_addr_size);
+ retval = snprintf(csp->listen_addr_str, listen_addr_size,
+ "%s:%d", host_addr, 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",
+ host_addr, csp->config->hport[i], listen_addr_size);
+ return 0;
+ }
+
return 1;
}
@@ -1332,18 +1493,13 @@ unsigned long resolve_hostname_to_ip(const char *host)
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);
}
-#ifdef FEATURE_CONNECTION_KEEP_ALIVE
/*********************************************************************
*
* Function : socket_is_still_alive
@@ -1397,9 +1553,44 @@ int socket_is_still_alive(jb_socket sfd)
return (no_data_waiting || (1 == recv(sfd, buf, 1, MSG_PEEK)));
}
-#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+#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