diff --git a/VERSION b/VERSION index 81c871d..1cac385 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.10.0 +1.11.0 diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 1c2aea5..f03af9c 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -190,37 +190,11 @@ The possible keywords and their descriptions are as follows: *MaxClients*:: - Tinyproxy creates one child process for each connected client. + Tinyproxy creates one thread for each connected client. This options specifies the absolute highest number processes that will be created. With other words, only MaxClients clients can be connected to Tinyproxy simultaneously. -*MinSpareServers*:: -*MaxSpareServers*:: - - Tinyproxy always keeps a certain number of idle child processes - so that it can handle new incoming client requests quickly. - `MinSpareServer` and `MaxSpareServers` control the lower and upper - limits for the number of spare processes. I.e. when the number of - spare servers drops below `MinSpareServers` then Tinyproxy will - start forking new spare processes in the background and when the - number of spare processes exceeds `MaxSpareServers` then Tinyproxy - will kill off extra processes. - -*StartServers*:: - - The number of servers to start initially. This should usually be - set to a value between MinSpareServers and MaxSpareServers. - -*MaxRequestsPerChild*:: - - This limits the number of connections that a child process - will handle before it is killed. The default value is `0` - which disables this feature. This option is meant as an - emergency measure in the case of problems with memory leakage. - In that case, setting `MaxRequestsPerChild` to a value of e.g. - 1000, or 10000 can be useful. - *Allow*:: *Deny*:: @@ -236,6 +210,9 @@ The possible keywords and their descriptions are as follows: end of the client host name, i.e, this can be a full host name like `host.example.com` or a domain name like `.example.com` or even a top level domain name like `.com`. + Note that by adding a rule using a host or domain name, a costly name + lookup has to be done for every new connection, which could slow down + the service considerably. *AddHeader*:: diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index f1b8817..06f5480 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -189,30 +189,6 @@ LogLevel Info # MaxClients 100 -# -# MinSpareServers/MaxSpareServers: These settings set the upper and -# lower limit for the number of spare servers which should be available. -# -# If the number of spare servers falls below MinSpareServers then new -# server processes will be spawned. If the number of servers exceeds -# MaxSpareServers then the extras will be killed off. -# -MinSpareServers 5 -MaxSpareServers 20 - -# -# StartServers: The number of servers to start initially. -# -StartServers 10 - -# -# MaxRequestsPerChild: The number of connections a thread will handle -# before it is killed. In practise this should be set to 0, which -# disables thread reaping. If you do notice problems with memory -# leakage, then set this to something like 10000. -# -MaxRequestsPerChild 0 - # # Allow: Customization of authorization controls. If there are any # access control keywords then the default action is to DENY. Otherwise, diff --git a/src/Makefile.am b/src/Makefile.am index af2f621..50e645b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,10 +48,12 @@ tinyproxy_SOURCES = \ upstream.c upstream.h \ basicauth.c basicauth.h \ base64.c base64.h \ + sblist.c sblist.h \ + loop.c loop.h \ connect-ports.c connect-ports.h EXTRA_tinyproxy_SOURCES = filter.c filter.h \ reverse-proxy.c reverse-proxy.h \ transparent-proxy.c transparent-proxy.h tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ -tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ +tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread diff --git a/src/acl.c b/src/acl.c index 13700df..7918a07 100644 --- a/src/acl.c +++ b/src/acl.c @@ -111,7 +111,7 @@ fill_netmask_array (char *bitmask_string, int v6, /** * If the access list has not been set up, create it. */ -static int init_access_list (vector_t *access_list) +static int init_access_list(vector_t *access_list) { if (!*access_list) { *access_list = vector_create (); @@ -143,7 +143,7 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) assert (location != NULL); - ret = init_access_list (access_list); + ret = init_access_list(access_list); if (ret != 0) { return -1; } @@ -170,7 +170,7 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) */ p = strchr (location, '/'); if (p != NULL) { - char dst[sizeof (struct in6_addr)]; + char dst[sizeof(struct in6_addr)]; int v6; /* @@ -185,7 +185,7 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) /* Check if the IP address before the netmask is * an IPv6 address */ - if (inet_pton (AF_INET6, location, dst) > 0) + if (inet_pton(AF_INET6, location, dst) > 0) v6 = 1; else v6 = 0; @@ -197,7 +197,7 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) for (i = 0; i < IPV6_LEN; i++) acl.address.ip.network[i] = ip_dst[i] & - acl.address.ip.mask[i]; + acl.address.ip.mask[i]; } else { /* In all likelihood a string */ acl.type = ACL_STRING; @@ -221,8 +221,8 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) * -1 if no tests match, so skip */ static int -acl_string_processing (struct acl_s *acl, - const char *ip_address, const char *string_address) +acl_string_processing (struct acl_s *acl, const char *ip_address, + union sockaddr_union *addr, char *string_addr) { int match; struct addrinfo hints, *res, *ressave; @@ -231,7 +231,6 @@ acl_string_processing (struct acl_s *acl, assert (acl && acl->type == ACL_STRING); assert (ip_address && strlen (ip_address) > 0); - assert (string_address && strlen (string_address) > 0); /* * If the first character of the ACL string is a period, we need to @@ -267,7 +266,15 @@ acl_string_processing (struct acl_s *acl, } STRING_TEST: - test_length = strlen (string_address); + if(string_addr[0] == 0) { + /* only do costly hostname resolution when it is absolutely needed, + and only once */ + if(getnameinfo ((void *) addr, sizeof (*addr), + string_addr, HOSTNAME_LENGTH, NULL, 0, 0) != 0) + return -1; + } + + test_length = strlen (string_addr); match_length = strlen (acl->address.string); /* @@ -278,7 +285,7 @@ STRING_TEST: return -1; if (strcasecmp - (string_address + (test_length - match_length), + (string_addr + (test_length - match_length), acl->address.string) == 0) { if (acl->access == ACL_DENY) return 0; @@ -329,15 +336,18 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip) * 1 if allowed * 0 if denied */ -int check_acl (const char *ip, const char *host, vector_t access_list) +int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) { struct acl_s *acl; int perm = 0; size_t i; + char string_addr[HOSTNAME_LENGTH]; assert (ip != NULL); assert (host != NULL); + string_addr[0] = 0; + /* * If there is no access list allow everything. */ @@ -348,7 +358,7 @@ int check_acl (const char *ip, const char *host, vector_t access_list) acl = (struct acl_s *) vector_getentry (access_list, i, NULL); switch (acl->type) { case ACL_STRING: - perm = acl_string_processing (acl, ip, host); + perm = acl_string_processing (acl, ip, addr, string_addr); break; case ACL_NUMERIC: @@ -371,8 +381,8 @@ int check_acl (const char *ip, const char *host, vector_t access_list) /* * Deny all connections by default. */ - log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].", - host, ip); + log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".", + ip); return 0; } diff --git a/src/acl.h b/src/acl.h index 2d11cef..ba0aebe 100644 --- a/src/acl.h +++ b/src/acl.h @@ -22,12 +22,13 @@ #define TINYPROXY_ACL_H #include "vector.h" +#include "sock.h" typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t; extern int insert_acl (char *location, acl_access_t access_type, vector_t *access_list); -extern int check_acl (const char *ip_address, const char *string_address, +extern int check_acl (const char *ip_address, union sockaddr_union *addr, vector_t access_list); extern void flush_access_list (vector_t access_list); diff --git a/src/base64.c b/src/base64.c index 1f0f643..cc9b6ae 100644 --- a/src/base64.c +++ b/src/base64.c @@ -19,7 +19,7 @@ #include "base64.h" static const char base64_tbl[64] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* rofl0r's base64 impl (taken from libulz) @@ -27,30 +27,31 @@ static const char base64_tbl[64] = dst needs to be at least BASE64ENC_BYTES(count) + 1 bytes in size. the string in dst will be zero-terminated. */ -void base64enc (char *dst, const void *src, size_t count) +void base64enc(char *dst, const void* src, size_t count) { - unsigned const char *s = src; - char *d = dst; - while (count) { - int i = 0, n = *s << 16; - s++; - count--; - if (count) { - n |= *s << 8; - s++; - count--; - i++; - } - if (count) { - n |= *s; - s++; - count--; - i++; - } - *d++ = base64_tbl[(n >> 18) & 0x3f]; - *d++ = base64_tbl[(n >> 12) & 0x3f]; - *d++ = i ? base64_tbl[(n >> 6) & 0x3f] : '='; - *d++ = i == 2 ? base64_tbl[n & 0x3f] : '='; - } - *d = 0; + unsigned const char *s = src; + char* d = dst; + while(count) { + int i = 0, n = *s << 16; + s++; + count--; + if(count) { + n |= *s << 8; + s++; + count--; + i++; + } + if(count) { + n |= *s; + s++; + count--; + i++; + } + *d++ = base64_tbl[(n >> 18) & 0x3f]; + *d++ = base64_tbl[(n >> 12) & 0x3f]; + *d++ = i ? base64_tbl[(n >> 6) & 0x3f] : '='; + *d++ = i == 2 ? base64_tbl[n & 0x3f] : '='; + } + *d = 0; } + diff --git a/src/basicauth.c b/src/basicauth.c index ac81ff8..d6c2420 100644 --- a/src/basicauth.c +++ b/src/basicauth.c @@ -32,31 +32,29 @@ * -1 if user/pass missing * 0 if user/pass too long */ -ssize_t basicauth_string (const char *user, const char *pass, - char *buf, size_t bufsize) +ssize_t basicauth_string(const char *user, const char *pass, + char *buf, size_t bufsize) { - char tmp[256 + 2]; - int l; - if (!user || !pass) - return -1; - l = snprintf (tmp, sizeof tmp, "%s:%s", user, pass); - if (l < 0 || l >= (ssize_t) sizeof tmp) - return 0; - if (bufsize < (BASE64ENC_BYTES ((unsigned) l) + 1)) - return 0; - base64enc (buf, tmp, l); - return BASE64ENC_BYTES (l); + char tmp[256+2]; + int l; + if (!user || !pass) return -1; + l = snprintf(tmp, sizeof tmp, "%s:%s", user, pass); + if (l < 0 || l >= (ssize_t) sizeof tmp) return 0; + if (bufsize < (BASE64ENC_BYTES((unsigned)l) + 1)) return 0; + base64enc(buf, tmp, l); + return BASE64ENC_BYTES(l); } /* * Add entry to the basicauth list */ -void basicauth_add (vector_t authlist, const char *user, const char *pass) +void basicauth_add (vector_t authlist, + const char *user, const char *pass) { - char b[BASE64ENC_BYTES ((256 + 2) - 1) + 1]; - ssize_t ret; + char b[BASE64ENC_BYTES((256+2)-1) + 1]; + ssize_t ret; - ret = basicauth_string (user, pass, b, sizeof b); + ret = basicauth_string(user, pass, b, sizeof b); if (ret == -1) { log_message (LOG_WARNING, "Illegal basicauth rule: missing user or pass"); @@ -67,13 +65,14 @@ void basicauth_add (vector_t authlist, const char *user, const char *pass) return; } - if (vector_append (authlist, b, ret + 1) == -ENOMEM) { + if (vector_append(authlist, b, ret + 1) == -ENOMEM) { log_message (LOG_ERR, "Unable to allocate memory in basicauth_add()"); return; } - log_message (LOG_INFO, "Added basic auth user : %s", user); + log_message (LOG_INFO, + "Added basic auth user : %s", user); } /* @@ -85,16 +84,15 @@ int basicauth_check (vector_t authlist, const char *authstring) { ssize_t vl, i; size_t el; - const char *entry; + const char* entry; vl = vector_length (authlist); - if (vl == -EINVAL) - return 0; + if (vl == -EINVAL) return 0; for (i = 0; i < vl; i++) { entry = vector_getentry (authlist, i, &el); if (strcmp (authstring, entry) == 0) return 1; } - return 0; + return 0; } diff --git a/src/buffer.c b/src/buffer.c index b2c88c2..b338183 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -244,9 +244,9 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr) #ifdef EWOULDBLOCK case EWOULDBLOCK: #else -#ifdef EAGAIN +# ifdef EAGAIN case EAGAIN: -#endif +# endif #endif case EINTR: bytesin = 0; @@ -254,7 +254,7 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr) default: log_message (LOG_ERR, "read_buffer: read() failed on fd %d: %s", - fd, strerror (errno)); + fd, strerror(errno)); bytesin = -1; break; } @@ -298,9 +298,9 @@ ssize_t write_buffer (int fd, struct buffer_s * buffptr) #ifdef EWOULDBLOCK case EWOULDBLOCK: #else -#ifdef EAGAIN +# ifdef EAGAIN case EAGAIN: -#endif +# endif #endif case EINTR: return 0; diff --git a/src/child.c b/src/child.c index c241593..861606b 100644 --- a/src/child.c +++ b/src/child.c @@ -31,176 +31,64 @@ #include "sock.h" #include "utils.h" #include "conf.h" +#include "sblist.h" +#include "loop.h" +#include static vector_t listen_fds; -/* - * Stores the internal data needed for each child (connection) - */ -enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED }; -struct child_s { - pid_t tid; - unsigned int connects; - enum child_status_t status; +struct client { + union sockaddr_union addr; + int fd; }; -/* - * A pointer to an array of children. A certain number of children are - * created when the program is started. - */ -static struct child_s *child_ptr; +struct child { + pthread_t thread; + struct client client; + volatile int done; +}; -static struct child_config_s { - unsigned int maxclients, maxrequestsperchild; - unsigned int maxspareservers, minspareservers, startservers; -} child_config; - -static unsigned int *servers_waiting; /* servers waiting for a connection */ - -/* - * Lock/Unlock the "servers_waiting" variable so that two children cannot - * modify it at the same time. - */ -#define SERVER_COUNT_LOCK() _child_lock_wait() -#define SERVER_COUNT_UNLOCK() _child_lock_release() - -/* START OF LOCKING SECTION */ - -/* - * These variables are required for the locking mechanism. Also included - * are the "private" functions for locking/unlocking. - */ -static struct flock lock_it, unlock_it; -static int lock_fd = -1; - -static void _child_lock_init (void) +static void* child_thread(void* data) { - char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX"; - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - lock_fd = mkstemp (lock_file); - unlink (lock_file); - - lock_it.l_type = F_WRLCK; - lock_it.l_whence = SEEK_SET; - lock_it.l_start = 0; - lock_it.l_len = 0; - - unlock_it.l_type = F_UNLCK; - unlock_it.l_whence = SEEK_SET; - unlock_it.l_start = 0; - unlock_it.l_len = 0; + struct child *c = data; + handle_connection (c->client.fd, &c->client.addr); + c->done = 1; + return NULL; } -static void _child_lock_wait (void) +static sblist *childs; + +static void collect_threads(void) { - int rc; - - while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) { - if (errno == EINTR) - continue; - else - return; - } -} - -static void _child_lock_release (void) -{ - if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) - return; -} - -/* END OF LOCKING SECTION */ - -#define SERVER_INC() do { \ - SERVER_COUNT_LOCK(); \ - ++(*servers_waiting); \ - DEBUG2("INC: servers_waiting: %d", *servers_waiting); \ - SERVER_COUNT_UNLOCK(); \ -} while (0) - -#define SERVER_DEC() do { \ - SERVER_COUNT_LOCK(); \ - assert(*servers_waiting > 0); \ - --(*servers_waiting); \ - DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \ - SERVER_COUNT_UNLOCK(); \ -} while (0) - -/* - * Set the configuration values for the various child related settings. - */ -short int child_configure (child_config_t type, unsigned int val) -{ - switch (type) { - case CHILD_MAXCLIENTS: - child_config.maxclients = val; - break; - case CHILD_MAXSPARESERVERS: - child_config.maxspareservers = val; - break; - case CHILD_MINSPARESERVERS: - child_config.minspareservers = val; - break; - case CHILD_STARTSERVERS: - child_config.startservers = val; - break; - case CHILD_MAXREQUESTSPERCHILD: - child_config.maxrequestsperchild = val; - break; - default: - DEBUG2 ("Invalid type (%d)", type); - return -1; - } - - return 0; -} - -/** - * child signal handler for sighup - */ -static void child_sighup_handler (int sig) -{ - if (sig == SIGHUP) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); - -#ifdef FILTER_ENABLE - filter_reload (); -#endif /* FILTER_ENABLE */ - } + size_t i; + for (i = 0; i < sblist_getsize(childs); ) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (c->done) { + pthread_join(c->thread, 0); + sblist_delete(childs, i); + safefree(c); + } else i++; + } } /* - * This is the main (per child) loop. + * This is the main loop accepting new connections. */ -static void child_main (struct child_s *ptr) +void child_main_loop (void) { int connfd; - struct sockaddr *cliaddr; - socklen_t clilen; + union sockaddr_union cliaddr_storage; + struct sockaddr *cliaddr = (void*) &cliaddr_storage; + socklen_t clilen = sizeof(cliaddr_storage); fd_set rfds; - int maxfd = 0; ssize_t i; - int ret; + int ret, listenfd, maxfd, was_full = 0; + pthread_attr_t *attrp, attr; + struct child *child; - cliaddr = (struct sockaddr *) - safemalloc (sizeof (struct sockaddr_storage)); - if (!cliaddr) { - log_message (LOG_CRIT, - "Could not allocate memory for child address."); - exit (0); - } + childs = sblist_new(sizeof (struct child*), config.maxclients); - ptr->connects = 0; - srand (time (NULL)); + loop_records_init(); /* * We have to wait for connections on multiple fds, @@ -208,304 +96,21 @@ static void child_main (struct child_s *ptr) */ while (!config.quit) { - int listenfd = -1; + collect_threads(); - FD_ZERO (&rfds); - - for (i = 0; i < vector_length (listen_fds); i++) { - int *fd = (int *) vector_getentry (listen_fds, i, NULL); - - ret = socket_nonblocking (*fd); - if (ret != 0) { - log_message (LOG_ERR, - "Failed to set the listening " - "socket %d to non-blocking: %s", - fd, strerror (errno)); - exit (1); - } - - FD_SET (*fd, &rfds); - maxfd = max (maxfd, *fd); - } - - ptr->status = T_WAITING; - - clilen = sizeof (struct sockaddr_storage); - - ret = select (maxfd + 1, &rfds, NULL, NULL, NULL); - if (ret == -1) { - if (errno == EINTR) { - continue; - } - log_message (LOG_ERR, "error calling select: %s", - strerror (errno)); - exit (1); - } else if (ret == 0) { - log_message (LOG_WARNING, "Strange: select returned 0 " - "but we did not specify a timeout..."); - continue; - } - - for (i = 0; i < vector_length (listen_fds); i++) { - int *fd = (int *) vector_getentry (listen_fds, i, NULL); - - if (FD_ISSET (*fd, &rfds)) { - /* - * only accept the connection on the first - * fd that we find readable. - fair? - */ - listenfd = *fd; - break; - } - } - - if (listenfd == -1) { - log_message (LOG_WARNING, "Strange: None of our listen " - "fds was readable after select"); - continue; - } - - ret = socket_blocking (listenfd); - if (ret != 0) { - log_message (LOG_ERR, "Failed to set listening " - "socket %d to blocking for accept: %s", - listenfd, strerror (errno)); - exit (1); - } - - /* - * We have a socket that is readable. - * Continue handling this connection. - */ - - connfd = accept (listenfd, cliaddr, &clilen); - -#ifndef NDEBUG - /* - * Enable the TINYPROXY_DEBUG environment variable if you - * want to use the GDB debugger. - */ - if (getenv ("TINYPROXY_DEBUG")) { - /* Pause for 10 seconds to allow us to connect debugger */ - fprintf (stderr, - "Process has accepted connection: %ld\n", - (long int) ptr->tid); - sleep (10); - fprintf (stderr, "Continuing process: %ld\n", - (long int) ptr->tid); - } -#endif - - /* - * Make sure no error occurred... - */ - if (connfd < 0) { - log_message (LOG_ERR, - "Accept returned an error (%s) ... retrying.", - strerror (errno)); - continue; - } - - ptr->status = T_CONNECTED; - - SERVER_DEC (); - - handle_connection (connfd); - ptr->connects++; - - if (child_config.maxrequestsperchild != 0) { - DEBUG2 ("%u connections so far...", ptr->connects); - - if (ptr->connects == child_config.maxrequestsperchild) { + if (sblist_getsize(childs) >= config.maxclients) { + if (!was_full) log_message (LOG_NOTICE, - "Child has reached MaxRequestsPerChild (%u). " - "Killing child.", ptr->connects); - break; - } + "Maximum number of connections reached. " + "Refusing new connections."); + was_full = 1; + usleep(16); + continue; } - SERVER_COUNT_LOCK (); - if (*servers_waiting > child_config.maxspareservers) { - /* - * There are too many spare children, kill ourself - * off. - */ - log_message (LOG_NOTICE, - "Waiting servers (%d) exceeds MaxSpareServers (%d). " - "Killing child.", - *servers_waiting, - child_config.maxspareservers); - SERVER_COUNT_UNLOCK (); - - break; - } else { - SERVER_COUNT_UNLOCK (); - } - - SERVER_INC (); - } - - ptr->status = T_EMPTY; - - safefree (cliaddr); - exit (0); -} - -/* - * Fork a child "child" (or in our case a process) and then start up the - * child_main() function. - */ -static pid_t child_make (struct child_s *ptr) -{ - pid_t pid; - - if ((pid = fork ()) > 0) - return pid; /* parent */ - - /* - * Reset the SIGNALS so that the child can be reaped. - */ - set_signal_handler (SIGCHLD, SIG_DFL); - set_signal_handler (SIGTERM, SIG_DFL); - set_signal_handler (SIGHUP, child_sighup_handler); - - child_main (ptr); /* never returns */ - return -1; -} - -/* - * Create a pool of children to handle incoming connections - */ -short int child_pool_create (void) -{ - unsigned int i; - - /* - * Make sure the number of MaxClients is not zero, since this - * variable determines the size of the array created for children - * later on. - */ - if (child_config.maxclients == 0) { - log_message (LOG_ERR, - "child_pool_create: \"MaxClients\" must be " - "greater than zero."); - return -1; - } - if (child_config.startservers == 0) { - log_message (LOG_ERR, - "child_pool_create: \"StartServers\" must be " - "greater than zero."); - return -1; - } - - child_ptr = - (struct child_s *) calloc_shared_memory (child_config.maxclients, - sizeof (struct child_s)); - if (!child_ptr) { - log_message (LOG_ERR, - "Could not allocate memory for children."); - return -1; - } - - servers_waiting = - (unsigned int *) malloc_shared_memory (sizeof (unsigned int)); - if (servers_waiting == MAP_FAILED) { - log_message (LOG_ERR, - "Could not allocate memory for child counting."); - return -1; - } - *servers_waiting = 0; - - /* - * Create a "locking" file for use around the servers_waiting - * variable. - */ - _child_lock_init (); - - if (child_config.startservers > child_config.maxclients) { - log_message (LOG_WARNING, - "Can not start more than \"MaxClients\" servers. " - "Starting %u servers instead.", - child_config.maxclients); - child_config.startservers = child_config.maxclients; - } - - for (i = 0; i != child_config.maxclients; i++) { - child_ptr[i].status = T_EMPTY; - child_ptr[i].connects = 0; - } - - for (i = 0; i != child_config.startservers; i++) { - DEBUG2 ("Trying to create child %d of %d", i + 1, - child_config.startservers); - child_ptr[i].status = T_WAITING; - child_ptr[i].tid = child_make (&child_ptr[i]); - - if (child_ptr[i].tid < 0) { - log_message (LOG_WARNING, - "Could not create child number %d of %d", - i, child_config.startservers); - return -1; - } else { - log_message (LOG_INFO, - "Creating child number %d of %d ...", - i + 1, child_config.startservers); - - SERVER_INC (); - } - } - - log_message (LOG_INFO, "Finished creating all children."); - - return 0; -} - -/* - * Keep the proper number of servers running. This is the birth of the - * servers. It monitors this at least once a second. - */ -void child_main_loop (void) -{ - unsigned int i; - - while (1) { - if (config.quit) - return; - - /* If there are not enough spare servers, create more */ - SERVER_COUNT_LOCK (); - if (*servers_waiting < child_config.minspareservers) { - log_message (LOG_NOTICE, - "Waiting servers (%d) is less than MinSpareServers (%d). " - "Creating new child.", - *servers_waiting, - child_config.minspareservers); - - SERVER_COUNT_UNLOCK (); - - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status == T_EMPTY) { - child_ptr[i].status = T_WAITING; - child_ptr[i].tid = - child_make (&child_ptr[i]); - if (child_ptr[i].tid < 0) { - log_message (LOG_NOTICE, - "Could not create child"); - - child_ptr[i].status = T_EMPTY; - break; - } - - SERVER_INC (); - - break; - } - } - } else { - SERVER_COUNT_UNLOCK (); - } - - sleep (5); + was_full = 0; + listenfd = -1; + maxfd = 0; /* Handle log rotation if it was requested */ if (received_sighup) { @@ -519,11 +124,117 @@ void child_main_loop (void) filter_reload (); #endif /* FILTER_ENABLE */ - /* propagate filter reload to all children */ - child_kill_children (SIGHUP); - received_sighup = FALSE; } + + + FD_ZERO(&rfds); + + for (i = 0; i < vector_length(listen_fds); i++) { + int *fd = (int *) vector_getentry(listen_fds, i, NULL); + + ret = socket_nonblocking(*fd); + if (ret != 0) { + log_message(LOG_ERR, "Failed to set the listening " + "socket %d to non-blocking: %s", + fd, strerror(errno)); + continue; + } + + FD_SET(*fd, &rfds); + maxfd = max(maxfd, *fd); + } + + ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); + if (ret == -1) { + if (errno == EINTR) { + continue; + } + log_message (LOG_ERR, "error calling select: %s", + strerror(errno)); + continue; + } else if (ret == 0) { + log_message (LOG_WARNING, "Strange: select returned 0 " + "but we did not specify a timeout..."); + continue; + } + + for (i = 0; i < vector_length(listen_fds); i++) { + int *fd = (int *) vector_getentry(listen_fds, i, NULL); + + if (FD_ISSET(*fd, &rfds)) { + /* + * only accept the connection on the first + * fd that we find readable. - fair? + */ + listenfd = *fd; + break; + } + } + + if (listenfd == -1) { + log_message(LOG_WARNING, "Strange: None of our listen " + "fds was readable after select"); + continue; + } + + ret = socket_blocking(listenfd); + if (ret != 0) { + log_message(LOG_ERR, "Failed to set listening " + "socket %d to blocking for accept: %s", + listenfd, strerror(errno)); + continue; + } + + /* + * We have a socket that is readable. + * Continue handling this connection. + */ + + connfd = accept (listenfd, cliaddr, &clilen); + + + /* + * Make sure no error occurred... + */ + if (connfd < 0) { + log_message (LOG_ERR, + "Accept returned an error (%s) ... retrying.", + strerror (errno)); + continue; + } + + child = safemalloc(sizeof(struct child)); + if (!child) { +oom: + close(connfd); + log_message (LOG_CRIT, + "Could not allocate memory for child."); + usleep(16); /* prevent 100% CPU usage in OOM situation */ + continue; + } + + child->done = 0; + + if (!sblist_add(childs, &child)) { + free(child); + goto oom; + } + + child->client.fd = connfd; + memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage)); + + attrp = 0; + if (pthread_attr_init(&attr) == 0) { + attrp = &attr; + pthread_attr_setstacksize(attrp, 256*1024); + } + + if (pthread_create(&child->thread, attrp, child_thread, child) != 0) { + sblist_delete(childs, sblist_getsize(childs) -1); + free(child); + goto oom; + } } } @@ -532,18 +243,32 @@ void child_main_loop (void) */ void child_kill_children (int sig) { - unsigned int i; + size_t i; - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status != T_EMPTY) - kill (child_ptr[i].tid, sig); - } + if (sig != SIGTERM) return; + + for (i = 0; i < sblist_getsize(childs); i++) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (!c->done) { + /* interrupt blocking operations. + this should cause the threads to shutdown orderly. */ + close(c->client.fd); + } + } + usleep(16); + collect_threads(); + if (sblist_getsize(childs) != 0) + log_message (LOG_CRIT, + "child_kill_children: %zu threads still alive!", + sblist_getsize(childs) + ); } + /** * Listen on the various configured interfaces */ -int child_listening_sockets (vector_t listen_addrs, uint16_t port) +int child_listening_sockets(vector_t listen_addrs, uint16_t port) { int ret; ssize_t i; @@ -551,7 +276,7 @@ int child_listening_sockets (vector_t listen_addrs, uint16_t port) assert (port > 0); if (listen_fds == NULL) { - listen_fds = vector_create (); + listen_fds = vector_create(); if (listen_fds == NULL) { log_message (LOG_ERR, "Could not create the list " "of listening fds"); @@ -559,26 +284,28 @@ int child_listening_sockets (vector_t listen_addrs, uint16_t port) } } - if ((listen_addrs == NULL) || (vector_length (listen_addrs) == 0)) { + if ((listen_addrs == NULL) || + (vector_length(listen_addrs) == 0)) + { /* * no Listen directive: * listen on the wildcard address(es) */ - ret = listen_sock (NULL, port, listen_fds); + ret = listen_sock(NULL, port, listen_fds); return ret; } - for (i = 0; i < vector_length (listen_addrs); i++) { + for (i = 0; i < vector_length(listen_addrs); i++) { const char *addr; - addr = (char *) vector_getentry (listen_addrs, i, NULL); + addr = (char *)vector_getentry(listen_addrs, i, NULL); if (addr == NULL) { - log_message (LOG_WARNING, - "got NULL from listen_addrs - skipping"); + log_message(LOG_WARNING, + "got NULL from listen_addrs - skipping"); continue; } - ret = listen_sock (addr, port, listen_fds); + ret = listen_sock(addr, port, listen_fds); if (ret != 0) { return ret; } @@ -591,12 +318,12 @@ void child_close_sock (void) { ssize_t i; - for (i = 0; i < vector_length (listen_fds); i++) { - int *fd = (int *) vector_getentry (listen_fds, i, NULL); + for (i = 0; i < vector_length(listen_fds); i++) { + int *fd = (int *) vector_getentry(listen_fds, i, NULL); close (*fd); } - vector_delete (listen_fds); + vector_delete(listen_fds); listen_fds = NULL; } diff --git a/src/conf.c b/src/conf.c index a069082..59d6603 100644 --- a/src/conf.c +++ b/src/conf.c @@ -27,7 +27,6 @@ #include "acl.h" #include "anonymous.h" -#include "child.h" #include "filter.h" #include "heap.h" #include "html-error.h" @@ -93,7 +92,8 @@ * All configuration handling functions are REQUIRED to be defined * with the same function template as below. */ -typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]); +typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, + unsigned long, regmatch_t[]); /* * Define the pattern used by any directive handling function. The @@ -108,7 +108,7 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]); */ #define HANDLE_FUNC(func) \ int func(struct config_s* conf, const char* line, \ - regmatch_t match[]) + unsigned long lineno, regmatch_t match[]) /* * List all the handling functions. These are defined later, but they need @@ -141,9 +141,7 @@ static HANDLE_FUNC (handle_listen); static HANDLE_FUNC (handle_logfile); static HANDLE_FUNC (handle_loglevel); static HANDLE_FUNC (handle_maxclients); -static HANDLE_FUNC (handle_maxrequestsperchild); -static HANDLE_FUNC (handle_maxspareservers); -static HANDLE_FUNC (handle_minspareservers); +static HANDLE_FUNC (handle_obsolete); static HANDLE_FUNC (handle_pidfile); static HANDLE_FUNC (handle_port); #ifdef REVERSE_SUPPORT @@ -152,7 +150,6 @@ static HANDLE_FUNC (handle_reversemagic); static HANDLE_FUNC (handle_reverseonly); static HANDLE_FUNC (handle_reversepath); #endif -static HANDLE_FUNC (handle_startservers); static HANDLE_FUNC (handle_statfile); static HANDLE_FUNC (handle_stathost); static HANDLE_FUNC (handle_syslog); @@ -197,89 +194,95 @@ struct { } directives[] = { /* comments */ { - BEGIN "#", handle_nop, NULL}, - /* blank lines */ + BEGIN "#", handle_nop, NULL + }, + /* blank lines */ { - "^[[:space:]]+$", handle_nop, NULL}, - /* string arguments */ - STDCONF ("logfile", STR, handle_logfile), - STDCONF ("pidfile", STR, handle_pidfile), - STDCONF ("anonymous", STR, handle_anonymous), - STDCONF ("viaproxyname", STR, handle_viaproxyname), - STDCONF ("defaulterrorfile", STR, handle_defaulterrorfile), - STDCONF ("statfile", STR, handle_statfile), - STDCONF ("stathost", STR, handle_stathost), - STDCONF ("xtinyproxy", BOOL, handle_xtinyproxy), - /* boolean arguments */ - STDCONF ("syslog", BOOL, handle_syslog), - STDCONF ("bindsame", BOOL, handle_bindsame), - STDCONF ("disableviaheader", BOOL, handle_disableviaheader), - /* integer arguments */ - STDCONF ("port", INT, handle_port), - STDCONF ("maxclients", INT, handle_maxclients), - STDCONF ("maxspareservers", INT, handle_maxspareservers), - STDCONF ("minspareservers", INT, handle_minspareservers), - STDCONF ("startservers", INT, handle_startservers), - STDCONF ("maxrequestsperchild", INT, handle_maxrequestsperchild), - STDCONF ("timeout", INT, handle_timeout), + "^[[:space:]]+$", handle_nop, NULL + }, + /* string arguments */ + STDCONF ("logfile", STR, handle_logfile), + STDCONF ("pidfile", STR, handle_pidfile), + STDCONF ("anonymous", STR, handle_anonymous), + STDCONF ("viaproxyname", STR, handle_viaproxyname), + STDCONF ("defaulterrorfile", STR, handle_defaulterrorfile), + STDCONF ("statfile", STR, handle_statfile), + STDCONF ("stathost", STR, handle_stathost), + STDCONF ("xtinyproxy", BOOL, handle_xtinyproxy), + /* boolean arguments */ + STDCONF ("syslog", BOOL, handle_syslog), + STDCONF ("bindsame", BOOL, handle_bindsame), + STDCONF ("disableviaheader", BOOL, handle_disableviaheader), + /* integer arguments */ + STDCONF ("port", INT, handle_port), + STDCONF ("maxclients", INT, handle_maxclients), + STDCONF ("maxspareservers", INT, handle_obsolete), + STDCONF ("minspareservers", INT, handle_obsolete), + STDCONF ("startservers", INT, handle_obsolete), + STDCONF ("maxrequestsperchild", INT, handle_obsolete), + STDCONF ("timeout", INT, handle_timeout), #ifdef UPSTREAM_SUPPORT - STDCONF ("deadtime", INT, handle_deadtime), + STDCONF ("deadtime", INT, handle_deadtime), #endif - STDCONF ("connectport", INT, handle_connectport), - /* alphanumeric arguments */ - STDCONF ("user", ALNUM, handle_user), - STDCONF ("group", ALNUM, handle_group), - /* ip arguments */ - STDCONF ("listen", "(" IP "|" IPV6 ")", handle_listen), - STDCONF ("allow", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", - handle_allow), - STDCONF ("deny", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", - handle_deny), - STDCONF ("bind", "(" IP "|" IPV6 ")", handle_bind), - /* other */ - STDCONF ("basicauth", ALNUM WS ALNUM, handle_basicauth), - STDCONF ("errorfile", INT WS STR, handle_errorfile), - STDCONF ("addheader", STR WS STR, handle_addheader), + STDCONF ("connectport", INT, handle_connectport), + /* alphanumeric arguments */ + STDCONF ("user", ALNUM, handle_user), + STDCONF ("group", ALNUM, handle_group), + /* ip arguments */ + STDCONF ("listen", "(" IP "|" IPV6 ")", handle_listen), + STDCONF ("allow", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + handle_allow), + STDCONF ("deny", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + handle_deny), + STDCONF ("bind", "(" IP "|" IPV6 ")", handle_bind), + /* other */ + STDCONF ("basicauth", ALNUM WS ALNUM, handle_basicauth), + STDCONF ("errorfile", INT WS STR, handle_errorfile), + STDCONF ("addheader", STR WS STR, handle_addheader), + #ifdef FILTER_ENABLE - /* filtering */ - STDCONF ("filter", STR, handle_filter), - STDCONF ("filterurls", BOOL, handle_filterurls), - STDCONF ("filterextended", BOOL, handle_filterextended), - STDCONF ("filterdefaultdeny", BOOL, handle_filterdefaultdeny), - STDCONF ("filtercasesensitive", BOOL, handle_filtercasesensitive), + /* filtering */ + STDCONF ("filter", STR, handle_filter), + STDCONF ("filterurls", BOOL, handle_filterurls), + STDCONF ("filterextended", BOOL, handle_filterextended), + STDCONF ("filterdefaultdeny", BOOL, handle_filterdefaultdeny), + STDCONF ("filtercasesensitive", BOOL, handle_filtercasesensitive), #endif #ifdef REVERSE_SUPPORT - /* Reverse proxy arguments */ - STDCONF ("reversebaseurl", STR, handle_reversebaseurl), - STDCONF ("reverseonly", BOOL, handle_reverseonly), - STDCONF ("reversemagic", BOOL, handle_reversemagic), - STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), + /* Reverse proxy arguments */ + STDCONF ("reversebaseurl", STR, handle_reversebaseurl), + STDCONF ("reverseonly", BOOL, handle_reverseonly), + STDCONF ("reversemagic", BOOL, handle_reversemagic), + STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), #endif #ifdef UPSTREAM_SUPPORT { - BEGIN "(upstream)" WS "(none)" WS STR END, handle_upstream_no, NULL}, + BEGIN "(upstream)" WS "(none)" WS STR END, handle_upstream_no, NULL + }, + { BEGIN "(upstream)" WS "(http|socks4|socks5)" WS - "(" USERNAME /*username */ ":" PASSWORD /*password */ "@" - ")?" - "(" IP "|" ALNUM ")" ":" INT "(" "(" PIPE "(" IP - "|" ALNUM ")" ":" INT ")+" ")?" "(" WS STR ")?" - END, handle_upstream, NULL}, + "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" + "(" IP "|" ALNUM ")" ":" INT "(" "(" PIPE "(" IP + "|" ALNUM ")" ":" INT ")+" ")?" "(" WS STR ")?" + END, handle_upstream, NULL + }, #endif - /* loglevel */ - STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", - handle_loglevel) + /* loglevel */ + STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", + handle_loglevel) }; const unsigned int ndirectives = sizeof (directives) / sizeof (directives[0]); -static void free_added_headers (vector_t add_headers) +static void +free_added_headers (vector_t add_headers) { ssize_t i; for (i = 0; i < vector_length (add_headers); i++) { http_header_t *header = (http_header_t *) - vector_getentry (add_headers, i, NULL); + vector_getentry (add_headers, i, NULL); safefree (header->name); safefree (header->value); @@ -295,18 +298,18 @@ void free_config (struct config_s *conf) safefree (conf->stathost); safefree (conf->user); safefree (conf->group); - vector_delete (conf->listen_addrs); - vector_delete (conf->basicauth_list); + vector_delete(conf->listen_addrs); + vector_delete(conf->basicauth_list); #ifdef FILTER_ENABLE safefree (conf->filter); -#endif /* FILTER_ENABLE */ +#endif /* FILTER_ENABLE */ #ifdef REVERSE_SUPPORT - free_reversepath_list (conf->reversepath_list); + free_reversepath_list(conf->reversepath_list); safefree (conf->reversebaseurl); #endif #ifdef UPSTREAM_SUPPORT free_upstream_list (conf->upstream_list); -#endif /* UPSTREAM_SUPPORT */ +#endif /* UPSTREAM_SUPPORT */ safefree (conf->pidpath); safefree (conf->bind_address); safefree (conf->via_proxy_name); @@ -318,7 +321,7 @@ void free_config (struct config_s *conf) free_connect_ports_list (conf->connect_ports); hashmap_delete (conf->anonymous_map); - memset (conf, 0, sizeof (*conf)); + memset (conf, 0, sizeof(*conf)); } /* @@ -327,7 +330,8 @@ void free_config (struct config_s *conf) * * Returns 0 on success; negative upon failure. */ -int config_compile_regex (void) +int +config_compile_regex (void) { unsigned int i, r; @@ -355,7 +359,8 @@ int config_compile_regex (void) * Frees pre-compiled regular expressions used by the configuration * file. This function is registered to be automatically called at exit. */ -static void config_free_regex (void) +static void +config_free_regex (void) { unsigned int i; @@ -376,7 +381,8 @@ static void config_free_regex (void) * Returns 0 if a match was found and successfully processed; otherwise, * a negative number is returned. */ -static int check_match (struct config_s *conf, const char *line) +static int check_match (struct config_s *conf, const char *line, + unsigned long lineno) { regmatch_t match[RE_MAX_MATCHES]; unsigned int i; @@ -386,7 +392,7 @@ static int check_match (struct config_s *conf, const char *line) assert (directives[i].cre); if (!regexec (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) - return (*directives[i].handler) (conf, line, match); + return (*directives[i].handler) (conf, line, lineno, match); } return -1; @@ -401,7 +407,7 @@ static int config_parse (struct config_s *conf, FILE * f) unsigned long lineno = 1; while (fgets (buffer, sizeof (buffer), f)) { - if (check_match (conf, buffer)) { + if (check_match (conf, buffer, lineno)) { printf ("Syntax error on line %ld\n", lineno); return 1; } @@ -473,16 +479,17 @@ static void initialize_with_defaults (struct config_s *conf, if (defaults->listen_addrs) { ssize_t i; - conf->listen_addrs = vector_create (); - for (i = 0; i < vector_length (defaults->listen_addrs); i++) { + conf->listen_addrs = vector_create(); + for (i=0; i < vector_length(defaults->listen_addrs); i++) { char *addr; size_t size; - addr = (char *) vector_getentry (defaults->listen_addrs, - i, &size); - vector_append (conf->listen_addrs, addr, size); + addr = (char *)vector_getentry(defaults->listen_addrs, + i, &size); + vector_append(conf->listen_addrs, addr, size); } } + #ifdef FILTER_ENABLE if (defaults->filter) { conf->filter = safestrdup (defaults->filter); @@ -491,7 +498,7 @@ static void initialize_with_defaults (struct config_s *conf, conf->filter_url = defaults->filter_url; conf->filter_extended = defaults->filter_extended; conf->filter_casesensitive = defaults->filter_casesensitive; -#endif /* FILTER_ENABLE */ +#endif /* FILTER_ENABLE */ #ifdef XTINYPROXY_ENABLE conf->add_xtinyproxy = defaults->add_xtinyproxy; @@ -508,8 +515,9 @@ static void initialize_with_defaults (struct config_s *conf, #endif #ifdef UPSTREAM_SUPPORT + conf->upstream_list = NULL; /* struct upstream *upstream_list; */ -#endif /* UPSTREAM_SUPPORT */ +#endif /* UPSTREAM_SUPPORT */ if (defaults->pidpath) { conf->pidpath = safestrdup (defaults->pidpath); @@ -668,7 +676,8 @@ set_bool_arg (unsigned int *var, const char *line, regmatch_t * match) return 0; } -static unsigned long get_long_arg (const char *line, regmatch_t * match) +static unsigned long +get_long_arg (const char *line, regmatch_t * match) { assert (line); assert (match && match->rm_so != -1); @@ -676,7 +685,8 @@ static unsigned long get_long_arg (const char *line, regmatch_t * match) return strtoul (line + match->rm_so, NULL, 0); } -static int set_int_arg (unsigned int *var, const char *line, regmatch_t * match) +static int +set_int_arg (unsigned int *var, const char *line, regmatch_t * match) { assert (var); assert (line); @@ -733,7 +743,8 @@ static HANDLE_FUNC (handle_viaproxyname) if (r) return r; log_message (LOG_INFO, - "Setting \"Via\" header to '%s'", conf->via_proxy_name); + "Setting \"Via\" header to '%s'", + conf->via_proxy_name); return 0; } @@ -745,7 +756,8 @@ static HANDLE_FUNC (handle_disableviaheader) return r; } - log_message (LOG_INFO, "Disabling transmission of the \"Via\" header."); + log_message (LOG_INFO, + "Disabling transmission of the \"Via\" header."); return 0; } @@ -810,32 +822,14 @@ static HANDLE_FUNC (handle_port) static HANDLE_FUNC (handle_maxclients) { - child_configure (CHILD_MAXCLIENTS, get_long_arg (line, &match[2])); + set_int_arg (&conf->maxclients, line, &match[2]); return 0; } -static HANDLE_FUNC (handle_maxspareservers) +static HANDLE_FUNC (handle_obsolete) { - child_configure (CHILD_MAXSPARESERVERS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_minspareservers) -{ - child_configure (CHILD_MINSPARESERVERS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_startservers) -{ - child_configure (CHILD_STARTSERVERS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_maxrequestsperchild) -{ - child_configure (CHILD_MAXREQUESTSPERCHILD, - get_long_arg (line, &match[2])); + fprintf (stderr, "WARNING: obsolete config item on line %lu\n", + lineno); return 0; } @@ -906,18 +900,18 @@ static HANDLE_FUNC (handle_listen) } if (conf->listen_addrs == NULL) { - conf->listen_addrs = vector_create (); - if (conf->listen_addrs == NULL) { - log_message (LOG_WARNING, "Could not create a list " - "of listen addresses."); - safefree (arg); - return -1; - } + conf->listen_addrs = vector_create(); + if (conf->listen_addrs == NULL) { + log_message (LOG_WARNING, "Could not create a list " + "of listen addresses."); + safefree (arg); + return -1; + } } - vector_append (conf->listen_addrs, arg, strlen (arg) + 1); + vector_append (conf->listen_addrs, arg, strlen(arg) + 1); - log_message (LOG_INFO, "Added address [%s] to listen addresses.", arg); + log_message(LOG_INFO, "Added address [%s] to listen addresses.", arg); safefree (arg); return 0; @@ -1004,10 +998,10 @@ static HANDLE_FUNC (handle_loglevel) static HANDLE_FUNC (handle_basicauth) { char *user, *pass; - user = get_string_arg (line, &match[2]); + user = get_string_arg(line, &match[2]); if (!user) return -1; - pass = get_string_arg (line, &match[3]); + pass = get_string_arg(line, &match[3]); if (!pass) { safefree (user); return -1; @@ -1099,19 +1093,19 @@ static HANDLE_FUNC (handle_reversepath) #ifdef UPSTREAM_SUPPORT -static enum proxy_type pt_from_string (const char *s) +static enum proxy_type pt_from_string(const char *s) { - static const char pt_map[][7] = { - [PT_NONE] = "none", - [PT_HTTP] = "http", - [PT_SOCKS4] = "socks4", - [PT_SOCKS5] = "socks5", - }; - unsigned i; - for (i = 0; i < sizeof (pt_map) / sizeof (pt_map[0]); i++) - if (!strcmp (pt_map[i], s)) - return i; - return PT_NONE; + static const char pt_map[][7] = { + [PT_NONE] = "none", + [PT_HTTP] = "http", + [PT_SOCKS4] = "socks4", + [PT_SOCKS5] = "socks5", + }; + unsigned i; + for (i = 0; i < sizeof (pt_map) / sizeof (pt_map[0]); i++) + if (!strcmp (pt_map[i], s)) + return i; + return PT_NONE; } static HANDLE_FUNC (handle_upstream) @@ -1124,8 +1118,8 @@ static HANDLE_FUNC (handle_upstream) pltr = plist = (upstream_proxy_list_t *) safemalloc (sizeof (upstream_proxy_list_t)); tmp = get_string_arg (line, &match[mi]); - pt = pt_from_string (tmp); - safefree (tmp); + pt = pt_from_string(tmp); + safefree(tmp); mi += 2; if (match[mi].rm_so != -1) diff --git a/src/conf.h b/src/conf.h index 57b6243..d8ba56a 100644 --- a/src/conf.h +++ b/src/conf.h @@ -45,6 +45,7 @@ struct config_s { char *stathost; unsigned int godaemon; /* boolean */ unsigned int quit; /* boolean */ + unsigned int maxclients; char *user; char *group; vector_t listen_addrs; diff --git a/src/connect-ports.c b/src/connect-ports.c index 46d15dd..41b4e3d 100644 --- a/src/connect-ports.c +++ b/src/connect-ports.c @@ -25,7 +25,7 @@ * Now, this routine adds a "port" to the list. It also creates the list if * it hasn't already by done. */ -void add_connect_port_allowed (int port, vector_t * connect_ports) +void add_connect_port_allowed (int port, vector_t *connect_ports) { if (!*connect_ports) { *connect_ports = vector_create (); @@ -53,8 +53,8 @@ int check_allowed_connect_ports (int port, vector_t connect_ports) int *data; /* - * The absence of ConnectPort options in the config file - * meanas that all ports are allowed for CONNECT. + * The absence of ConnectPort options in the config file + * meanas that all ports are allowed for CONNECT. */ if (!connect_ports) return 1; diff --git a/src/conns.c b/src/conns.c index 94faeea..505b5c4 100644 --- a/src/conns.c +++ b/src/conns.c @@ -31,7 +31,6 @@ #include "stats.h" struct conn_s *initialize_conn (int client_fd, const char *ipaddr, - const char *string_addr, const char *sock_ipaddr) { struct conn_s *connptr; @@ -79,7 +78,6 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr, connptr->server_ip_addr = (sock_ipaddr ? safestrdup (sock_ipaddr) : NULL); connptr->client_ip_addr = safestrdup (ipaddr); - connptr->client_string_addr = safestrdup (string_addr); connptr->upstream_proxy = NULL; @@ -134,8 +132,6 @@ void destroy_conn (struct conn_s *connptr) safefree (connptr->server_ip_addr); if (connptr->client_ip_addr) safefree (connptr->client_ip_addr); - if (connptr->client_string_addr) - safefree (connptr->client_string_addr); #ifdef REVERSE_SUPPORT if (connptr->reversepath) diff --git a/src/conns.h b/src/conns.h index b63d026..393e5d4 100644 --- a/src/conns.h +++ b/src/conns.h @@ -62,10 +62,9 @@ struct conn_s { char *server_ip_addr; /* - * Store the client's IP and hostname information + * Store the client's IP information */ char *client_ip_addr; - char *client_string_addr; /* * Store the incoming request's HTTP protocol. @@ -92,7 +91,6 @@ struct conn_s { * Functions for the creation and destruction of a connection structure. */ extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr, - const char *string_addr, const char *sock_ipaddr); extern void destroy_conn (struct conn_s *connptr); diff --git a/src/daemon.c b/src/daemon.c index 22a8377..41b821a 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -41,9 +41,10 @@ void makedaemon (void) if (fork () != 0) exit (0); - if (chdir ("/") != 0) { - log_message (LOG_WARNING, "Could not change directory to /"); - } + if (chdir ("/") != 0) { + log_message (LOG_WARNING, + "Could not change directory to /"); + } umask (0177); diff --git a/src/filter.c b/src/filter.c index 3d5d937..e18132e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -104,11 +104,13 @@ void filter_init (void) if (*s == '\0') continue; - if (!p) /* head of list */ - fl = p = (struct filter_list *) + if (!p) /* head of list */ + fl = p = + (struct filter_list *) safecalloc (1, sizeof (struct filter_list)); - else { /* next entry */ - p->next = (struct filter_list *) + else { /* next entry */ + p->next = + (struct filter_list *) safecalloc (1, sizeof (struct filter_list)); p = p->next; } diff --git a/src/hashmap.c b/src/hashmap.c index eca5a21..7793d08 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -104,7 +104,7 @@ hashmap_t hashmap_create (unsigned int nbuckets) if (!ptr) return NULL; - ptr->seed = (uint32_t) rand (); + ptr->seed = (uint32_t)rand(); ptr->size = nbuckets; ptr->buckets = (struct hashbucket_s *) safecalloc (nbuckets, sizeof (struct @@ -508,7 +508,8 @@ char *lookup_variable (hashmap_t map, const char *varname) if (hashmap_is_end (map, result_iter)) return (NULL); - if (hashmap_return_entry (map, result_iter, &key, (void **) &data) < 0) + if (hashmap_return_entry (map, result_iter, + &key, (void **) &data) < 0) return (NULL); return (data); diff --git a/src/heap.c b/src/heap.c index c7d8560..0611c39 100644 --- a/src/heap.c +++ b/src/heap.c @@ -97,61 +97,3 @@ char *debugging_strdup (const char *s, const char *file, unsigned long line) #endif /* !NDEBUG */ -/* - * Allocate a block of memory in the "shared" memory region. - * - * FIXME: This uses the most basic (and slowest) means of creating a - * shared memory location. It requires the use of a temporary file. We might - * want to look into something like MM (Shared Memory Library) for a better - * solution. - */ -void *malloc_shared_memory (size_t size) -{ - int fd; - void *ptr; - char buffer[32]; - - static const char *shared_file = "/tmp/tinyproxy.shared.XXXXXX"; - - assert (size > 0); - - strlcpy (buffer, shared_file, sizeof (buffer)); - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - if ((fd = mkstemp (buffer)) == -1) - return MAP_FAILED; - unlink (buffer); - - if (ftruncate (fd, size) == -1) - return MAP_FAILED; - ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - return ptr; -} - -/* - * Allocate a block of memory from the "shared" region an initialize it to - * zero. - */ -void *calloc_shared_memory (size_t nmemb, size_t size) -{ - void *ptr; - long length; - - assert (nmemb > 0); - assert (size > 0); - - length = nmemb * size; - - ptr = malloc_shared_memory (length); - if (ptr == MAP_FAILED) - return ptr; - - memset (ptr, 0, length); - - return ptr; -} diff --git a/src/heap.h b/src/heap.h index 9f1e51f..aca69ec 100644 --- a/src/heap.h +++ b/src/heap.h @@ -52,10 +52,4 @@ extern char *debugging_strdup (const char *s, const char *file, #endif -/* - * Allocate memory from the "shared" region of memory. - */ -extern void *malloc_shared_memory (size_t size); -extern void *calloc_shared_memory (size_t nmemb, size_t size); - #endif diff --git a/src/html-error.c b/src/html-error.c index aa3b02f..2b15c08 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -86,7 +86,8 @@ static char *get_html_file (unsigned int errornum) /* * Send an already-opened file to the client with variable substitution. */ -int send_html_file (FILE * infile, struct conn_s *connptr) +int +send_html_file (FILE *infile, struct conn_s *connptr) { char *inbuf; char *varstart = NULL; @@ -104,9 +105,8 @@ int send_html_file (FILE * infile, struct conn_s *connptr) if (in_variable) { *p = '\0'; varval = (const char *) - lookup_variable - (connptr->error_variables, - varstart); + lookup_variable (connptr->error_variables, + varstart); if (!varval) varval = "(unknown)"; r = write_message (connptr->client_fd, @@ -160,21 +160,25 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message) const char headers[] = "HTTP/1.0 %d %s\r\n" "Server: %s/%s\r\n" - "Content-Type: text/html\r\n" "%s" "Connection: close\r\n" "\r\n"; + "Content-Type: text/html\r\n" + "%s" + "Connection: close\r\n" "\r\n"; const char p_auth_str[] = - "Proxy-Authenticate: Basic realm=\"" PACKAGE_NAME "\"\r\n"; + "Proxy-Authenticate: Basic realm=\"" + PACKAGE_NAME "\"\r\n"; const char w_auth_str[] = - "WWW-Authenticate: Basic realm=\"" PACKAGE_NAME "\"\r\n"; + "WWW-Authenticate: Basic realm=\"" + PACKAGE_NAME "\"\r\n"; - /* according to rfc7235, the 407 error must be accompanied by - * a Proxy-Authenticate header field. */ - const char *add = - code == 407 ? p_auth_str : (code == 401 ? w_auth_str : ""); + /* according to rfc7235, the 407 error must be accompanied by + a Proxy-Authenticate header field. */ + const char *add = code == 407 ? p_auth_str : (code == 401 ? w_auth_str : ""); return (write_message (connptr->client_fd, headers, - code, message, PACKAGE, VERSION, add)); + code, message, PACKAGE, VERSION, + add)); } /* @@ -203,12 +207,12 @@ int send_http_error_message (struct conn_s *connptr) error_file = get_html_file (connptr->error_number); if (!(infile = fopen (error_file, "r"))) { - char *detail = - lookup_variable (connptr->error_variables, "detail"); - return (write_message - (connptr->client_fd, fallback_error, - connptr->error_number, connptr->error_string, - connptr->error_string, detail, PACKAGE, VERSION)); + char *detail = lookup_variable (connptr->error_variables, "detail"); + return (write_message (connptr->client_fd, fallback_error, + connptr->error_number, + connptr->error_string, + connptr->error_string, + detail, PACKAGE, VERSION)); } ret = send_html_file (infile, connptr); @@ -258,7 +262,6 @@ int add_standard_vars (struct conn_s *connptr) ADD_VAR_RET ("cause", connptr->error_string); ADD_VAR_RET ("request", connptr->request_line); ADD_VAR_RET ("clientip", connptr->client_ip_addr); - ADD_VAR_RET ("clienthost", connptr->client_string_addr); /* The following value parts are all non-NULL and will * trigger warnings in ADD_VAR_RET(), so we use @@ -270,7 +273,8 @@ int add_standard_vars (struct conn_s *connptr) gmtime (&global_time)); add_error_variable (connptr, "date", timebuf); - add_error_variable (connptr, "website", "https://tinyproxy.github.io/"); + add_error_variable (connptr, "website", + "https://tinyproxy.github.io/"); add_error_variable (connptr, "version", VERSION); add_error_variable (connptr, "package", PACKAGE); diff --git a/src/log.c b/src/log.c index 56f14f7..0ed686b 100644 --- a/src/log.c +++ b/src/log.c @@ -29,6 +29,7 @@ #include "utils.h" #include "vector.h" #include "conf.h" +#include static const char *syslog_level[] = { NULL, @@ -45,6 +46,8 @@ static const char *syslog_level[] = { #define TIME_LENGTH 16 #define STRING_LENGTH 800 +static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + /* * Global file descriptor for the log file */ @@ -63,7 +66,7 @@ static int log_level = LOG_INFO; */ static vector_t log_message_storage; -static unsigned int logging_initialized = FALSE; /* boolean */ +static unsigned int logging_initialized = FALSE; /* boolean */ /* * Open the log file and store the file descriptor in a global location. @@ -71,8 +74,8 @@ static unsigned int logging_initialized = FALSE; /* boolean */ int open_log_file (const char *log_file_name) { if (log_file_name == NULL) { - if (config.godaemon == FALSE) - log_file_fd = fileno (stdout); + if(config.godaemon == FALSE) + log_file_fd = fileno(stdout); else log_file_fd = -1; } else { @@ -86,7 +89,7 @@ int open_log_file (const char *log_file_name) */ void close_log_file (void) { - if (log_file_fd < 0 || log_file_fd == fileno (stdout)) { + if (log_file_fd < 0 || log_file_fd == fileno(stdout)) { return; } @@ -161,16 +164,18 @@ void log_message (int level, const char *fmt, ...) goto out; } - if (!config.syslog && log_file_fd == -1) + if(!config.syslog && log_file_fd == -1) goto out; if (config.syslog) { + pthread_mutex_lock(&log_mutex); #ifdef HAVE_VSYSLOG_H vsyslog (level, fmt, args); #else vsnprintf (str, STRING_LENGTH, fmt, args); syslog (level, "%s", str); #endif + pthread_mutex_unlock(&log_mutex); } else { char *p; @@ -187,27 +192,33 @@ void log_message (int level, const char *fmt, ...) * Overwrite the '\0' and leave room for a trailing '\n' * be added next. */ - p = str + strlen (str); - vsnprintf (p, STRING_LENGTH - strlen (str) - 1, fmt, args); + p = str + strlen(str); + vsnprintf (p, STRING_LENGTH - strlen(str) - 1, fmt, args); - p = str + strlen (str); + p = str + strlen(str); *p = '\n'; - *(p + 1) = '\0'; + *(p+1) = '\0'; assert (log_file_fd >= 0); + pthread_mutex_lock(&log_mutex); ret = write (log_file_fd, str, strlen (str)); + pthread_mutex_unlock(&log_mutex); + if (ret == -1) { config.syslog = TRUE; - log_message (LOG_CRIT, "ERROR: Could not write to log " - "file %s: %s.", - config.logf_name, strerror (errno)); - log_message (LOG_CRIT, - "Falling back to syslog logging"); + log_message(LOG_CRIT, "ERROR: Could not write to log " + "file %s: %s.", + config.logf_name, strerror(errno)); + log_message(LOG_CRIT, + "Falling back to syslog logging"); } + pthread_mutex_lock(&log_mutex); fsync (log_file_fd); + pthread_mutex_unlock(&log_mutex); + } out: @@ -227,7 +238,7 @@ static void send_stored_logs (void) if (log_message_storage == NULL) return; - log_message (LOG_DEBUG, "sending stored logs"); + log_message(LOG_DEBUG, "sending stored logs"); for (i = 0; (ssize_t) i != vector_length (log_message_storage); ++i) { string = @@ -252,7 +263,7 @@ static void send_stored_logs (void) vector_delete (log_message_storage); log_message_storage = NULL; - log_message (LOG_DEBUG, "done sending stored logs"); + log_message(LOG_DEBUG, "done sending stored logs"); } /** diff --git a/src/loop.c b/src/loop.c new file mode 100644 index 0000000..77b073d --- /dev/null +++ b/src/loop.c @@ -0,0 +1,76 @@ +#include +#include +#include "loop.h" +#include "conf.h" +#include "main.h" +#include "sblist.h" +#include "sock.h" + +struct loop_record { + union sockaddr_union addr; + time_t tstamp; +}; + +static sblist *loop_records; +static pthread_mutex_t loop_records_lock = PTHREAD_MUTEX_INITIALIZER; + +void loop_records_init(void) { + loop_records = sblist_new(sizeof (struct loop_record), 32); +} + +#if 0 +static void su_to_str(union sockaddr_union *addr, char *buf) { + int af = addr->v4.sin_family; + unsigned port = ntohs(af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port); + char portb[32]; + sprintf(portb, ":%u", port); + getpeer_information (addr, buf, 256); + strcat(buf, portb); +} +#endif + +void loop_records_add(union sockaddr_union *addr) { + time_t now =time(0); + struct loop_record rec; + pthread_mutex_lock(&loop_records_lock); + rec.tstamp = now; + rec.addr = *addr; + sblist_add(loop_records, &rec); + pthread_mutex_unlock(&loop_records_lock); +} + +#define TIMEOUT_SECS 15 + +int connection_loops (union sockaddr_union *addr) { + int ret = 0, af, our_af = addr->v4.sin_family; + void *ipdata, *our_ipdata = our_af == AF_INET ? (void*)&addr->v4.sin_addr.s_addr : (void*)&addr->v6.sin6_addr.s6_addr; + size_t i, cmp_len = our_af == AF_INET ? sizeof(addr->v4.sin_addr.s_addr) : sizeof(addr->v6.sin6_addr.s6_addr); + unsigned port, our_port = ntohs(our_af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port); + time_t now = time(0); + + pthread_mutex_lock(&loop_records_lock); + for (i = 0; i < sblist_getsize(loop_records); ) { + struct loop_record *rec = sblist_get(loop_records, i); + + if (rec->tstamp + TIMEOUT_SECS < now) { + sblist_delete(loop_records, i); + continue; + } + + if (!ret) { + af = rec->addr.v4.sin_family; + if (af != our_af) goto next; + port = ntohs(af == AF_INET ? rec->addr.v4.sin_port : rec->addr.v6.sin6_port); + if (port != our_port) goto next; + ipdata = af == AF_INET ? (void*)&rec->addr.v4.sin_addr.s_addr : (void*)&rec->addr.v6.sin6_addr.s6_addr; + if (!memcmp(ipdata, our_ipdata, cmp_len)) { + ret = 1; + } + } +next: + i++; + } + pthread_mutex_unlock(&loop_records_lock); + return ret; +} + diff --git a/src/loop.h b/src/loop.h new file mode 100644 index 0000000..19877d3 --- /dev/null +++ b/src/loop.h @@ -0,0 +1,11 @@ +#ifndef LOOP_H +#define LOOP_H + +#include "sock.h" + +void loop_records_init(void); +void loop_records_add(union sockaddr_union *addr); +int connection_loops (union sockaddr_union *addr); + +#endif + diff --git a/src/main.c b/src/main.c index 29fbf5f..6d987cf 100644 --- a/src/main.c +++ b/src/main.c @@ -54,7 +54,8 @@ unsigned int received_sighup = FALSE; /* boolean */ /* * Handle a signal */ -static void takesig (int sig) +static void +takesig (int sig) { pid_t pid; int status; @@ -64,6 +65,7 @@ static void takesig (int sig) received_sighup = TRUE; break; + case SIGINT: case SIGTERM: config.quit = TRUE; break; @@ -79,7 +81,8 @@ static void takesig (int sig) /* * Display the version information for the user. */ -static void display_version (void) +static void +display_version (void) { printf ("%s %s\n", PACKAGE, VERSION); } @@ -87,7 +90,8 @@ static void display_version (void) /* * Display usage to the user. */ -static void display_usage (void) +static void +display_usage (void) { int features = 0; @@ -140,7 +144,8 @@ static void display_usage (void) ".\n"); } -static int get_id (char *str) +static int +get_id (char *str) { char *tstr; @@ -164,7 +169,8 @@ static int get_id (char *str) * * This function parses command line arguments. **/ -static void process_cmdline (int argc, char **argv, struct config_s *conf) +static void +process_cmdline (int argc, char **argv, struct config_s *conf) { int opt; @@ -210,7 +216,8 @@ static void process_cmdline (int argc, char **argv, struct config_s *conf) * the config file. This function is typically called during * initialization when the effective user is root. **/ -static void change_user (const char *program) +static void +change_user (const char *program) { if (config.group && strlen (config.group) > 0) { int gid = get_id (config.group); @@ -234,6 +241,7 @@ static void change_user (const char *program) program, config.group); exit (EX_NOPERM); } + #ifdef HAVE_SETGROUPS /* Drop all supplementary groups, otherwise these are inherited from the calling process */ if (setgroups (0, NULL) < 0) { @@ -278,7 +286,7 @@ static void change_user (const char *program) static void initialize_config_defaults (struct config_s *conf) { - memset (conf, 0, sizeof (*conf)); + memset (conf, 0, sizeof(*conf)); conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf"); if (!conf->config_file) { @@ -298,6 +306,7 @@ static void initialize_config_defaults (struct config_s *conf) #endif conf->logf_name = NULL; conf->pidpath = NULL; + conf->maxclients = 100; } /** @@ -322,8 +331,11 @@ done: return ret; } -int main (int argc, char **argv) +int +main (int argc, char **argv) { + srand(time(NULL)); /* for hashmap seeds */ + /* Only allow u+rw bits. This may be required for some versions * of glibc so that mkstemp() doesn't make us vulnerable. */ @@ -331,7 +343,7 @@ int main (int argc, char **argv) log_message (LOG_INFO, "Initializing " PACKAGE " ..."); - if (config_compile_regex ()) { + if (config_compile_regex()) { exit (EX_SOFTWARE); } @@ -339,7 +351,8 @@ int main (int argc, char **argv) process_cmdline (argc, argv, &config_defaults); if (reload_config_file (config_defaults.config_file, - &config, &config_defaults)) { + &config, + &config_defaults)) { exit (EX_SOFTWARE); } @@ -356,8 +369,8 @@ int main (int argc, char **argv) if (config.godaemon == TRUE) { if (!config.syslog && config.logf_name == NULL) - fprintf (stderr, "WARNING: logging deactivated " - "(can't log to stdout when daemonized)\n"); + fprintf(stderr, "WARNING: logging deactivated " + "(can't log to stdout when daemonized)\n"); makedaemon (); } @@ -367,13 +380,14 @@ int main (int argc, char **argv) argv[0]); exit (EX_OSERR); } + #ifdef FILTER_ENABLE if (config.filter) filter_init (); #endif /* FILTER_ENABLE */ /* Start listening on the selected port. */ - if (child_listening_sockets (config.listen_addrs, config.port) < 0) { + if (child_listening_sockets(config.listen_addrs, config.port) < 0) { fprintf (stderr, "%s: Could not create listening sockets.\n", argv[0]); exit (EX_OSERR); @@ -400,13 +414,6 @@ int main (int argc, char **argv) exit (EX_SOFTWARE); } - if (child_pool_create () < 0) { - fprintf (stderr, - "%s: Could not create the pool of children.\n", - argv[0]); - exit (EX_SOFTWARE); - } - /* These signals are only for the parent process. */ log_message (LOG_INFO, "Setting the various signals."); @@ -444,6 +451,7 @@ int main (int argc, char **argv) "Could not remove PID file \"%s\": %s.", config.pidpath, strerror (errno)); } + #ifdef FILTER_ENABLE if (config.filter) filter_destroy (); diff --git a/src/network.c b/src/network.c index 84c7fb3..224f924 100644 --- a/src/network.c +++ b/src/network.c @@ -36,7 +36,7 @@ ssize_t safe_write (int fd, const void *buf, size_t count) { ssize_t len; size_t bytestosend; - const char *buffer = buf; + const char *buffer = buf; assert (fd >= 0); assert (buffer != NULL); @@ -204,7 +204,8 @@ ssize_t readline (int fd, char **whole_buffer) break; } - line_ptr->next = (struct read_lines_s *) + line_ptr->next = + (struct read_lines_s *) safecalloc (sizeof (struct read_lines_s), 1); if (!line_ptr->next) { ret = -ENOMEM; diff --git a/src/reqs.c b/src/reqs.c index f5a212a..4c6f535 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -49,6 +49,7 @@ #include "connect-ports.h" #include "conf.h" #include "basicauth.h" +#include "loop.h" /* * Maximum length of a HTTP line @@ -60,13 +61,13 @@ * enabled. */ #ifdef UPSTREAM_SUPPORT -#define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) -#define UPSTREAM_REQUEST(request) upstream_get(request, config.upstream_list) -#define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) +# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) +# define UPSTREAM_REQUEST(request) upstream_get(request, config.upstream_list) +# define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) #else -#define UPSTREAM_CONFIGURED() (0) -#define UPSTREAM_REQUEST(request) (request->host) -#define UPSTREAM_IS_HTTP(up) (0) +# define UPSTREAM_CONFIGURED() (0) +# define UPSTREAM_REQUEST(request) (request->host) +# define UPSTREAM_IS_HTTP(up) (0) #endif /* @@ -231,8 +232,8 @@ static int extract_url (const char *lurl, const char *surl, int default_port, /* Remove any surrounding '[' and ']' from IPv6 literals */ p = strrchr (request->host, ']'); if (p && (*(request->host) == '[')) { - memmove (request->host, request->host + 1, - strlen (request->host) - 2); + memmove(request->host, request->host + 1, + strlen(request->host) - 2); *p = '\0'; p--; *p = '\0'; @@ -257,7 +258,7 @@ static int establish_http_connection (struct conn_s *connptr, struct request_s *request) { char portbuff[7]; - char dst[sizeof (struct in6_addr)]; + char dst[sizeof(struct in6_addr)]; /* Build a port string if it's not a standard port */ if (request->port != HTTP_PORT && request->port != HTTP_PORT_SSL) @@ -265,7 +266,7 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) else portbuff[0] = '\0'; - if (inet_pton (AF_INET6, request->host, dst) > 0) { + if (inet_pton(AF_INET6, request->host, dst) > 0) { /* host is an IPv6 address literal, so surround it with * [] */ return write_message (connptr->server_fd, @@ -423,7 +424,8 @@ BAD_REQUEST_ERROR: /* Verify that the port in the CONNECT method is allowed */ if (!check_allowed_connect_ports (request->port, - config.connect_ports)) { + config.connect_ports)) + { indicate_http_error (connptr, 403, "Access violation", "detail", "The CONNECT method not allowed " @@ -541,8 +543,8 @@ static int pull_client_data (struct conn_s *connptr, long int length) */ ret = socket_nonblocking (connptr->client_fd); if (ret != 0) { - log_message (LOG_ERR, "Failed to set the client socket " - "to non-blocking: %s", strerror (errno)); + log_message(LOG_ERR, "Failed to set the client socket " + "to non-blocking: %s", strerror (errno)); goto ERROR_EXIT; } @@ -550,8 +552,8 @@ static int pull_client_data (struct conn_s *connptr, long int length) ret = socket_blocking (connptr->client_fd); if (ret != 0) { - log_message (LOG_ERR, "Failed to set the client socket " - "to blocking: %s", strerror (errno)); + log_message(LOG_ERR, "Failed to set the client socket " + "to blocking: %s", strerror (errno)); goto ERROR_EXIT; } @@ -564,8 +566,8 @@ static int pull_client_data (struct conn_s *connptr, long int length) bytes_read = read (connptr->client_fd, buffer, 2); if (bytes_read == -1) { log_message - (LOG_WARNING, - "Could not read two bytes from POST message"); + (LOG_WARNING, + "Could not read two bytes from POST message"); } } @@ -872,7 +874,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) * http proxy is in use.) */ if (connptr->server_fd == -1 || connptr->show_stats - || (connptr->connect_method && !UPSTREAM_IS_HTTP (connptr))) { + || (connptr->connect_method && ! UPSTREAM_IS_HTTP (connptr))) { log_message (LOG_INFO, "Not sending client headers to remote machine"); return 0; @@ -1091,8 +1093,8 @@ retry: while (reverse) { if (strncasecmp (header, reverse->url, (len = - strlen (reverse->url))) - == 0) + strlen (reverse-> + url))) == 0) break; reverse = reverse->next; } @@ -1163,15 +1165,15 @@ static void relay_connection (struct conn_s *connptr) ret = socket_nonblocking (connptr->client_fd); if (ret != 0) { - log_message (LOG_ERR, "Failed to set the client socket " - "to non-blocking: %s", strerror (errno)); + log_message(LOG_ERR, "Failed to set the client socket " + "to non-blocking: %s", strerror (errno)); return; } ret = socket_nonblocking (connptr->server_fd); if (ret != 0) { - log_message (LOG_ERR, "Failed to set the server socket " - "to non-blocking: %s", strerror (errno)); + log_message(LOG_ERR, "Failed to set the server socket " + "to non-blocking: %s", strerror (errno)); return; } @@ -1250,9 +1252,9 @@ static void relay_connection (struct conn_s *connptr) */ ret = socket_blocking (connptr->client_fd); if (ret != 0) { - log_message (LOG_ERR, - "Failed to set client socket to blocking: %s", - strerror (errno)); + log_message(LOG_ERR, + "Failed to set client socket to blocking: %s", + strerror (errno)); return; } @@ -1282,41 +1284,41 @@ static void relay_connection (struct conn_s *connptr) } static int -connect_to_upstream_proxy (struct conn_s *connptr, struct request_s *request) +connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) { - unsigned len; - unsigned char buff[512]; /* won't use more than 7 + 255 */ - unsigned short port; - size_t ulen, passlen; + unsigned len; + unsigned char buff[512]; /* won't use more than 7 + 255 */ + unsigned short port; + size_t ulen, passlen; - struct hostent *host; - struct upstream *cur_upstream = connptr->upstream_proxy; + struct hostent *host; + struct upstream *cur_upstream = connptr->upstream_proxy; - ulen = cur_upstream->ua.user ? strlen (cur_upstream->ua.user) : 0; - passlen = cur_upstream->pass ? strlen (cur_upstream->pass) : 0; + ulen = cur_upstream->ua.user ? strlen (cur_upstream->ua.user) : 0; + passlen = cur_upstream->pass ? strlen (cur_upstream->pass) : 0; - log_message (LOG_CONN, - "Established connection to %s proxy \"%s\" using file descriptor %d.", - proxy_type_name (cur_upstream->type), cur_upstream->host, - connptr->server_fd); + log_message (LOG_CONN, + "Established connection to %s proxy \"%s\" using file descriptor %d.", + proxy_type_name (cur_upstream->type), cur_upstream->host, + connptr->server_fd); - if (cur_upstream->type == PT_SOCKS4) { + if (cur_upstream->type == PT_SOCKS4) { - buff[0] = 4; /* socks version */ - buff[1] = 1; /* connect command */ - port = htons (request->port); - memcpy (&buff[2], &port, 2); /* dest port */ - host = gethostbyname (request->host); - memcpy (&buff[4], host->h_addr_list[0], 4); /* dest ip */ - buff[8] = 0; /* user */ - if (9 != safe_write (connptr->server_fd, buff, 9)) - return -1; - if (8 != safe_read (connptr->server_fd, buff, 8)) - return -1; - if (buff[0] != 0 || buff[1] != 90) - return -1; + buff[0] = 4; /* socks version */ + buff[1] = 1; /* connect command */ + port = htons (request->port); + memcpy (&buff[2], &port, 2); /* dest port */ + host = gethostbyname (request->host); + memcpy (&buff[4], host->h_addr_list[0], 4); /* dest ip */ + buff[8] = 0; /* user */ + if (9 != safe_write (connptr->server_fd, buff, 9)) + return -1; + if (8 != safe_read (connptr->server_fd, buff, 8)) + return -1; + if (buff[0] != 0 || buff[1] != 90) + return -1; - } else if (cur_upstream->type == PT_SOCKS5) { + } else if (cur_upstream->type == PT_SOCKS5) { /* init */ int n_methods = ulen ? 2 : 1; @@ -1518,7 +1520,8 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) #endif } -static int get_request_entity (struct conn_s *connptr) +static int +get_request_entity (struct conn_s *connptr) { int ret; fd_set rset; @@ -1533,9 +1536,9 @@ static int get_request_entity (struct conn_s *connptr) if (ret == -1) { log_message (LOG_ERR, "Error calling select on client fd %d: %s", - connptr->client_fd, strerror (errno)); + connptr->client_fd, strerror(errno)); } else if (ret == 0) { - log_message (LOG_INFO, "no entity"); + log_message (LOG_INFO, "no entity"); } else if (ret == 1 && FD_ISSET (connptr->client_fd, &rset)) { ssize_t nread; nread = read_buffer (connptr->client_fd, connptr->cbuffer); @@ -1546,7 +1549,8 @@ static int get_request_entity (struct conn_s *connptr) ret = -1; } else { log_message (LOG_INFO, - "Read request entity of %d bytes", nread); + "Read request entity of %d bytes", + nread); ret = 0; } } else { @@ -1568,7 +1572,7 @@ static int get_request_entity (struct conn_s *connptr) * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes */ -void handle_connection (int fd) +void handle_connection (int fd, union sockaddr_union* addr) { ssize_t i; struct conn_s *connptr; @@ -1577,26 +1581,39 @@ void handle_connection (int fd) char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; - char peer_string[HOSTNAME_LENGTH]; - getpeer_information (fd, peer_ipaddr, peer_string); + getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr)); if (config.bindsame) getsock_ip (fd, sock_ipaddr); log_message (LOG_CONN, config.bindsame ? - "Connect (file descriptor %d): %s [%s] at [%s]" : - "Connect (file descriptor %d): %s [%s]", - fd, peer_string, peer_ipaddr, sock_ipaddr); + "Connect (file descriptor %d): %s at [%s]" : + "Connect (file descriptor %d): %s", + fd, peer_ipaddr, sock_ipaddr); - connptr = initialize_conn (fd, peer_ipaddr, peer_string, + connptr = initialize_conn (fd, peer_ipaddr, config.bindsame ? sock_ipaddr : NULL); if (!connptr) { close (fd); return; } - if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) { + if (connection_loops (addr)) { + log_message (LOG_CONN, + "Prevented endless loop (file descriptor %d): %s", + fd, peer_ipaddr); + + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "You tried to connect to the " + "machine the proxy is running on", + NULL); + goto fail; + } + + + if (check_acl (peer_ipaddr, addr, config.access_list) <= 0) { update_stats (STAT_DENIED); indicate_http_error (connptr, 403, "Access denied", "detail", @@ -1647,44 +1664,34 @@ void handle_connection (int fd) ssize_t len; char *authstring; int failure = 1, stathost_connect = 0; - len = - hashmap_entry_by_key (hashofheaders, "proxy-authorization", - (void **) &authstring); + len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", + (void **) &authstring); if (len == 0 && config.stathost) { len = hashmap_entry_by_key (hashofheaders, "host", (void **) &authstring); - if (len - && !strncmp (authstring, config.stathost, - strlen (config.stathost))) { - len = - hashmap_entry_by_key (hashofheaders, - "authorization", - (void **) - &authstring); + if (len && !strncmp (authstring, config.stathost, strlen(config.stathost))) { + len = hashmap_entry_by_key (hashofheaders, "authorization", + (void **) &authstring); stathost_connect = 1; - } else - len = 0; + } else len = 0; } if (len == 0) { - if (stathost_connect) - goto e401; + if (stathost_connect) goto e401; update_stats (STAT_DENIED); - indicate_http_error (connptr, 407, - "Proxy Authentication Required", + indicate_http_error (connptr, 407, "Proxy Authentication Required", "detail", "This proxy requires authentication.", NULL); goto fail; } - if ( /* currently only "basic" auth supported */ - (strncmp (authstring, "Basic ", 6) == 0 || - strncmp (authstring, "basic ", 6) == 0) && - basicauth_check (config.basicauth_list, - authstring + 6) == 1) - failure = 0; - if (failure) { + if ( /* currently only "basic" auth supported */ + (strncmp (authstring, "Basic ", 6) == 0 || + strncmp (authstring, "basic ", 6) == 0) && + basicauth_check (config.basicauth_list, authstring + 6) == 1) + failure = 0; + if(failure) { e401: update_stats (STAT_DENIED); indicate_http_error (connptr, 401, "Unauthorized", @@ -1703,7 +1710,7 @@ e401: */ for (i = 0; i < vector_length (config.add_headers); i++) { http_header_t *header = (http_header_t *) - vector_getentry (config.add_headers, i, NULL); + vector_getentry (config.add_headers, i, NULL); hashmap_insert (hashofheaders, header->name, @@ -1749,7 +1756,7 @@ e401: goto fail; } - if (!connptr->connect_method || UPSTREAM_IS_HTTP (connptr)) { + if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) { if (process_server_headers (connptr) < 0) { update_stats (STAT_BADCONN); goto fail; @@ -1781,7 +1788,8 @@ fail: * to send our data properly. */ if (get_request_entity (connptr) < 0) { - log_message (LOG_WARNING, "Could not retrieve request entity"); + log_message (LOG_WARNING, + "Could not retrieve request entity"); indicate_http_error (connptr, 400, "Bad Request", "detail", "Could not retrieve the request entity " diff --git a/src/reqs.h b/src/reqs.h index f928d1b..c301f62 100644 --- a/src/reqs.h +++ b/src/reqs.h @@ -23,6 +23,7 @@ #define _TINYPROXY_REQS_H_ #include "common.h" +#include "sock.h" /* * Port constants for HTTP (80) and SSL (443) @@ -44,6 +45,6 @@ struct request_s { char *path; }; -extern void handle_connection (int fd); +extern void handle_connection (int fd, union sockaddr_union* addr); #endif diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 30bfbc5..0264787 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -139,10 +139,13 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, && (reverse = reversepath_get (cookieval + strlen (REVERSE_COOKIE) + 1, - config.reversepath_list))) { + config.reversepath_list))) + { rewrite_url = (char *) safemalloc - (strlen (url) + strlen (reverse->url) + 1); + (strlen (url) + + strlen (reverse->url) + + 1); strcpy (rewrite_url, reverse->url); strcat (rewrite_url, url + 1); diff --git a/src/sblist.c b/src/sblist.c new file mode 100644 index 0000000..4ddc4aa --- /dev/null +++ b/src/sblist.c @@ -0,0 +1,80 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#include "sblist.h" +#include +#include +#include +#define MY_PAGE_SIZE 4096 + +sblist* sblist_new(size_t itemsize, size_t blockitems) { + sblist* ret = (sblist*) malloc(sizeof(sblist)); + sblist_init(ret, itemsize, blockitems); + return ret; +} + +static void sblist_clear(sblist* l) { + l->items = NULL; + l->capa = 0; + l->count = 0; +} + +void sblist_init(sblist* l, size_t itemsize, size_t blockitems) { + if(l) { + l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize; + l->itemsize = itemsize; + sblist_clear(l); + } +} + +void sblist_free_items(sblist* l) { + if(l) { + if(l->items) free(l->items); + sblist_clear(l); + } +} + +void sblist_free(sblist* l) { + if(l) { + sblist_free_items(l); + free(l); + } +} + +char* sblist_item_from_index(sblist* l, size_t idx) { + return l->items + (idx * l->itemsize); +} + +void* sblist_get(sblist* l, size_t item) { + if(item < l->count) return (void*) sblist_item_from_index(l, item); + return NULL; +} + +int sblist_set(sblist* l, void* item, size_t pos) { + if(pos >= l->count) return 0; + memcpy(sblist_item_from_index(l, pos), item, l->itemsize); + return 1; +} + +int sblist_grow_if_needed(sblist* l) { + char* temp; + if(l->count == l->capa) { + temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize); + if(!temp) return 0; + l->capa += l->blockitems; + l->items = temp; + } + return 1; +} + +int sblist_add(sblist* l, void* item) { + if(!sblist_grow_if_needed(l)) return 0; + l->count++; + return sblist_set(l, item, l->count - 1); +} + +void sblist_delete(sblist* l, size_t item) { + if (l->count && item < l->count) { + memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize); + l->count--; + } +} diff --git a/src/sblist.h b/src/sblist.h new file mode 100644 index 0000000..02c33d7 --- /dev/null +++ b/src/sblist.h @@ -0,0 +1,92 @@ +#ifndef SBLIST_H +#define SBLIST_H + +/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd + modified for direct inclusion in tinyproxy, and for this purpose released under + the license of tinyproxy. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* + * simple buffer list. + * + * this thing here is basically a generic dynamic array + * will realloc after every blockitems inserts + * can store items of any size. + * + * so think of it as a by-value list, as opposed to a typical by-ref list. + * you typically use it by having some struct on the stack, and pass a pointer + * to sblist_add, which will copy the contents into its internal memory. + * + */ + +typedef struct { + size_t itemsize; + size_t blockitems; + size_t count; + size_t capa; + char* items; +} sblist; + +#define sblist_getsize(X) ((X)->count) +#define sblist_get_count(X) ((X)->count) +#define sblist_empty(X) ((X)->count == 0) + +/* for dynamic style */ +sblist* sblist_new(size_t itemsize, size_t blockitems); +void sblist_free(sblist* l); + +/*for static style*/ +void sblist_init(sblist* l, size_t itemsize, size_t blockitems); +void sblist_free_items(sblist* l); + +/* accessors */ +void* sblist_get(sblist* l, size_t item); +/* returns 1 on success, 0 on OOM */ +int sblist_add(sblist* l, void* item); +int sblist_set(sblist* l, void* item, size_t pos); +void sblist_delete(sblist* l, size_t item); +char* sblist_item_from_index(sblist* l, size_t idx); +int sblist_grow_if_needed(sblist* l); +int sblist_insert(sblist* l, void* item, size_t pos); +/* same as sblist_add, but returns list index of new item, or -1 */ +size_t sblist_addi(sblist* l, void* item); +void sblist_sort(sblist *l, int (*compar)(const void *, const void *)); +/* insert element into presorted list, returns listindex of new entry or -1*/ +size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *)); + +#ifndef __COUNTER__ +#define __COUNTER__ __LINE__ +#endif + +#define __sblist_concat_impl( x, y ) x##y +#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y ) +#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__) + +/* use with custom iterator variable */ +#define sblist_iter_counter(LIST, ITER, PTR) \ + for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared */ +#define sblist_iter_counter2(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared and signed */ +/* useful for a loop which can delete items from the list, and then decrease the iterator var. */ +#define sblist_iter_counter2s(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++) + + +/* uses "magic" iterator variable */ +#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/sock.c b/src/sock.c index e3acb74..8513ba8 100644 --- a/src/sock.c +++ b/src/sock.c @@ -33,11 +33,12 @@ #include "sock.h" #include "text.h" #include "conf.h" +#include "loop.h" /* * Return a human readable error for getaddrinfo() and getnameinfo(). */ -static const char *get_gai_error (int n) +static const char * get_gai_error (int n) { if (n == EAI_SYSTEM) return strerror (errno); @@ -50,7 +51,8 @@ static const char *get_gai_error (int n) * returned if the bind succeeded. Otherwise, -1 is returned * to indicate an error. */ -static int bind_socket (int sockfd, const char *addr, int family) +static int +bind_socket (int sockfd, const char *addr, int family) { struct addrinfo hints, *res, *ressave; int n; @@ -66,8 +68,7 @@ static int bind_socket (int sockfd, const char *addr, int family) n = getaddrinfo (addr, NULL, &hints, &res); if (n != 0) { log_message (LOG_INFO, - "bind_socket: getaddrinfo failed for %s: ", addr, - get_gai_error (n)); + "bind_socket: getaddrinfo failed for %s: ", addr, get_gai_error (n)); return -1; } @@ -100,8 +101,8 @@ int opensock (const char *host, int port, const char *bind_to) assert (host != NULL); assert (port > 0); - log_message (LOG_INFO, - "opensock: opening connection to %s:%d", host, port); + log_message(LOG_INFO, + "opensock: opening connection to %s:%d", host, port); memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -112,13 +113,12 @@ int opensock (const char *host, int port, const char *bind_to) n = getaddrinfo (host, portstr, &hints, &res); if (n != 0) { log_message (LOG_ERR, - "opensock: Could not retrieve address info for %s:%d: %s", - host, port, get_gai_error (n)); + "opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n)); return -1; } - log_message (LOG_INFO, - "opensock: getaddrinfo returned for %s:%d", host, port); + log_message(LOG_INFO, + "opensock: getaddrinfo returned for %s:%d", host, port); ressave = res; do { @@ -129,7 +129,8 @@ int opensock (const char *host, int port, const char *bind_to) /* Bind to the specified address */ if (bind_to) { - if (bind_socket (sockfd, bind_to, res->ai_family) < 0) { + if (bind_socket (sockfd, bind_to, + res->ai_family) < 0) { close (sockfd); continue; /* can't bind, so try again */ } @@ -141,8 +142,17 @@ int opensock (const char *host, int port, const char *bind_to) } } - if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) + if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) { + union sockaddr_union *p = (void*) res->ai_addr, u; + int af = res->ai_addr->sa_family; + unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port); + socklen_t slen = sizeof u; + if (dport == config.port) { + getsockname(sockfd, (void*)&u, &slen); + loop_records_add(&u); + } break; /* success */ + } close (sockfd); } while ((res = res->ai_next) != NULL); @@ -151,7 +161,8 @@ int opensock (const char *host, int port, const char *bind_to) if (res == NULL) { log_message (LOG_ERR, "opensock: Could not establish a connection to %s:%d", - host, port); + host, + port); return -1; } @@ -184,13 +195,14 @@ int socket_blocking (int sock) return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK); } + /** * Try to listen on one socket based on the addrinfo * as returned from getaddrinfo. * * Return the file descriptor upon success, -1 upon error. */ -static int listen_on_one_socket (struct addrinfo *ad) +static int listen_on_one_socket(struct addrinfo *ad) { int listenfd; int ret; @@ -198,60 +210,59 @@ static int listen_on_one_socket (struct addrinfo *ad) char numerichost[NI_MAXHOST]; int flags = NI_NUMERICHOST; - ret = getnameinfo (ad->ai_addr, ad->ai_addrlen, - numerichost, NI_MAXHOST, NULL, 0, flags); + ret = getnameinfo(ad->ai_addr, ad->ai_addrlen, + numerichost, NI_MAXHOST, NULL, 0, flags); if (ret != 0) { - log_message (LOG_ERR, "getnameinfo failed: %s", - get_gai_error (ret)); + log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret)); return -1; } - log_message (LOG_INFO, "trying to listen on host[%s], family[%d], " - "socktype[%d], proto[%d]", numerichost, - ad->ai_family, ad->ai_socktype, ad->ai_protocol); + log_message(LOG_INFO, "trying to listen on host[%s], family[%d], " + "socktype[%d], proto[%d]", numerichost, + ad->ai_family, ad->ai_socktype, ad->ai_protocol); - listenfd = socket (ad->ai_family, ad->ai_socktype, ad->ai_protocol); + listenfd = socket(ad->ai_family, ad->ai_socktype, ad->ai_protocol); if (listenfd == -1) { - log_message (LOG_ERR, "socket() failed: %s", strerror (errno)); + log_message(LOG_ERR, "socket() failed: %s", strerror(errno)); return -1; } - ret = setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)); + ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret != 0) { - log_message (LOG_ERR, - "setsockopt failed to set SO_REUSEADDR: %s", - strerror (errno)); - close (listenfd); + log_message(LOG_ERR, + "setsockopt failed to set SO_REUSEADDR: %s", + strerror(errno)); + close(listenfd); return -1; } if (ad->ai_family == AF_INET6) { - ret = setsockopt (listenfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, - sizeof (on)); + ret = setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, + sizeof(on)); if (ret != 0) { - log_message (LOG_ERR, - "setsockopt failed to set IPV6_V6ONLY: %s", - strerror (errno)); - close (listenfd); + log_message(LOG_ERR, + "setsockopt failed to set IPV6_V6ONLY: %s", + strerror(errno)); + close(listenfd); return -1; } } - ret = bind (listenfd, ad->ai_addr, ad->ai_addrlen); + ret = bind(listenfd, ad->ai_addr, ad->ai_addrlen); if (ret != 0) { - log_message (LOG_ERR, "bind failed: %s", strerror (errno)); - close (listenfd); + log_message(LOG_ERR, "bind failed: %s", strerror (errno)); + close(listenfd); + return -1; + } + + ret = listen(listenfd, MAXLISTEN); + if (ret != 0) { + log_message(LOG_ERR, "listen failed: %s", strerror(errno)); + close(listenfd); return -1; } - ret = listen (listenfd, MAXLISTEN); - if (ret != 0) { - log_message (LOG_ERR, "listen failed: %s", strerror (errno)); - close (listenfd); - return -1; - } - - log_message (LOG_INFO, "listening on fd [%d]", listenfd); + log_message(LOG_INFO, "listening on fd [%d]", listenfd); return listenfd; } @@ -276,8 +287,8 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) assert (port > 0); assert (listen_fds != NULL); - log_message (LOG_INFO, "listen_sock called with addr = '%s'", - addr == NULL ? "(NULL)" : addr); + log_message(LOG_INFO, "listen_sock called with addr = '%s'", + addr == NULL ? "(NULL)" : addr); memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -290,19 +301,21 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) if (n != 0) { log_message (LOG_ERR, "Unable to getaddrinfo() for %s:%d because of %s", - addr, port, get_gai_error (n)); + addr, + port, + get_gai_error (n)); return -1; } for (rp = result; rp != NULL; rp = rp->ai_next) { int listenfd; - listenfd = listen_on_one_socket (rp); + listenfd = listen_on_one_socket(rp); if (listenfd == -1) { continue; } - vector_append (listen_fds, &listenfd, sizeof (int)); + vector_append (listen_fds, &listenfd, sizeof(int)); /* success */ ret = 0; @@ -351,27 +364,9 @@ int getsock_ip (int fd, char *ipaddr) /* * Return the peer's socket information. */ -int getpeer_information (int fd, char *ipaddr, char *string_addr) +void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len) { - struct sockaddr_storage sa; - socklen_t salen = sizeof sa; - - assert (fd >= 0); - assert (ipaddr != NULL); - assert (string_addr != NULL); - - /* Set the strings to default values */ - ipaddr[0] = '\0'; - strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH); - - /* Look up the IP address */ - if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0) - return -1; - - if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL) - return -1; - - /* Get the full host name */ - return getnameinfo ((struct sockaddr *) &sa, salen, - string_addr, HOSTNAME_LENGTH, NULL, 0, 0); + int af = addr->v4.sin_family; + void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr; + inet_ntop(af, ipdata, ipaddr, ipaddr_len); } diff --git a/src/sock.h b/src/sock.h index f1225ea..033e179 100644 --- a/src/sock.h +++ b/src/sock.h @@ -28,8 +28,14 @@ #define MAXLINE (1024 * 4) +#include "common.h" #include "vector.h" +union sockaddr_union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; +}; + extern int opensock (const char *host, int port, const char *bind_to); extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds); @@ -37,6 +43,6 @@ extern int socket_nonblocking (int sock); extern int socket_blocking (int sock); extern int getsock_ip (int fd, char *ipaddr); -extern int getpeer_information (int fd, char *ipaddr, char *string_addr); +extern void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len); #endif diff --git a/src/stats.c b/src/stats.c index 2e577b4..93a30cf 100644 --- a/src/stats.c +++ b/src/stats.c @@ -33,6 +33,7 @@ #include "stats.h" #include "utils.h" #include "conf.h" +#include struct stat_s { unsigned long int num_reqs; @@ -43,14 +44,16 @@ struct stat_s { }; static struct stat_s *stats; +static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER; /* * Initialize the statistics information to zero. */ void init_stats (void) { - stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s)); - if (stats == MAP_FAILED) + stats = (struct stat_s *) safemalloc (sizeof (struct stat_s)); + if (!stats) return; memset (stats, 0, sizeof (struct stat_s)); @@ -59,7 +62,8 @@ void init_stats (void) /* * Display the statics of the tinyproxy server. */ -int showstats (struct conn_s *connptr) +int +showstats (struct conn_s *connptr) { char *message_buffer; char opens[16], reqs[16], badconns[16], denied[16], refused[16]; @@ -71,45 +75,51 @@ int showstats (struct conn_s *connptr) snprintf (denied, sizeof (denied), "%lu", stats->num_denied); snprintf (refused, sizeof (refused), "%lu", stats->num_refused); + pthread_mutex_lock(&stats_file_lock); + if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) { message_buffer = (char *) safemalloc (MAXBUFFSIZE); - if (!message_buffer) - return -1; - - snprintf - (message_buffer, MAXBUFFSIZE, - "\n" - "\n" - "\n" - "%s version %s run-time statistics\n" - "\n" - "

%s version %s run-time statistics

\n" - "

\n" - "Number of open connections: %lu
\n" - "Number of requests: %lu
\n" - "Number of bad connections: %lu
\n" - "Number of denied connections: %lu
\n" - "Number of refused connections due to high load: %lu\n" - "

\n" - "
\n" - "

Generated by %s version %s.

\n" "\n" - "\n", - PACKAGE, VERSION, PACKAGE, VERSION, - stats->num_open, - stats->num_reqs, - stats->num_badcons, stats->num_denied, - stats->num_refused, PACKAGE, VERSION); - - if (send_http_message (connptr, 200, "OK", message_buffer) < 0) { - safefree (message_buffer); + if (!message_buffer) { +err_minus_one: + pthread_mutex_unlock(&stats_file_lock); return -1; } + snprintf + (message_buffer, MAXBUFFSIZE, + "\n" + "\n" + "\n" + "%s version %s run-time statistics\n" + "\n" + "

%s version %s run-time statistics

\n" + "

\n" + "Number of open connections: %lu
\n" + "Number of requests: %lu
\n" + "Number of bad connections: %lu
\n" + "Number of denied connections: %lu
\n" + "Number of refused connections due to high load: %lu\n" + "

\n" + "
\n" + "

Generated by %s version %s.

\n" "\n" + "\n", + PACKAGE, VERSION, PACKAGE, VERSION, + stats->num_open, + stats->num_reqs, + stats->num_badcons, stats->num_denied, + stats->num_refused, PACKAGE, VERSION); + + if (send_http_message (connptr, 200, "OK", + message_buffer) < 0) { + safefree (message_buffer); + goto err_minus_one; + } + safefree (message_buffer); + pthread_mutex_unlock(&stats_file_lock); return 0; } - add_error_variable (connptr, "opens", opens); add_error_variable (connptr, "reqs", reqs); add_error_variable (connptr, "badconns", badconns); @@ -119,6 +129,7 @@ int showstats (struct conn_s *connptr) send_http_headers (connptr, 200, "Statistic requested"); send_html_file (statfile, connptr); fclose (statfile); + pthread_mutex_unlock(&stats_file_lock); return 0; } @@ -129,6 +140,9 @@ int showstats (struct conn_s *connptr) */ int update_stats (status_t update_level) { + int ret = 0; + + pthread_mutex_lock(&stats_update_lock); switch (update_level) { case STAT_BADCONN: ++stats->num_badcons; @@ -147,8 +161,9 @@ int update_stats (status_t update_level) ++stats->num_denied; break; default: - return -1; + ret = -1; } + pthread_mutex_unlock(&stats_update_lock); - return 0; + return ret; } diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index f6e3176..df5fbce 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -111,21 +111,21 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, return 1; } - for (i = 0; i < vector_length (conf->listen_addrs); i++) { + for (i = 0; i < vector_length(conf->listen_addrs); i++) { const char *addr; - addr = (char *) vector_getentry (conf->listen_addrs, i, NULL); + addr = (char *)vector_getentry(conf->listen_addrs, i, NULL); - if (addr && strcmp (request->host, addr) == 0) { - log_message (LOG_ERR, - "transparent: destination IP %s is local " - "on socket fd %d", - request->host, connptr->client_fd); - indicate_http_error (connptr, 400, "Bad Request", - "detail", - "You tried to connect to the " - "machine the proxy is running on", - "url", *url, NULL); + if (addr && strcmp(request->host, addr) == 0) { + log_message(LOG_ERR, + "transparent: destination IP %s is local " + "on socket fd %d", + request->host, connptr->client_fd); + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "You tried to connect to the " + "machine the proxy is running on", + "url", *url, NULL); return 0; } } diff --git a/src/upstream.c b/src/upstream.c index 55092c4..81573f4 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -31,7 +31,7 @@ #include "basicauth.h" #ifdef UPSTREAM_SUPPORT -static const char *proxy_list_name (struct upstream_proxy_list *plist) +static const char *proxy_list_string (struct upstream_proxy_list *plist) { #define MAXBUF ((size_t)(1024 * 96)) static char hostport[MAXBUF]; @@ -50,6 +50,100 @@ static const char *proxy_list_name (struct upstream_proxy_list *plist) return pbuffer; } +/* return 1 if IP string is valid, else return 0 */ +static int is_valid_ip (const char *str) +{ + int num, ret = 0, dots = 0; + char *ptr, *ip_str; + + if (str == NULL) + return 0; + + ip_str = safestrdup (str); + ptr = strtok (ip_str, "."); + + if (ptr == NULL) + goto cleanup; + + while (ptr) { + char *dptr = ptr; + + /* after parsing string, it must contain only digits */ + while (*dptr) { + if (*dptr >= '0' && *dptr <= '9') + ++dptr; + else + goto cleanup; + } + + num = atoi (ptr); + + /* check for valid IP */ + if (num >= 0 && num <= 255) { + /* parse remaining string */ + ptr = strtok (NULL, "."); + if (ptr != NULL) + ++dots; + } else + goto cleanup; + } + + /* valid IP string must contain 3 dots */ + if (dots != 3) + goto cleanup; + + ret = 1; +cleanup: + safefree (ip_str); + return ret; +} + +static char *get_hostip (int *lookup_err, char *host, in_addr_t ip, + in_addr_t mask) +{ + char *hostip; + + hostip = host; + + if (!is_valid_ip (host)) { /* resolve host and check ip */ + int ret; + struct addrinfo *res, *ressave; + + res = NULL; + ret = getaddrinfo (host, NULL, NULL, &res); + ressave = res; + if (ret != 0) { + *lookup_err = ret; + if (ret == EAI_SYSTEM) + log_message (LOG_ERR, + "get_hostip: Could not retrieve address info for %s: %s", + host, strerror (errno)); + else + log_message (LOG_ERR, + "get_hostip: Could not retrieve address info for %s: %s", + host, gai_strerror (ret)); + } else { + do { + struct in_addr tmp; + struct sockaddr_in *stmp; + stmp = (struct sockaddr_in *) (res->ai_addr); + tmp = stmp->sin_addr; + + if ((ntohl (inet_addr (inet_ntoa (tmp))) & mask) + == ip) { + /* return if IP matches */ + hostip = inet_ntoa (tmp); + break; + } + + } while ((res = res->ai_next) != NULL); + } + if (ressave) + freeaddrinfo (ressave); + } + return safestrdup (hostip); +} + const char *proxy_type_name (proxy_type type) { switch (type) { @@ -140,7 +234,7 @@ static struct upstream *upstream_build (const struct upstream_proxy_list *plist, !strncasecmp (domain, "regexei(", 8)))) { /* extended regex case insenstive */ rflag = 1; rptr = domain + 8; - cflags |= REG_ICASE; + cflags |= REG_EXTENDED; cflags |= REG_ICASE; } if (rflag) { @@ -182,7 +276,7 @@ static struct upstream *upstream_build (const struct upstream_proxy_list *plist, log_message (LOG_INFO, "Added upstream %s %s for [default]", proxy_type_name (type), - proxy_list_name (up->plist)); + proxy_list_string (up->plist)); } else if (plist == NULL || type == PT_NONE) { if (!domain || domain[0] == '\0') { log_message (LOG_WARNING, @@ -197,31 +291,46 @@ static struct upstream *upstream_build (const struct upstream_proxy_list *plist, struct in_addr addrstruct; *ptr = '\0'; - if (inet_aton (domain, &addrstruct) != 0) { - up->ip = ntohl (addrstruct.s_addr); - *ptr++ = '/'; + if (is_valid_ip (domain)) { + if (inet_aton (domain, &addrstruct) != + 0) { + up->ip = + ntohl (addrstruct.s_addr); + *ptr++ = '/'; - if (strchr (ptr, '.')) { - if (inet_aton (ptr, &addrstruct) - != 0) + if (is_valid_ip (ptr)) { + if (inet_aton + (ptr, &addrstruct) + != 0) + up->mask = + ntohl + (addrstruct. + s_addr); + } else if (atoi (ptr) < 33 + && atoi (ptr) > -1) { up->mask = - ntohl - (addrstruct.s_addr); - } else { - up->mask = - ~((1 << (32 - atoi (ptr))) - - 1); + ~((1 << + (32 - + atoi (ptr))) - + 1); + } else { + up->domain = + safestrdup (domain); + } } + } else { + *ptr++ = '/'; + up->domain = safestrdup (domain); } } else { up->domain = safestrdup (domain); } #ifdef UPSTREAM_REGEX } else { - int err = 0; + int ret = 0; up->cpat = (regex_t *) safemalloc (sizeof (regex_t)); - err = regcomp (up->cpat, up->pat, cflags); - if (err != 0) { + ret = regcomp (up->cpat, up->pat, cflags); + if (ret != 0) { log_message (LOG_WARNING, "Bad regex: %s", up->pat); goto fail; @@ -242,10 +351,10 @@ static struct upstream *upstream_build (const struct upstream_proxy_list *plist, up->domain = safestrdup (domain); #ifdef UPSTREAM_REGEX if (rflag) { - int err = 0; + int ret = 0; up->cpat = (regex_t *) safemalloc (sizeof (regex_t)); - err = regcomp (up->cpat, up->pat, cflags); - if (err != 0) { + ret = regcomp (up->cpat, up->pat, cflags); + if (ret != 0) { log_message (LOG_WARNING, "Bad regex: %s", up->pat); goto fail; @@ -254,7 +363,7 @@ static struct upstream *upstream_build (const struct upstream_proxy_list *plist, #endif log_message (LOG_INFO, "Added upstream %s %s for %s", proxy_type_name (type), - proxy_list_name (up->plist), domain); + proxy_list_string (up->plist), domain); } return up; @@ -285,14 +394,16 @@ fail: /* * Add an entry to the upstream list */ -void upstream_add (const struct upstream_proxy_list *plist, const char *domain, - const char *user, const char *pass, - proxy_type type, struct upstream **upstream_list) +void upstream_add (const struct upstream_proxy_list *plist, + const char *domain, const char *user, + const char *pass, proxy_type type, + struct upstream **upstream_list) { struct upstream *up; struct upstream_proxy_list *upp; up = upstream_build (plist, domain, user, pass, type); + if (up == NULL) { return; } @@ -351,10 +462,30 @@ upstream_cleanup: struct upstream *upstream_get (struct request_s *request, struct upstream *up) { char *host = request->host; + int lookup_err; in_addr_t my_ip = INADDR_NONE; + lookup_err = 0; + DEBUG2 ("Given url %s", request->url ? request->url : "NULL"); + DEBUG2 ("Given host %s", request->host); while (up) { + DEBUG2 ("Upstream type: %s\n", proxy_type_name (up->type)); +#ifdef UPSTREAM_REGEX + DEBUG2 (, "Check against pattern: %s\n", + up->pat ? up->pat : "NULL"); +#endif + DEBUG2 (, "Check against domain: %s\n", + up->domain ? up->domain : "NULL"); + if (up->ip && up->mask) { + struct in_addr tmp1, tmp2; + tmp1.s_addr = htonl (up->ip); + tmp2.s_addr = htonl (up->mask); + DEBUG2 ("Check against ip/mask: %s/%s\n", + inet_ntoa (tmp1), inet_ntoa (tmp2)); + } else { + DEBUG2 ("Check against ip/mask: NO\n"); + } #ifdef UPSTREAM_REGEX if (up->cpat) { int result; @@ -362,40 +493,69 @@ struct upstream *upstream_get (struct request_s *request, struct upstream *up) result = regexec (up->cpat, url, (size_t) 0, (regmatch_t *) 0, 0); - + if (up->type == PT_NONE && result == 0) { + up = NULL; + break; + } if (result == 0) break; /* regex match */ } else if (up->domain) { #else if (up->domain) { #endif - if (strcasecmp (host, up->domain) == 0) + if (strcasecmp (host, up->domain) == 0) { + if (up->type == PT_NONE) + up = NULL; break; /* exact match */ + } if (up->domain[0] == '.') { char *dot = strchr (host, '.'); - if (!dot && !up->domain[1]) + if (!dot && !up->domain[1]) { + if (up->type == PT_NONE) + up = NULL; break; /* local host matches "." */ + } while (dot && strcasecmp (dot, up->domain)) dot = strchr (dot + 1, '.'); - if (dot) + if (dot) { + if (up->type == PT_NONE) + up = NULL; break; /* subdomain match */ + } } - } else if (up->ip) { - if (my_ip == INADDR_NONE) - my_ip = ntohl (inet_addr (host)); + } else if (up->ip && up->mask) { + char *hostip = NULL; - if ((my_ip & up->mask) == up->ip) - break; + if (!lookup_err) { + hostip = + get_hostip (&lookup_err, host, up->ip, + up->mask); + + if (is_valid_ip (hostip)) { + if (my_ip == INADDR_NONE) + my_ip = + ntohl (inet_addr (hostip)); + + if ((my_ip & up->mask) == up->ip) { + if (up->type == PT_NONE) + up = NULL; + safefree (hostip); + break; + } + } + safefree (hostip); + } } else { break; /* No domain or IP, default upstream */ } up = up->next; } + if (up && !up->plist) up = NULL; @@ -403,7 +563,7 @@ struct upstream *upstream_get (struct request_s *request, struct upstream *up) log_message (LOG_INFO, "Found upstream proxy/proxies %s %s for %s", proxy_type_name (up->type), - proxy_list_name (up->plist), host); + proxy_list_string (up->plist), host); else log_message (LOG_INFO, "No upstream proxy for %s", host); diff --git a/src/utils.c b/src/utils.c index bb7ea94..ef2e673 100644 --- a/src/utils.c +++ b/src/utils.c @@ -185,7 +185,8 @@ int create_file_safely (const char *filename, unsigned int truncate_file) * * Returns: %0 on success, non-zero values on errors. **/ -int pidfile_create (const char *filename) +int +pidfile_create (const char *filename) { int fildes; FILE *fd; diff --git a/src/vector.c b/src/vector.c index 18f42c6..cf9fc75 100644 --- a/src/vector.c +++ b/src/vector.c @@ -111,7 +111,10 @@ typedef enum { } vector_pos_t; static int -vector_insert (vector_t vector, void *data, size_t len, vector_pos_t pos) +vector_insert (vector_t vector, + void *data, + size_t len, + vector_pos_t pos) { struct vectorentry_s *entry; diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index dd95400..6f797ef 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -84,10 +84,6 @@ Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log" PidFile "$TINYPROXY_PID_FILE" LogLevel Info MaxClients 100 -MinSpareServers 5 -MaxSpareServers 20 -StartServers 10 -MaxRequestsPerChild 0 Allow 127.0.0.0/8 ViaProxyName "tinyproxy" #DisableViaHeader Yes