Obtained from: ftp://ftp.linux.org.uk/pub/linux/rmk/ijb20.rmk.diff --- bind.c.orig Fri Nov 30 11:25:23 2001 +++ bind.c Fri Nov 30 11:33:46 2001 @@ -45,6 +45,242 @@ long remote_ip_long; char *remote_ip_str; +#ifdef HAVE_IPV6 +#ifdef HAVE_POLL +/* + * Do we have the superiour poll() interface? + */ +#include + +static struct pollfd *b_pfd; +static int nr_fds; +#else +/* + * Argh, we've only got the select() interface. + */ +#include + +static fd_set sfd; +static int max_fd; +#endif + +static int add_fd(fd) + int fd; +{ +#ifdef HAVE_POLL + struct pollfd *n; + int nr = nr_fds + 1; + + n = realloc(b_pfd, nr * sizeof(*n)); + if (!n) + return -3; + + n[nr_fds].fd = fd; + n[nr_fds].events = POLLIN; + + b_pfd = n; + nr_fds = nr; + + return 0; +#else + if (fd >= max_fd) + max_fd = fd + 1; + FD_SET(fd, &sfd); + return 0; +#endif +} + +/* + * Bind one port of an address family, specified by `ai' + */ +static int bind_one(ai) + struct addrinfo *ai; +{ + int fd, one = 1; + + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) { + /* + * Is it an unsupported family or protocol? + * Move along please. + */ + if (errno == EINVAL || errno == EPROTONOSUPPORT) + return -1; + + /* + * Ok, something else went wrong - fatal error. + */ + return -3; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)); + + /* + * Now bind the socket. This may fail on Linux. + */ + if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) { + close(fd); + + if (errno == EADDRINUSE) + return -2; + else + return -1; + } + + /* + * and ensure that it is listening. + */ + while (listen(fd, 5) == -1) { + if (errno != EINTR) { + close(fd); + return -1; + } + } + + return add_fd(fd); +} + +/* + * BIND-PORT (portnum) + * if success, return file descriptor + * if failure, returns -2 if address is in use, otherwise -1 + */ +int bind_port (hostnam, portnum) +char *hostnam; +int portnum; +{ + struct addrinfo *ai, *aip, aihint; + int rc, nr = 0; + char serv[NI_MAXSERV]; + + if (snprintf(serv, NI_MAXSERV, "%d", portnum) >= NI_MAXSERV) + return -1; + + memset(&aihint, 0, sizeof(aihint)); + + aihint.ai_flags = AI_PASSIVE; + aihint.ai_family = PF_UNSPEC; + aihint.ai_socktype = SOCK_STREAM; + + rc = getaddrinfo(hostnam, serv, &aihint, &ai); + if (rc) + return -1; + + /* + * Go through each entry creating a socket and trying + * to bind it. Note that on Linux, if we bind to an + * IPv6 address, we can't bind to it's corresponding + * IPv4 address, so we bind IPv6 first, then IPv4. + * + * We classify success as being able to establish at + * least one listening socket. + */ + for (aip = ai; aip; aip = aip->ai_next) { + if (aip->ai_family == PF_INET6) { + rc = bind_one(aip); + if (rc == 0) + nr++; + } + } + + for (aip = ai; aip; aip = aip->ai_next) { + if (aip->ai_family == PF_INET) { + rc = bind_one(aip); + if (rc == 0) + nr++; + } + } + + freeaddrinfo(ai); + + if (nr != 0) + rc = 0; + + return rc; +} + +/* + * ACCEPT-CONNECTION + * the argument, fd, is the value returned from bind_port + * + * when a connection is accepted, it returns the file descriptor + * for the connected port + */ +int accept_connection (_fd) +int _fd; +{ + struct sockaddr_storage sa; + int afd, alen = sizeof(sa); + char host[NI_MAXHOST]; + int rc, fd; + +#ifdef HAVE_POLL + int i; + + do { + rc = poll(b_pfd, nr_fds, -1); + } while (rc == 0 || (rc == -1 && errno == EINTR)); + + /* + * I wish we could spawn the handler here. Alas, without + * rewriting more of ijb... + */ + for (i = 0; i < nr_fds; i++) + if (b_pfd[i].revents) + break; + + /* + * hmm, if we ran out of fds to check, someone lied to us. + */ + if (i >= nr_fds) + return -1; + + fd = b_pfd[i].fd; +#else + fd_set rfds; + + rfds = sfd; + do { + rc = select(max_fd, &rfds, NULL, NULL, NULL); + } while (rc == 0 || (rc == -1 && errno == EINTR)); + + /* + * Find the first fd. Same comment as above. + */ + for (fd = 0; fd < max_fd; fd++) + if (FD_ISSET(fd, &rfds)) + break; + + /* + * If we found no fds, someone lied to us. + */ + if (fd >= max_fd) + return -1; + +#endif + afd = accept(fd, (struct sockaddr *)&sa, &alen); + if (afd < 0) + return -1; + + if (getnameinfo((struct sockaddr *)&sa, alen, + host, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST)) + strcpy(host, "unknown"); + + remote_ip_str = strdup(host); + remote_ip_long = 0; + + return afd; +} +#else +/* + * -------------------------------- IPv4 ---------------------------- + */ + +extern int atoip(); + + /* * BIND-PORT (portnum) * if success, return file descriptor @@ -100,7 +336,6 @@ return fd; } - /* * ACCEPT-CONNECTION * the argument, fd, is the value returned from bind_port @@ -128,3 +363,5 @@ return afd; } +#endif + --- conn.c.orig Fri Nov 30 11:25:23 2001 +++ conn.c Fri Nov 30 11:30:30 2001 @@ -41,6 +41,10 @@ #include "gnuregex.h" #endif +#ifdef HAVE_POLL +#include +#endif + #include "jcc.h" int @@ -82,14 +86,127 @@ return(inaddr.sin_addr.s_addr); } +#ifdef HAVE_IPV6 +int connect_to(char *host, int portnum, struct client_state *csp) +{ + struct addrinfo *ai, *aip, aihint; + int fd = -1, rc; + char serv[NI_MAXSERV]; + + if (snprintf(serv, NI_MAXSERV, "%d", portnum) >= NI_MAXSERV) { + errno = EOVERFLOW; + return -1; + } + + memset(&aihint, 0, sizeof(aihint)); + + aihint.ai_family = PF_UNSPEC; + aihint.ai_socktype = SOCK_STREAM; + + rc = getaddrinfo(host, serv, &aihint, &ai); + if (rc) + return -1; + + /* + * Go through each entry trying to connect to the host. + */ + for (aip = ai; aip; aip = aip->ai_next) { + int flags; + + fd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (fd == -1) + continue; + +#ifdef TCP_NODELAY /* turn off TCP coalescence */ + { + int mi = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&mi, sizeof(mi)); + } +#endif +#if !defined(_WIN32) && !defined(__BEOS__) + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + fcntl(fd, F_SETFL, flags | O_NDELAY); +#endif + do { + rc = connect(fd, aip->ai_addr, aip->ai_addrlen); + } while (rc == -1 && errno == EINTR); + + if (rc == -1 && errno != EINPROGRESS) { + close(fd); + fd = -1; + continue; + } + + /* + * Ok, the connection is in progress. + */ +#if !defined(_WIN32) && !defined(__BEOS__) + if (flags != -1) + fcntl(fd, F_SETFL, flags); +#endif + { +#ifdef HAVE_POLL + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLOUT | POLLERR | POLLHUP; + + if (poll(&pfd, 1, 30000) <= 0) { + close(fd); + fd = -1; + continue; + } + + if (pfd.revents & (POLLERR|POLLHUP)) { + close(fd); + fd = -1; + continue; + } +#else + fd_set rfds, wfds; + struct timeval tv[1]; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_SET(fd, &rfds); + FD_SET(fd, &wfds); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + if (select(fd + 1, &rfds, &wfds, NULL, tv) <= 0) { + (void) close(fd); + fd = -1; + continue; + } + + if (FD_ISSET(fd, &rfds) && FD_ISSET(fd, &wfds)) { + int r = 0, l = sizeof(r); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &r, &l) + || r) { + (void) close(fd); + fd = -1; + continue; + } + } +#endif + } + break; + } + + freeaddrinfo(ai); + return fd; +} + +#else int connect_to(char *host, int portnum, struct client_state *csp) { struct sockaddr_in inaddr; int fd, addr; - fd_set wfds; - struct timeval tv[1]; int flags; struct access_control_addr src[1], dst[1]; @@ -122,23 +239,19 @@ } #ifdef TCP_NODELAY -{ /* turn off TCP coalescence */ - int mi = 1; - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char * ) &mi, sizeof (int)); -} + { /* turn off TCP coalescence */ + int mi = 1; + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char * ) &mi, sizeof (int)); + } #endif - -#ifndef _WIN32 -#ifndef __BEOS__ +#if !defined(_WIN32) && !defined(__BEOS__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); } #endif -#endif while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == -1) { - #ifdef _WIN32 if (errno == WSAEINPROGRESS) #else @@ -154,25 +267,59 @@ } } -#ifndef _WIN32 -#ifndef __BEOS__ +#if !defined(_WIN32) && !defined(__BEOS__) if (flags != -1) { flags &= ~O_NDELAY; fcntl(fd, F_SETFL, flags); } #endif -#endif - /* wait for connection to complete */ - FD_ZERO(&wfds); - FD_SET(fd, &wfds); + { +#ifdef HAVE_POLL + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLOUT | POLLERR | POLLHUP; + + if (poll(&pfd, 1, 30000) <= 0) { + close(fd); + return -1; + } - tv->tv_sec = 30; - tv->tv_usec = 0; + if (pfd.revents & (POLLERR|POLLHUP)) { + close(fd); + return -1; + } +#else + fd_set rfds, wfds; + struct timeval tv[1]; - if (select(fd + 1, NULL, &wfds, NULL, tv) <= 0) { - (void) close(fd); - return(-1); + /* wait for connection to complete */ + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_SET(fd, &rfds); + FD_SET(fd, &wfds); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + if (select(fd + 1, &rfds, &wfds, NULL, tv) <= 0) { + (void) close(fd); + return(-1); + } + + if (FD_ISSET(fd, &rfds) && FD_ISSET(fd, &wfds)) { + int r = 0, l = sizeof(r); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &r, &l) + || r) { + (void) close(fd); + fd = -1; + } + } +#endif } + return(fd); } +#endif --- jcc.c.orig Fri Nov 30 11:25:24 2001 +++ jcc.c Fri Nov 30 11:30:30 2001 @@ -32,9 +32,13 @@ #include /* declarations for threads and stuff. */ #endif +#ifdef HAVE_POLL +#include +#else #ifndef FD_ZERO #include #endif +#endif #endif @@ -640,7 +644,26 @@ server_body = 0; +#ifdef HAVE_POLL + fds[0].fd = csp->cfd; + fds[0].events = POLLIN|POLLHUP; + fds[1].fd = csp->sfd; + fds[1].events = POLLIN|POLLHUP; +#endif + for(;;) { +#ifdef HAVE_POLL + n = poll(fds, 2, -1); + + if (n < 0) { + fprintf(logfp, "%s: poll() failed!: ", prog); + fperror(logfp, ""); + return; + } + +#define IS_CLIENT() (fds[0].revents & POLLIN) +#define IS_SERVER() (fds[1].revents & POLLIN) +#else FD_ZERO(&rfds); FD_SET(csp->cfd, &rfds); @@ -653,12 +676,14 @@ fperror(logfp, ""); return; } - +#define IS_CLIENT() FD_ISSET(csp->cfd, &rfds) +#define IS_SERVER() FD_ISSET(csp->sfd, &rfds) +#endif /* this is the body of the browser's request * just read it and write it. */ - if(FD_ISSET(csp->cfd, &rfds)) { + if(IS_CLIENT()) { n = read_socket(csp->cfd, buf, sizeof(buf)); @@ -679,7 +704,7 @@ * otherwise it's the body */ - if(FD_ISSET(csp->sfd, &rfds)) { + if(IS_SERVER()) { n = read_socket(csp->sfd, buf, sizeof(buf)); --- jcc.h.orig Fri Nov 30 11:25:24 2001 +++ jcc.h Fri Nov 30 11:37:12 2001 @@ -339,6 +339,7 @@ extern void client_cookie_adder(struct client_state *csp); extern void client_xtra_adder(struct client_state *csp); extern void client_x_forwarded_adder(struct client_state *csp); +extern void server_conn_close_adder(struct client_state *csp); /* interceptors from filters.c */ --- parsers.c.orig Fri Nov 30 11:25:24 2001 +++ parsers.c Fri Nov 30 11:30:30 2001 @@ -32,6 +32,7 @@ { "cookie:", 7, client_send_cookie }, { "x-forwarded-for:", 16, client_x_forwarded }, { "proxy-connection:", 17, crumble }, + { "keep-alive:", 11, crumble }, /* { "if-modified-since:", 18, crumble }, */ { NULL, 0, NULL } }; @@ -57,6 +58,7 @@ }; void (*add_server_headers[])() = { + server_conn_close_adder, /* for http/1.1 */ NULL }; @@ -608,6 +610,12 @@ if(csp->accept_server_cookie == 0) return(crumble(v, s, csp)); return(strdup(s)); +} + +void server_conn_close_adder(struct client_state *csp) +{ + char *p = strsav(NULL, "Connection: close"); + enlist(csp->headers, p); } /* case insensitive string comparison */ --- socks4.c.orig Fri Nov 30 11:25:25 2001 +++ socks4.c Fri Nov 30 11:30:31 2001 @@ -11,6 +11,7 @@ #include +#include #include #include @@ -112,6 +113,8 @@ strcpy(((char *)cbuf) + csiz, http->host); csiz = n; break; + default: + return -1; /* oops */ } c->vn = 4;