summaryrefslogtreecommitdiffhomepage
path: root/src/reqs.c
diff options
context:
space:
mode:
authorSteven Young <sdyoung@users.sourceforge.net>2000-02-16 17:32:49 +0000
committerSteven Young <sdyoung@users.sourceforge.net>2000-02-16 17:32:49 +0000
commit37e63909c09359ddd5baf7a237387ee5f7219c2d (patch)
tree118da48d4b5ed947bc4cb9c004d7e10a976be57a /src/reqs.c
parenta094587fb0e92c5638808d7bff91ef802e200112 (diff)
This commit was generated by cvs2svn to compensate for changes in r2,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'src/reqs.c')
-rw-r--r--src/reqs.c832
1 files changed, 832 insertions, 0 deletions
diff --git a/src/reqs.c b/src/reqs.c
new file mode 100644
index 0000000..7d6aa9f
--- /dev/null
+++ b/src/reqs.c
@@ -0,0 +1,832 @@
+/* $Id: reqs.c,v 1.1.1.1 2000-02-16 17:32:23 sdyoung Exp $
+ *
+ * This is where all the work in tinyproxy is actually done. Incoming
+ * connections are added to the active list of connections and then the header
+ * is processed to determine what is being requested. tinyproxy then connects
+ * to the remote server and begins to relay the bytes back and forth between
+ * the client and the remote server. Basically, we sit there and sling bytes
+ * back and forth. Optionally, we can weed out headers we do not want to send,
+ * and also add a header of our own.
+ *
+ * Copyright (C) 1998 Steven Young
+ * Copyright (C) 1999 Robert James Kaes (rjkaes@flarenet.com)
+ * Copyright (C) 2000 Chris Lightfoot (chris@ex-parrot.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <defines.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sysexits.h>
+#include <assert.h>
+
+#include "config.h"
+#include "tinyproxy.h"
+#include "sock.h"
+#include "utils.h"
+#include "conns.h"
+#include "log.h"
+#include "reqs.h"
+#include "buffer.h"
+#include "filter.h"
+#include "uri.h"
+#include "regexp.h"
+
+/* chris - for asynchronous DNS */
+#include "dnscache.h"
+#include <adns.h>
+extern adns_state adns;
+
+#ifdef XTINYPROXY
+
+static void Add_XTinyproxy_Header(struct conn_s *connptr)
+{
+ char *header_line;
+ char ipaddr[PEER_IP_LENGTH];
+ int length;
+
+ assert(connptr);
+
+ if (!(header_line = xmalloc(sizeof(char) * 32)))
+ return;
+
+ length = sprintf(header_line, "X-Tinyproxy: %s\r\n",
+ getpeer_ip(connptr->client_fd, ipaddr));
+
+ unshift_buffer(connptr->cbuffer, header_line, length);
+}
+
+#endif
+
+#define HTTPPATTERN "^([a-z]+)[ \t]+([^ \t]+)([ \t]+(HTTP/[0-9]+\\.[0-9]+))?"
+#define NMATCH 4
+#define METHOD_IND 1
+#define URI_IND 2
+#define VERSION_MARK 3
+#define VERSION_IND 4
+
+#define HTTP400ERROR "Unrecognizable request. Only HTTP is allowed."
+#define HTTP500ERROR "Unable to connect to remote server."
+#define HTTP503ERROR "Internal server error."
+
+/*
+ * Parse a client HTTP request and then establish connection.
+ */
+static int clientreq(struct conn_s *connptr)
+{
+ URI *uri = NULL;
+ char *inbuf, *buffer, *request, *port;
+ char *inbuf_ptr;
+
+ regex_t preg;
+ regmatch_t pmatch[NMATCH];
+
+ long len;
+ int fd, port_no;
+
+ char peer_ipaddr[PEER_IP_LENGTH];
+
+ assert(connptr);
+
+ getpeer_ip(connptr->client_fd, peer_ipaddr);
+ /* chris - getpeer_string could block, so for the moment take it out */
+
+ if ((len
+ = readline(connptr->client_fd, connptr->cbuffer, &inbuf)) <= 0) {
+ return len;
+ }
+
+ inbuf_ptr = inbuf + len - 1;
+ while (*inbuf_ptr == '\r' || *inbuf_ptr == '\n')
+ *inbuf_ptr-- = '\0';
+
+ /* Log the incoming connection */
+ if (!config.restricted) {
+ log("Connect: %s", peer_ipaddr);
+ log("Request: %s", inbuf);
+ }
+
+ if (regcomp(&preg, HTTPPATTERN, REG_EXTENDED | REG_ICASE) != 0) {
+ log("ERROR clientreq: regcomp");
+ return 0;
+ }
+ if (regexec(&preg, inbuf, NMATCH, pmatch, 0) != 0) {
+ log("ERROR clientreq: regexec");
+ regfree(&preg);
+ return -1;
+ }
+ regfree(&preg);
+
+ if (pmatch[VERSION_MARK].rm_so == -1)
+ connptr->simple_req = TRUE;
+
+ if (pmatch[METHOD_IND].rm_so == -1 || pmatch[URI_IND].rm_so == -1) {
+ log("ERROR clientreq: Incomplete line from %s (%s)",
+ peer_ipaddr, inbuf);
+ httperr(connptr, 400, HTTP400ERROR);
+ goto COMMON_EXIT;
+ }
+
+ len = pmatch[URI_IND].rm_eo - pmatch[URI_IND].rm_so;
+ if (!(buffer = xmalloc(len + 1))) {
+ log("ERROR clientreq: Cannot allocate buffer for request from %s",
+ peer_ipaddr);
+ httperr(connptr, 503, HTTP503ERROR);
+ goto COMMON_EXIT;
+ }
+ memcpy(buffer, inbuf + pmatch[URI_IND].rm_so, len);
+ buffer[len] = '\0';
+ if (!(uri = explode_uri(buffer))) {
+ safefree(buffer);
+ log("ERROR clientreq: Problem with explode_uri");
+ httperr(connptr, 503, HTTP503ERROR);
+ goto COMMON_EXIT;
+ }
+ safefree(buffer);
+
+ if (strcasecmp(uri->scheme, "http") != 0) {
+ char *error_string = xmalloc(strlen(uri->scheme) + 64);
+ sprintf(error_string, "Invalid scheme (%s). Only HTTP is allowed.",
+ uri->scheme);
+ httperr(connptr, 400, error_string);
+ safefree(error_string);
+ goto COMMON_EXIT;
+ }
+
+ if ((strlen(config.stathost) > 0) &&
+ strcasecmp(uri->authority, config.stathost) == 0) {
+ showstats(connptr);
+ goto COMMON_EXIT;
+ }
+
+ port_no = 80;
+ if ((port = strchr(uri->authority, ':'))) {
+ *port++ = '\0';
+ if (strlen(port) > 0)
+ port_no = atoi(port);
+ }
+
+ /* chris - so this can be passed on to clientreq_dnscomplete. */
+ connptr->port_no = port_no;
+
+#ifdef FILTER_ENABLE
+ /* Filter domains out */
+ if (config.filter) {
+ if (filter_host(uri->authority)) {
+ log("ERROR clientreq: Filtered connection (%s)",
+ peer_ipaddr);
+ httperr(connptr, 404,
+ "Unable to connect to filtered host.");
+ goto COMMON_EXIT;
+ }
+ }
+#endif /* FILTER_ENABLE */
+
+ /* Build a new request from the first line of the header */
+ if (!(request = xmalloc(strlen(inbuf) + 1))) {
+ log("ERROR clientreq: cannot allocate buffer for request from %s",
+ peer_ipaddr);
+ httperr(connptr, 503, HTTP503ERROR);
+ goto COMMON_EXIT;
+ }
+
+ /* We need to set the version number WE support */
+ memcpy(request, inbuf, pmatch[METHOD_IND].rm_eo);
+ request[pmatch[METHOD_IND].rm_eo] = '\0';
+ strcat(request, " ");
+ strcat(request, uri->path);
+ if (uri->query) {
+ strcat(request, "?");
+ strcat(request, uri->query);
+ }
+ strcat(request, " HTTP/1.0\r\n");
+
+ /* chris - If domain is in dotted-quad format or is already in the
+ * DNS cache, then go straight to WAITCONN.
+ */
+ if (inet_aton(uri->authority, NULL) || lookup(NULL, uri->authority) == 0) {
+ if ((fd = opensock(uri->authority, port_no)) < 0) {
+ safefree(request);
+ httperr(connptr, 500,
+ "Unable to connect to host (cannot create sock)");
+ stats.num_badcons++;
+ goto COMMON_EXIT;
+ }
+
+ connptr->server_fd = fd;
+ connptr->type = WAITCONN;
+ }
+ /* Otherwise submit a DNS request and hope for the best. */
+ else {
+ if (adns_submit(adns, uri->authority, adns_r_a, adns_qf_quoteok_cname | adns_qf_cname_loose, connptr, &(connptr->adns_qu))) {
+ safefree(request);
+ httperr(connptr, 500, "Resolver error connecting to host");
+ stats.num_badcons++;
+ goto COMMON_EXIT;
+ } else {
+#ifdef __DEBUG__
+ log("DNS request submitted");
+#endif
+ /* copy domain for later caching */
+ connptr->domain = strdup(uri->authority);
+ connptr->type = DNS_WAITCONN;
+ }
+ }
+
+#ifdef XTINYPROXY
+ /* Add a X-Tinyproxy header which contains our IP address */
+ if (config.my_domain
+ && xstrstr(uri->authority, config.my_domain,
+ strlen(uri->authority), FALSE)) {
+ Add_XTinyproxy_Header(connptr);
+ }
+#endif
+
+ /* Add the rewritten request to the buffer */
+ unshift_buffer(connptr->cbuffer, request, strlen(request));
+
+ /*
+ * HACK HACK HACK: When we're sending a POST there is no restriction
+ * on the length of the header. If we don't let all the header lines
+ * through, the POST will not work. This _definitely_ needs to be
+ * fixed. - rjkaes
+ */
+ if (!xstrstr(inbuf, "POST ", 5, FALSE)) {
+ connptr->clientheader = TRUE;
+ }
+
+ COMMON_EXIT:
+ safefree(inbuf);
+ free_uri(uri);
+ return 0;
+}
+
+/* chris - added this to move a connection from the DNS_WAITCONN state
+ * to the WAITCONN state by connecting it to the server, once the name
+ * has been resolved.
+ */
+static int clientreq_dnscomplete(struct conn_s *connptr, struct in_addr *inaddr) {
+ int fd;
+
+ fd = opensock_inaddr(inaddr, connptr->port_no);
+
+ if (fd < 0) {
+#ifdef __DEBUG__
+ log("Failed to open connection to server");
+#endif
+ httperr(connptr, 500,
+ "Unable to connect to host (cannot create sock)");
+ stats.num_badcons++;
+
+ return 0;
+ } else {
+#ifdef __DEBUG__
+ log("Connected to server");
+#endif
+ connptr->server_fd = fd;
+ connptr->type = WAITCONN;
+ }
+
+ return 0;
+}
+
+/*
+ * Finish the client request
+ */
+static int clientreq_finish(struct conn_s *connptr)
+{
+ int sockopt, len = sizeof(sockopt);
+
+ assert(connptr);
+
+ if (getsockopt
+ (connptr->server_fd, SOL_SOCKET, SO_ERROR, &sockopt, &len) < 0) {
+ log("ERROR clientreq_finish: getsockopt error (%s)",
+ strerror(errno));
+ return -1;
+ }
+
+ if (sockopt != 0) {
+ if (sockopt == EINPROGRESS)
+ return 0;
+ else if (sockopt == ETIMEDOUT) {
+ httperr(connptr, 408, "Connect Timed Out");
+ return 0;
+ } else {
+ log("ERROR clientreq_finish: could not create connection (%s)",
+ strerror(sockopt));
+ return -1;
+ }
+ }
+
+ connptr->type = RELAYCONN;
+ stats.num_opens++;
+ return 0;
+}
+
+/*
+ * Check to see if the line is allowed or not depending on the anonymous
+ * headers which are to be allowed.
+ */
+static int anonheader(char *line)
+{
+ struct allowedhdr_s *allowedptr = allowedhdrs;
+
+ assert(line);
+ assert(allowedhdrs);
+
+ if (!xstrstr(line, "GET ", 4, FALSE)
+ || !xstrstr(line, "POST ", 5, FALSE)
+ || !xstrstr(line, "HEAD ", 5, FALSE))
+ return 1;
+
+ for (allowedptr = allowedhdrs; allowedptr;
+ allowedptr = allowedptr->next) {
+ if (!strncasecmp
+ (line, allowedptr->hdrname, strlen(allowedptr->hdrname))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Used to read in the lines from the header (client side) when we're doing
+ * the anonymous header reduction.
+ */
+static int readanonconn(struct conn_s *connptr)
+{
+ char *line = NULL;
+ int retv;
+
+ assert(connptr);
+
+ if ((retv = readline(connptr->client_fd, connptr->cbuffer, &line)) <=
+ 0) {
+ return retv;
+ }
+
+ if ((line[0] == '\n') || (strncmp(line, "\r\n", 2) == 0)) {
+ connptr->clientheader = TRUE;
+ } else if (!anonheader(line)) {
+ safefree(line);
+ return 0;
+ }
+
+ push_buffer(connptr->cbuffer, line, strlen(line));
+ return 0;
+}
+
+/*
+ * Read in the bytes from the socket
+ */
+static int readconn(int fd, struct buffer_s *buffptr)
+{
+ int bytesin;
+
+ assert(fd >= 0);
+ assert(buffptr);
+
+ if ((bytesin = readbuff(fd, buffptr)) < 0) {
+ return -1;
+ }
+#ifdef __DEBUG__
+ log("readconn [%d]: %d", fd, bytesin);
+#endif
+
+ stats.num_rx += bytesin;
+ return bytesin;
+}
+
+/*
+ * Write the bytes from the buffer to the socket
+ */
+static int writeconn(int fd, struct buffer_s *buffptr)
+{
+ int bytessent;
+
+ assert(fd >= 0);
+ assert(buffptr);
+
+ if ((bytessent = writebuff(fd, buffptr)) < 0) {
+ return -1;
+ }
+
+ stats.num_tx += bytessent;
+ return bytessent;
+}
+
+/*
+ * Factored out the common function to read from the client. It was used in
+ * two different places with no change, so no point in having the same code
+ * twice.
+ */
+static int read_from_client(struct conn_s *connptr, fd_set * readfds)
+{
+ assert(connptr);
+ assert(readfds);
+
+ if (FD_ISSET(connptr->client_fd, readfds)) {
+ if (config.anonymous && !connptr->clientheader) {
+ if (readanonconn(connptr) < 0) {
+ shutdown(connptr->client_fd, 2);
+ shutdown(connptr->server_fd, 2);
+ connptr->type = FINISHCONN;
+ return -1;
+ }
+ } else if (readconn(connptr->client_fd, connptr->cbuffer) < 0) {
+ shutdown(connptr->client_fd, 2);
+ shutdown(connptr->server_fd, 2);
+ connptr->type = FINISHCONN;
+ return -1;
+ }
+ connptr->actiontime = time(NULL);
+ }
+
+ return 0;
+}
+
+/*
+ * Factored out the common write to client function since, again, it was used
+ * in two different places with no changes.
+ */
+static int write_to_client(struct conn_s *connptr, fd_set * writefds)
+{
+ assert(connptr);
+ assert(writefds);
+
+ if (FD_ISSET(connptr->client_fd, writefds)) {
+ if (writeconn(connptr->client_fd, connptr->sbuffer) < 0) {
+ shutdown(connptr->client_fd, 2);
+ shutdown(connptr->server_fd, 2);
+ connptr->type = FINISHCONN;
+ return -1;
+ }
+ connptr->actiontime = time(NULL);
+ }
+
+ return 0;
+}
+
+/*
+ * All of the *_req functions handle the various stages a connection can go
+ * through. I moved them out from getreqs because they handle a lot of error
+ * code and it was indenting too far in. As you can see they are very simple,
+ * and are only called once from getreqs, hence the inlining.
+ */
+
+inline static void newconn_req(struct conn_s *connptr, fd_set * readfds)
+{
+ assert(connptr);
+ assert(readfds);
+
+ if (FD_ISSET(connptr->client_fd, readfds)) {
+ if (clientreq(connptr) < 0) {
+ shutdown(connptr->client_fd, 2);
+ connptr->type = FINISHCONN;
+ return;
+ }
+
+ if (!connptr)
+ abort();
+
+ connptr->actiontime = time(NULL);
+ }
+}
+
+inline static void waitconn_req(struct conn_s *connptr, fd_set * readfds,
+ fd_set * writefds)
+{
+ assert(connptr);
+ assert(readfds);
+ assert(writefds);
+
+ if (read_from_client(connptr, readfds) < 0)
+ return;
+
+ if (FD_ISSET(connptr->server_fd, readfds)
+ || FD_ISSET(connptr->server_fd, writefds)) {
+ if (clientreq_finish(connptr) < 0) {
+ shutdown(connptr->server_fd, 2);
+ shutdown(connptr->client_fd, 2);
+ connptr->type = FINISHCONN;
+ return;
+ }
+ connptr->actiontime = time(NULL);
+ }
+}
+
+inline static void relayconn_req(struct conn_s *connptr, fd_set * readfds,
+ fd_set * writefds)
+{
+ assert(connptr);
+ assert(readfds);
+ assert(writefds);
+
+ if (read_from_client(connptr, readfds) < 0)
+ return;
+
+ if (FD_ISSET(connptr->server_fd, readfds)) {
+ if (connptr->serverheader) {
+ if (readconn(connptr->server_fd, connptr->sbuffer) < 0) {
+ shutdown(connptr->server_fd, 2);
+ connptr->type = CLOSINGCONN;
+ return;
+ }
+ } else {
+ /*
+ * We need to read in the first line to rewrite the
+ * version back down to HTTP/1.0 (if needed)
+ */
+ char *line = NULL, *ptr, *newline;
+ int retv;
+
+ if (
+ (retv =
+ readline(connptr->server_fd, connptr->sbuffer,
+ &line)) < 0) {
+ shutdown(connptr->server_fd, 2);
+ httperr(connptr, 500, "Server Closed Early");
+ return;
+ } else if (retv == 0)
+ return;
+
+ connptr->serverheader = TRUE;
+
+ if (strncasecmp(line, "HTTP/1.0", 8)) {
+ /* Okay, we need to rewrite it then */
+ if (!(ptr = strchr(line, ' '))) {
+ shutdown(connptr->server_fd, 2);
+ httperr(connptr, 500,
+ "There was Server Error");
+ return;
+ }
+ ptr++;
+
+ if (!(newline = xmalloc(strlen(line) + 1))) {
+ shutdown(connptr->server_fd, 2);
+ httperr(connptr, 503,
+ "No Memory Available");
+ return;
+ }
+
+ sprintf(newline, "HTTP/1.0 %s", ptr);
+ safefree(line);
+ line = newline;
+ }
+
+ push_buffer(connptr->sbuffer, line, strlen(line));
+ }
+ connptr->actiontime = time(NULL);
+ }
+
+ if (write_to_client(connptr, writefds) < 0)
+ return;
+
+ if (FD_ISSET(connptr->server_fd, writefds)) {
+ if (writeconn(connptr->server_fd, connptr->cbuffer) < 0) {
+ shutdown(connptr->server_fd, 2);
+ connptr->type = CLOSINGCONN;
+ return;
+ }
+ connptr->actiontime = time(NULL);
+ }
+}
+
+inline static void closingconn_req(struct conn_s *connptr, fd_set * writefds)
+{
+ assert(connptr);
+ assert(writefds);
+
+ write_to_client(connptr, writefds);
+}
+
+/*
+ * Check against the valid subnet to see if we should allow the access
+ */
+static int validuser(int fd)
+{
+ char ipaddr[PEER_IP_LENGTH];
+
+ assert(fd >= 0);
+
+ if (config.subnet == NULL)
+ return 1;
+
+ if (!strncmp(config.subnet, getpeer_ip(fd, ipaddr),
+ strlen(config.subnet))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Loop that checks for new connections, dispatches to the correct
+ * routines if bytes are pending, checks to see if it's time for a
+ * garbage collect.
+ */
+int getreqs(void)
+{
+ static unsigned int garbc = 0;
+ fd_set readfds, writefds, exceptfds; /* chris - ADNS expects exceptfds */
+ struct conn_s *connptr;
+ int fd;
+ struct timeval tv, now; /* chris - for ADNS timeouts */
+
+ char peer_ipaddr[PEER_IP_LENGTH];
+
+ if (setup_fd < 0) {
+ log("ERROR getreqs: setup_fd not a socket");
+ return -1;
+ }
+
+ /* Garbage collect the dead connections and close any idle ones */
+ if (garbc++ >= GARBCOLL_INTERVAL) {
+ garbcoll();
+ garbc = 0;
+ }
+ conncoll();
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(setup_fd, &readfds);
+
+ for (connptr = connections; connptr; connptr = connptr->next) {
+#ifdef __DEBUG__
+ log("Connptr: %p - %d / client %d server %d", connptr,
+ connptr->type, connptr->client_fd, connptr->server_fd);
+#endif
+ switch (connptr->type) {
+ case NEWCONN:
+ if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE)
+ FD_SET(connptr->client_fd, &readfds);
+ else {
+ httperr(connptr, 414,
+ "Your Request is way too long.");
+ }
+ break;
+
+ /* no case here for DNS_WAITCONN */
+
+ case WAITCONN:
+ FD_SET(connptr->server_fd, &readfds);
+ FD_SET(connptr->server_fd, &writefds);
+
+ if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE)
+ FD_SET(connptr->client_fd, &readfds);
+ break;
+
+ case RELAYCONN:
+ if (buffer_size(connptr->sbuffer) > 0)
+ FD_SET(connptr->client_fd, &writefds);
+ if (buffer_size(connptr->sbuffer) < MAXBUFFSIZE)
+ FD_SET(connptr->server_fd, &readfds);
+
+ if (buffer_size(connptr->cbuffer) > 0)
+ FD_SET(connptr->server_fd, &writefds);
+ if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE)
+ FD_SET(connptr->client_fd, &readfds);
+
+ break;
+
+ case CLOSINGCONN:
+ if (buffer_size(connptr->sbuffer) > 0)
+ FD_SET(connptr->client_fd, &writefds);
+ else {
+ shutdown(connptr->client_fd, 2);
+ shutdown(connptr->server_fd, 2);
+ connptr->type = FINISHCONN;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Set a 60 second time out */
+ tv.tv_sec = 1;//60;
+ tv.tv_usec = 0;
+
+ /* chris - Make ADNS do its stuff, too. */
+ {
+ struct timeval *tv_mod = &tv;
+ int foo = FD_SETSIZE;
+ gettimeofday(&now, NULL);
+ FD_ZERO(&exceptfds);
+ adns_beforeselect(adns, &foo, &readfds, &writefds, &exceptfds, &tv_mod, NULL, &now);
+ }
+
+ if (select(FD_SETSIZE, &readfds, &writefds, &exceptfds, &tv) < 0) {
+#ifdef __DEBUG__
+ log("select error: %s", strerror(errno));
+#endif
+ return 0;
+ }
+
+ /* chris - see whether any ADNS lookups have completed */
+ gettimeofday(&now, NULL);
+ adns_afterselect(adns, FD_SETSIZE, &readfds, &writefds, &exceptfds, &now);
+
+ for (connptr = connections; connptr; connptr = connptr->next) {
+ adns_answer *ans;
+
+ if (connptr->type == DNS_WAITCONN &&
+ adns_check(adns, &(connptr->adns_qu), &ans, (void**)&connptr) == 0) {
+
+ if (ans->status == adns_s_ok) {
+ if (connptr->domain) {
+ insert(ans->rrs.inaddr, connptr->domain);
+ free(connptr->domain);
+ }
+
+ clientreq_dnscomplete(connptr, ans->rrs.inaddr);
+ free(ans);
+
+ /* hack */
+ FD_SET(connptr->server_fd, &readfds);
+#ifdef __DEBUG__
+ log("DNS resolution successful");
+#endif
+ } else {
+ free(ans);
+
+ httperr(connptr, 500, "Unable to resolve hostname in URL");
+#ifdef __DEBUG__
+ log("DNS resolution failed");
+#endif
+ }
+ }
+ }
+
+ /* Check to see if there are new connections pending */
+ if (FD_ISSET(setup_fd, &readfds) && (fd = listen_sock()) >= 0) {
+ new_conn(fd); /* make a connection from the FD */
+
+ if (validuser(fd)) {
+ if (config.cutoffload && (load > config.cutoffload)) {
+ stats.num_refused++;
+ httperr(connptr, 503,
+ "tinyproxy is not accepting connections due to high system load");
+ }
+ } else {
+ httperr(connptr, 403,
+ "You are not authorized to use the service.");
+ log("AUTH Rejected connection from %s",
+ getpeer_ip(fd, peer_ipaddr));
+ }
+ }
+
+ /*
+ * Loop through the connections and dispatch them to the appropriate
+ * handler
+ */
+ for (connptr = connections; connptr; connptr = connptr->next) {
+ switch (connptr->type) {
+ case NEWCONN:
+ newconn_req(connptr, &readfds);
+ break;
+
+ case WAITCONN:
+ waitconn_req(connptr, &readfds, &writefds);
+ break;
+
+ case RELAYCONN:
+ relayconn_req(connptr, &readfds, &writefds);
+ break;
+
+ case CLOSINGCONN:
+ closingconn_req(connptr, &writefds);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}