summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml36
-rw-r--r--configure.ac18
-rw-r--r--docs/man5/tinyproxy.conf.txt.in9
-rw-r--r--etc/tinyproxy.conf.in7
-rwxr-xr-xscripts/version.sh4
-rw-r--r--src/Makefile.am16
-rw-r--r--src/acl.c161
-rw-r--r--src/anonymous.c16
-rw-r--r--src/anonymous.h2
-rw-r--r--src/basicauth.c23
-rw-r--r--src/basicauth.h6
-rw-r--r--src/child.c119
-rw-r--r--src/child.h5
-rw-r--r--src/common.h2
-rw-r--r--src/conf-tokens.c72
-rw-r--r--src/conf-tokens.gperf61
-rw-r--r--src/conf-tokens.h55
-rw-r--r--src/conf.c382
-rw-r--r--src/conf.h23
-rw-r--r--src/connect-ports.c16
-rw-r--r--src/connect-ports.h8
-rw-r--r--src/conns.c71
-rw-r--r--src/conns.h24
-rw-r--r--src/filter.c4
-rw-r--r--src/hashmap.c516
-rw-r--r--src/hashmap.h125
-rw-r--r--src/hostspec.c166
-rw-r--r--src/hostspec.h26
-rw-r--r--src/hsearch.c219
-rw-r--r--src/hsearch.h23
-rw-r--r--src/html-error.c96
-rw-r--r--src/html-error.h5
-rw-r--r--src/http-message.c5
-rw-r--r--src/log.c37
-rw-r--r--src/loop.c5
-rw-r--r--src/loop.h1
-rw-r--r--src/main.c22
-rw-r--r--src/mypoll.c48
-rw-r--r--src/mypoll.h31
-rw-r--r--src/orderedmap.c115
-rw-r--r--src/orderedmap.h20
-rw-r--r--src/reqs.c410
-rw-r--r--src/reqs.h3
-rw-r--r--src/reverse-proxy.c50
-rw-r--r--src/reverse-proxy.h4
-rw-r--r--src/sock.c66
-rw-r--r--src/sock.h22
-rw-r--r--src/stats.c10
-rw-r--r--src/transparent-proxy.c25
-rw-r--r--src/transparent-proxy.h4
-rw-r--r--src/upstream.c125
-rw-r--r--src/upstream.h18
-rw-r--r--src/vector.c214
-rw-r--r--src/vector.h75
-rwxr-xr-xtests/scripts/run_tests.sh64
-rwxr-xr-xtests/scripts/run_tests_valgrind.sh2
-rwxr-xr-xtests/scripts/webclient.pl5
57 files changed, 1882 insertions, 1815 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..4354107
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,36 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: ./autogen.sh
+ - run: ./configure
+ - run: make
+ - run: make test
+ test-macos:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: brew install automake
+ - run: ./autogen.sh
+ - run: ./configure
+ - run: make
+ valgrind-test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: install valgrind
+ run: sudo apt-get install --assume-yes valgrind
+ - run: ./autogen.sh
+ - run: ./configure --enable-debug --enable-transparent --enable-reverse
+ - run: make
+ - run: make test
+ - run: make valgrind-test
diff --git a/configure.ac b/configure.ac
index 7de6087..3849383 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,7 +141,7 @@ AC_HEADER_STDC
AC_HEADER_TIME
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \
- values.h])
+ values.h poll.h])
dnl Checks for libary functions
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
@@ -204,6 +204,22 @@ fi #manpage_support_enabled
AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno")
+AC_PATH_PROG(GPERF, gperf, no)
+AM_CONDITIONAL(HAVE_GPERF, test "x$GPERF" != "x" -a "x$GPERF" != "xno")
+AH_TEMPLATE([HAVE_GPERF],
+ [Whether you have gperf installed for faster config parsing.])
+
+if test "x$GPERF" != "x" -a "x$GPERF" != "xno" ; then
+ AS_ECHO_N(["checking whether gperf is recent enough... "])
+ if "$GPERF" < src/conf-tokens.gperf >/dev/null 2>&1 ; then
+ AS_ECHO("yes")
+ AC_DEFINE(HAVE_GPERF)
+ else
+ AM_CONDITIONAL(HAVE_GPERF, false)
+ AS_ECHO("no")
+ fi
+fi
+
AC_CONFIG_FILES([
Makefile
src/Makefile
diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in
index 8a18d55..758382c 100644
--- a/docs/man5/tinyproxy.conf.txt.in
+++ b/docs/man5/tinyproxy.conf.txt.in
@@ -58,6 +58,8 @@ only on one specific address.
This allows you to specify which address Tinyproxy will bind
to for outgoing connections to web servers or upstream proxies.
+This parameter may be specified multiple times, then Tinyproxy
+will try all the specified addresses in order.
=item B<BindSame>
@@ -291,6 +293,11 @@ If this boolean option is set to `Yes` or `On`, filtering is
performed for URLs rather than for domains. The default is to
filter based on domains.
+Note that filtering for URLs works only in plain HTTP scenarios.
+Since HTTPS has become ubiquitous during the last years, this
+will only work on a tiny fraction of websites, so it is
+recommended not to use this option.
+
=item B<FilterExtended>
If this boolean option is set to `Yes`, then extended POSIX
@@ -309,6 +316,8 @@ The default filtering policy is to allow everything that is
not matched by a filtering rule. Setting `FilterDefaultDeny`
to `Yes` changes the policy do deny everything but the domains
or URLs matched by the filtering rules.
+In other words, if set to `No` the Filter list acts as a
+blacklist, if set to `Yes` as a whitelist.
=item B<Anonymous>
diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in
index 06f5480..5a170c2 100644
--- a/etc/tinyproxy.conf.in
+++ b/etc/tinyproxy.conf.in
@@ -43,6 +43,13 @@ Port 8888
#BindSame yes
#
+# BindIPv4Mapped: If enabled, tinyproxy will bind the outgoing IPv6
+# connection to the IPv4 address of the incoming connection using
+# the mapping.
+#
+#BindIPv4Mapped 2001:0db8::ffff:0:0
+
+#
# Timeout: The maximum number of seconds of inactivity a connection is
# allowed to have before it is closed by tinyproxy.
#
diff --git a/scripts/version.sh b/scripts/version.sh
index f3948bc..03fb3aa 100755
--- a/scripts/version.sh
+++ b/scripts/version.sh
@@ -5,11 +5,11 @@ GIT_DIR="${SCRIPT_DIR}/../.git"
if test -d "${GIT_DIR}" ; then
if type git >/dev/null 2>&1 ; then
- gitstr=$(git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null)
+ gitstr=$(git describe --match '[0-9]*.[0-9]*.*' 2>/dev/null)
if test "x$?" != x0 ; then
sed 's/$/-git/' < VERSION
else
- printf "%s\n" "$gitstr" | sed -e 's/-/-git-/'
+ printf "%s\n" "$gitstr" | sed -e 's/-g/-git-/'
fi
else
sed 's/$/-git/' < VERSION
diff --git a/src/Makefile.am b/src/Makefile.am
index 50e645b..d132a75 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,15 +24,16 @@ AM_CPPFLAGS = \
-DLOCALSTATEDIR=\"${localstatedir}\"
tinyproxy_SOURCES = \
+ hostspec.c hostspec.h \
acl.c acl.h \
anonymous.c anonymous.h \
buffer.c buffer.h \
child.c child.h \
common.h \
+ conf-tokens.c conf-tokens.h \
conf.c conf.h \
conns.c conns.h \
daemon.c daemon.h \
- hashmap.c hashmap.h \
heap.c heap.h \
html-error.c html-error.h \
http-message.c http-message.h \
@@ -44,12 +45,14 @@ tinyproxy_SOURCES = \
text.c text.h \
main.c main.h \
utils.c utils.h \
- vector.c vector.h \
upstream.c upstream.h \
basicauth.c basicauth.h \
base64.c base64.h \
sblist.c sblist.h \
+ hsearch.c hsearch.h \
+ orderedmap.c orderedmap.h \
loop.c loop.h \
+ mypoll.c mypoll.h \
connect-ports.c connect-ports.h
EXTRA_tinyproxy_SOURCES = filter.c filter.h \
@@ -57,3 +60,12 @@ EXTRA_tinyproxy_SOURCES = filter.c filter.h \
transparent-proxy.c transparent-proxy.h
tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread
+
+if HAVE_GPERF
+conf-tokens.c: conf-tokens-gperf.inc
+conf-tokens-gperf.inc: conf-tokens.gperf
+ $(GPERF) $< > $@
+endif
+
+EXTRA_DIST = conf-tokens.gperf
+
diff --git a/src/acl.c b/src/acl.c
index 75216c3..2d141ba 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -29,16 +29,7 @@
#include "network.h"
#include "sock.h"
#include "sblist.h"
-
-#include <limits.h>
-
-/* Define how long an IPv6 address is in bytes (128 bits, 16 bytes) */
-#define IPV6_LEN 16
-
-enum acl_type {
- ACL_STRING,
- ACL_NUMERIC
-};
+#include "hostspec.h"
/*
* Hold the information about a particular access control. We store
@@ -47,66 +38,9 @@ enum acl_type {
*/
struct acl_s {
acl_access_t access;
- enum acl_type type;
- union {
- char *string;
- struct {
- unsigned char network[IPV6_LEN];
- unsigned char mask[IPV6_LEN];
- } ip;
- } address;
+ struct hostspec h;
};
-/*
- * Fills in the netmask array given a numeric value.
- *
- * Returns:
- * 0 on success
- * -1 on failure (invalid mask value)
- *
- */
-static int
-fill_netmask_array (char *bitmask_string, int v6,
- unsigned char array[], size_t len)
-{
- unsigned int i;
- unsigned long int mask;
- char *endptr;
-
- errno = 0; /* to distinguish success/failure after call */
- mask = strtoul (bitmask_string, &endptr, 10);
-
- /* check for various conversion errors */
- if ((errno == ERANGE && mask == ULONG_MAX)
- || (errno != 0 && mask == 0) || (endptr == bitmask_string))
- return -1;
-
- if (v6 == 0) {
- /* The mask comparison is done as an IPv6 address, so
- * convert to a longer mask in the case of IPv4
- * addresses. */
- mask += 12 * 8;
- }
-
- /* check valid range for a bit mask */
- if (mask > (8 * len))
- return -1;
-
- /* we have a valid range to fill in the array */
- for (i = 0; i != len; ++i) {
- if (mask >= 8) {
- array[i] = 0xff;
- mask -= 8;
- } else if (mask > 0) {
- array[i] = (unsigned char) (0xff << (8 - mask));
- mask = 0;
- } else {
- array[i] = 0;
- }
- }
-
- return 0;
-}
/**
* If the access list has not been set up, create it.
@@ -138,74 +72,19 @@ int
insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
{
struct acl_s acl;
- int ret;
- char *p, ip_dst[IPV6_LEN];
assert (location != NULL);
- ret = init_access_list(access_list);
- if (ret != 0) {
+ if (init_access_list(access_list) != 0)
return -1;
- }
/*
* Start populating the access control structure.
*/
memset (&acl, 0, sizeof (struct acl_s));
acl.access = access_type;
-
- /*
- * Check for a valid IP address (the simplest case) first.
- */
- if (full_inet_pton (location, ip_dst) > 0) {
- acl.type = ACL_NUMERIC;
- memcpy (acl.address.ip.network, ip_dst, IPV6_LEN);
- memset (acl.address.ip.mask, 0xff, IPV6_LEN);
- } else {
- int i;
-
- /*
- * At this point we're either a hostname or an
- * IP address with a slash.
- */
- p = strchr (location, '/');
- if (p != NULL) {
- char dst[sizeof(struct in6_addr)];
- int v6;
-
- /*
- * We have a slash, so it's intended to be an
- * IP address with mask
- */
- *p = '\0';
- if (full_inet_pton (location, ip_dst) <= 0)
- return -1;
-
- acl.type = ACL_NUMERIC;
-
- /* Check if the IP address before the netmask is
- * an IPv6 address */
- if (inet_pton(AF_INET6, location, dst) > 0)
- v6 = 1;
- else
- v6 = 0;
-
- if (fill_netmask_array
- (p + 1, v6, &(acl.address.ip.mask[0]), IPV6_LEN)
- < 0)
- return -1;
-
- for (i = 0; i < IPV6_LEN; i++)
- acl.address.ip.network[i] = ip_dst[i] &
- acl.address.ip.mask[i];
- } else {
- /* In all likelihood a string */
- acl.type = ACL_STRING;
- acl.address.string = safestrdup (location);
- if (!acl.address.string)
- return -1;
- }
- }
+ if(hostspec_parse(location, &acl.h) || acl.h.type == HST_NONE)
+ return -1;
if(!sblist_add(*access_list, &acl)) return -1;
return 0;
@@ -229,7 +108,7 @@ acl_string_processing (struct acl_s *acl, const char *ip_address,
size_t test_length, match_length;
char ipbuf[512];
- assert (acl && acl->type == ACL_STRING);
+ assert (acl && acl->h.type == HST_STRING);
assert (ip_address && strlen (ip_address) > 0);
/*
@@ -237,11 +116,11 @@ acl_string_processing (struct acl_s *acl, const char *ip_address,
* do a string based test only; otherwise, we can do a reverse
* lookup test as well.
*/
- if (acl->address.string[0] != '.') {
+ if (acl->h.address.string[0] != '.') {
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- if (getaddrinfo (acl->address.string, NULL, &hints, &res) != 0)
+ if (getaddrinfo (acl->h.address.string, NULL, &hints, &res) != 0)
goto STRING_TEST;
ressave = res;
@@ -275,7 +154,7 @@ STRING_TEST:
}
test_length = strlen (string_addr);
- match_length = strlen (acl->address.string);
+ match_length = strlen (acl->h.address.string);
/*
* If the string length is shorter than AC string, return a -1 so
@@ -286,7 +165,7 @@ STRING_TEST:
if (strcasecmp
(string_addr + (test_length - match_length),
- acl->address.string) == 0) {
+ acl->h.address.string) == 0) {
if (acl->access == ACL_DENY)
return 0;
else
@@ -310,11 +189,11 @@ static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN])
uint8_t x, y;
int i;
- assert (acl && acl->type == ACL_NUMERIC);
+ assert (acl && acl->h.type == HST_NUMERIC);
for (i = 0; i != IPV6_LEN; ++i) {
- x = addr[i] & acl->address.ip.mask[i];
- y = acl->address.ip.network[i];
+ x = addr[i] & acl->h.address.ip.mask[i];
+ y = acl->h.address.ip.network[i];
/* If x and y don't match, the IP addresses don't match */
if (x != y)
@@ -355,12 +234,12 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis
for (i = 0; i < sblist_getsize (access_list); ++i) {
acl = sblist_get (access_list, i);
- switch (acl->type) {
- case ACL_STRING:
+ switch (acl->h.type) {
+ case HST_STRING:
perm = acl_string_processing (acl, ip, addr, string_addr);
break;
- case ACL_NUMERIC:
+ case HST_NUMERIC:
if (ip[0] == '\0')
continue;
@@ -368,6 +247,10 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis
? check_numeric_acl (acl, numeric_addr)
: -1;
break;
+
+ case HST_NONE:
+ perm = -1;
+ break;
}
/*
@@ -404,8 +287,8 @@ void flush_access_list (acl_list_t access_list)
*/
for (i = 0; i < sblist_getsize (access_list); ++i) {
acl = sblist_get (access_list, i);
- if (acl->type == ACL_STRING) {
- safefree (acl->address.string);
+ if (acl->h.type == HST_STRING) {
+ safefree (acl->h.address.string);
}
}
diff --git a/src/anonymous.c b/src/anonymous.c
index f38fd44..91e490c 100644
--- a/src/anonymous.c
+++ b/src/anonymous.c
@@ -23,7 +23,7 @@
#include "main.h"
#include "anonymous.h"
-#include "hashmap.h"
+#include "hsearch.h"
#include "heap.h"
#include "log.h"
#include "conf.h"
@@ -42,7 +42,7 @@ int anonymous_search (struct config_s *conf, const char *s)
assert (s != NULL);
assert (conf->anonymous_map != NULL);
- return hashmap_search (conf->anonymous_map, s);
+ return !!htab_find (conf->anonymous_map, s);
}
/*
@@ -51,23 +51,21 @@ int anonymous_search (struct config_s *conf, const char *s)
* Return -1 if there is an error, otherwise a 0 is returned if the insert was
* successful.
*/
-int anonymous_insert (struct config_s *conf, const char *s)
+int anonymous_insert (struct config_s *conf, char *s)
{
- char data = 1;
-
assert (s != NULL);
if (!conf->anonymous_map) {
- conf->anonymous_map = hashmap_create (32);
+ conf->anonymous_map = htab_create (32);
if (!conf->anonymous_map)
return -1;
}
- if (hashmap_search (conf->anonymous_map, s) > 0) {
- /* The key was already found, so return a positive number. */
+ if (htab_find (conf->anonymous_map, s)) {
+ /* The key was already found. */
return 0;
}
/* Insert the new key */
- return hashmap_insert (conf->anonymous_map, s, &data, sizeof (data));
+ return htab_insert (conf->anonymous_map, s, HTV_N(1)) ? 0 : -1;
}
diff --git a/src/anonymous.h b/src/anonymous.h
index 6fc4518..78ce771 100644
--- a/src/anonymous.h
+++ b/src/anonymous.h
@@ -23,6 +23,6 @@
extern short int is_anonymous_enabled (struct config_s *conf);
extern int anonymous_search (struct config_s *conf, const char *s);
-extern int anonymous_insert (struct config_s *conf, const char *s);
+extern int anonymous_insert (struct config_s *conf, char *s);
#endif
diff --git a/src/basicauth.c b/src/basicauth.c
index d6c2420..ed0553b 100644
--- a/src/basicauth.c
+++ b/src/basicauth.c
@@ -48,10 +48,10 @@ ssize_t basicauth_string(const char *user, const char *pass,
/*
* Add entry to the basicauth list
*/
-void basicauth_add (vector_t authlist,
+void basicauth_add (sblist *authlist,
const char *user, const char *pass)
{
- char b[BASE64ENC_BYTES((256+2)-1) + 1];
+ char b[BASE64ENC_BYTES((256+2)-1) + 1], *s;
ssize_t ret;
ret = basicauth_string(user, pass, b, sizeof b);
@@ -65,7 +65,8 @@ void basicauth_add (vector_t authlist,
return;
}
- if (vector_append(authlist, b, ret + 1) == -ENOMEM) {
+ if (!(s = safestrdup(b)) || !sblist_add(authlist, &s)) {
+ safefree(s);
log_message (LOG_ERR,
"Unable to allocate memory in basicauth_add()");
return;
@@ -80,18 +81,16 @@ void basicauth_add (vector_t authlist,
* is in the basicauth list.
* return 1 on success, 0 on failure.
*/
-int basicauth_check (vector_t authlist, const char *authstring)
+int basicauth_check (sblist *authlist, const char *authstring)
{
- ssize_t vl, i;
- size_t el;
- const char* entry;
+ size_t i;
+ char** entry;
- vl = vector_length (authlist);
- if (vl == -EINVAL) return 0;
+ if (!authlist) return 0;
- for (i = 0; i < vl; i++) {
- entry = vector_getentry (authlist, i, &el);
- if (strcmp (authstring, entry) == 0)
+ for (i = 0; i < sblist_getsize(authlist); i++) {
+ entry = sblist_get (authlist, i);
+ if (entry && strcmp (authstring, *entry) == 0)
return 1;
}
return 0;
diff --git a/src/basicauth.h b/src/basicauth.h
index 61dc5c3..ef25b66 100644
--- a/src/basicauth.h
+++ b/src/basicauth.h
@@ -22,14 +22,14 @@
#define TINYPROXY_BASICAUTH_H
#include <stddef.h>
-#include "vector.h"
+#include "sblist.h"
extern ssize_t basicauth_string(const char *user, const char *pass,
char *buf, size_t bufsize);
-extern void basicauth_add (vector_t authlist,
+extern void basicauth_add (sblist *authlist,
const char *user, const char *pass);
-extern int basicauth_check (vector_t authlist, const char *authstring);
+extern int basicauth_check (sblist *authlist, const char *authstring);
#endif
diff --git a/src/child.c b/src/child.c
index 15d4fe8..985357d 100644
--- a/src/child.c
+++ b/src/child.c
@@ -33,25 +33,27 @@
#include "conf.h"
#include "sblist.h"
#include "loop.h"
+#include "conns.h"
+#include "mypoll.h"
#include <pthread.h>
-static vector_t listen_fds;
+static sblist* listen_fds;
struct client {
union sockaddr_union addr;
- int fd;
};
struct child {
pthread_t thread;
struct client client;
+ struct conn_s conn;
volatile int done;
};
static void* child_thread(void* data)
{
struct child *c = data;
- handle_connection (c->client.fd, &c->client.addr);
+ handle_connection (&c->conn, &c->client.addr);
c->done = 1;
return NULL;
}
@@ -80,19 +82,24 @@ void child_main_loop (void)
union sockaddr_union cliaddr_storage;
struct sockaddr *cliaddr = (void*) &cliaddr_storage;
socklen_t clilen = sizeof(cliaddr_storage);
- fd_set rfds;
+ int nfds = sblist_getsize(listen_fds);
+ pollfd_struct *fds = safecalloc(nfds, sizeof *fds);
ssize_t i;
- int ret, listenfd, maxfd, was_full = 0;
+ int ret, listenfd, was_full = 0;
pthread_attr_t *attrp, attr;
struct child *child;
childs = sblist_new(sizeof (struct child*), config->maxclients);
- loop_records_init();
+ for (i = 0; i < nfds; i++) {
+ int *fd = sblist_get(listen_fds, i);
+ fds[i].fd = *fd;
+ fds[i].events |= MYPOLL_READ;
+ }
/*
* We have to wait for connections on multiple fds,
- * so use select.
+ * so use select/poll/whatever.
*/
while (!config->quit) {
@@ -110,7 +117,6 @@ void child_main_loop (void)
was_full = 0;
listenfd = -1;
- maxfd = 0;
/* Handle log rotation if it was requested */
if (received_sighup) {
@@ -124,62 +130,35 @@ void child_main_loop (void)
received_sighup = FALSE;
}
+ ret = mypoll(fds, nfds, -1);
- 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",
+ log_message (LOG_ERR, "error calling " SELECT_OR_POLL ": %s",
strerror(errno));
continue;
} else if (ret == 0) {
- log_message (LOG_WARNING, "Strange: select returned 0 "
+ log_message (LOG_WARNING, "Strange: " SELECT_OR_POLL " 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)) {
+ for (i = 0; i < nfds; i++) {
+ if (fds[i].revents & MYPOLL_READ) {
/*
* only accept the connection on the first
* fd that we find readable. - fair?
*/
- listenfd = *fd;
+ listenfd = fds[i].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));
+ "fds was readable after " SELECT_OR_POLL);
continue;
}
@@ -201,7 +180,7 @@ void child_main_loop (void)
continue;
}
- child = safemalloc(sizeof(struct child));
+ child = safecalloc(1, sizeof(struct child));
if (!child) {
oom:
close(connfd);
@@ -218,7 +197,9 @@ oom:
goto oom;
}
- child->client.fd = connfd;
+ conn_struct_init(&child->conn);
+ child->conn.client_fd = connfd;
+
memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage));
attrp = 0;
@@ -233,6 +214,7 @@ oom:
goto oom;
}
}
+ safefree(fds);
}
/*
@@ -240,40 +222,48 @@ oom:
*/
void child_kill_children (int sig)
{
- size_t i;
+ size_t i, tries = 0;
if (sig != SIGTERM) return;
+ log_message (LOG_INFO,
+ "trying to bring down %zu threads...",
+ sblist_getsize(childs)
+ );
+
+again:
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);
- }
+ if (!c->done) pthread_kill(c->thread, SIGCHLD);
}
- usleep(16);
+ usleep(8192);
collect_threads();
if (sblist_getsize(childs) != 0)
+ if(tries++ < 8) goto again;
+ if (sblist_getsize(childs) != 0)
log_message (LOG_CRIT,
"child_kill_children: %zu threads still alive!",
sblist_getsize(childs)
);
}
+void child_free_children(void) {
+ sblist_free(childs);
+ childs = 0;
+}
/**
* Listen on the various configured interfaces
*/
-int child_listening_sockets(vector_t listen_addrs, uint16_t port)
+int child_listening_sockets(sblist *listen_addrs, uint16_t port)
{
int ret;
- ssize_t i;
+ size_t i;
assert (port > 0);
if (listen_fds == NULL) {
- listen_fds = vector_create();
+ listen_fds = sblist_new(sizeof(int), 16);
if (listen_fds == NULL) {
log_message (LOG_ERR, "Could not create the list "
"of listening fds");
@@ -281,8 +271,7 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port)
}
}
- if ((listen_addrs == NULL) ||
- (vector_length(listen_addrs) == 0))
+ if (!listen_addrs || !sblist_getsize(listen_addrs))
{
/*
* no Listen directive:
@@ -292,17 +281,17 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port)
return ret;
}
- for (i = 0; i < vector_length(listen_addrs); i++) {
- const char *addr;
+ for (i = 0; i < sblist_getsize(listen_addrs); i++) {
+ char **addr;
- addr = (char *)vector_getentry(listen_addrs, i, NULL);
- if (addr == NULL) {
+ addr = sblist_get(listen_addrs, i);
+ if (!addr || !*addr) {
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;
}
@@ -313,14 +302,14 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port)
void child_close_sock (void)
{
- ssize_t i;
+ size_t i;
- for (i = 0; i < vector_length(listen_fds); i++) {
- int *fd = (int *) vector_getentry(listen_fds, i, NULL);
+ for (i = 0; i < sblist_getsize(listen_fds); i++) {
+ int *fd = sblist_get(listen_fds, i);
close (*fd);
}
- vector_delete(listen_fds);
+ sblist_free(listen_fds);
listen_fds = NULL;
}
diff --git a/src/child.h b/src/child.h
index 70f52b7..ffcd9d0 100644
--- a/src/child.h
+++ b/src/child.h
@@ -21,7 +21,7 @@
#ifndef TINYPROXY_CHILD_H
#define TINYPROXY_CHILD_H
-#include "vector.h"
+#include "sblist.h"
typedef enum {
CHILD_MAXCLIENTS,
@@ -32,10 +32,11 @@ typedef enum {
} child_config_t;
extern short int child_pool_create (void);
-extern int child_listening_sockets (vector_t listen_addrs, uint16_t port);
+extern int child_listening_sockets (sblist *listen_addrs, uint16_t port);
extern void child_close_sock (void);
extern void child_main_loop (void);
extern void child_kill_children (int sig);
+extern void child_free_children(void);
extern short int child_configure (child_config_t type, unsigned int val);
diff --git a/src/common.h b/src/common.h
index 47a1ed1..a492582 100644
--- a/src/common.h
+++ b/src/common.h
@@ -68,7 +68,7 @@
# include <arpa/inet.h>
# include <grp.h>
# include <pwd.h>
-# include <regex.h>
+# include <limits.h>
/* rest - some oddball headers */
#ifdef HAVE_VALUES_H
diff --git a/src/conf-tokens.c b/src/conf-tokens.c
new file mode 100644
index 0000000..edfcedf
--- /dev/null
+++ b/src/conf-tokens.c
@@ -0,0 +1,72 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include "conf-tokens.h"
+
+#ifdef HAVE_GPERF
+#include "conf-tokens-gperf.inc"
+#else
+
+#include <strings.h>
+
+const struct config_directive_entry *
+config_directive_find (register const char *str, register size_t len)
+{
+ size_t i;
+ static const struct config_directive_entry wordlist[] =
+ {
+ {"",CD_NIL}, {"",CD_NIL},
+ {"allow", CD_allow},
+ {"stathost", CD_stathost},
+ {"listen", CD_listen},
+ {"timeout", CD_timeout},
+ {"statfile", CD_statfile},
+ {"pidfile", CD_pidfile},
+ {"bindsame", CD_bindsame},
+ {"reversebaseurl", CD_reversebaseurl},
+ {"viaproxyname", CD_viaproxyname},
+ {"upstream", CD_upstream},
+ {"anonymous", CD_anonymous},
+ {"group", CD_group},
+ {"defaulterrorfile", CD_defaulterrorfile},
+ {"startservers", CD_startservers},
+ {"filtercasesensitive", CD_filtercasesensitive},
+ {"filterurls", CD_filterurls},
+ {"filter", CD_filter},
+ {"reversemagic", CD_reversemagic},
+ {"errorfile", CD_errorfile},
+ {"minspareservers", CD_minspareservers},
+ {"user", CD_user},
+ {"disableviaheader", CD_disableviaheader},
+ {"deny", CD_deny},
+ {"xtinyproxy", CD_xtinyproxy},
+ {"reversepath", CD_reversepath},
+ {"bind", CD_bind},
+ {"maxclients", CD_maxclients},
+ {"reverseonly", CD_reverseonly},
+ {"port", CD_port},
+ {"maxspareservers", CD_maxspareservers},
+ {"syslog", CD_syslog},
+ {"filterdefaultdeny", CD_filterdefaultdeny},
+ {"loglevel", CD_loglevel},
+ {"filterextended", CD_filterextended},
+ {"connectport", CD_connectport},
+ {"logfile", CD_logfile},
+ {"basicauth", CD_basicauth},
+ {"addheader", CD_addheader},
+ {"maxrequestsperchild", CD_maxrequestsperchild},
+ {"bindipv4mapped", CD_bindipv4mapped},
+ {"bindipv6mapped", CD_bindipv6mapped},
+ };
+
+ for(i=0;i<sizeof(wordlist)/sizeof(wordlist[0]);++i) {
+ if(!strcasecmp(str, wordlist[i].name))
+ return &wordlist[i];
+ }
+ return 0;
+}
+
+#endif
diff --git a/src/conf-tokens.gperf b/src/conf-tokens.gperf
new file mode 100644
index 0000000..ef93245
--- /dev/null
+++ b/src/conf-tokens.gperf
@@ -0,0 +1,61 @@
+%{
+#include <string.h>
+#include <stdlib.h>
+#include "conf-tokens.h"
+%}
+
+struct config_directive_entry { const char* name; enum config_directive value; };
+
+%struct-type
+%define slot-name name
+%define initializer-suffix ,CD_NIL
+%define lookup-function-name config_directive_find
+%ignore-case
+%7bit
+%compare-lengths
+%readonly-tables
+%define constants-prefix CDS_
+%omit-struct-type
+
+%%
+logfile, CD_logfile
+pidfile, CD_pidfile
+anonymous, CD_anonymous
+viaproxyname, CD_viaproxyname
+defaulterrorfile, CD_defaulterrorfile
+statfile, CD_statfile
+stathost, CD_stathost
+xtinyproxy, CD_xtinyproxy
+syslog, CD_syslog
+bindsame, CD_bindsame
+disableviaheader, CD_disableviaheader
+port, CD_port
+maxclients, CD_maxclients
+maxspareservers, CD_maxspareservers
+minspareservers, CD_minspareservers
+startservers, CD_startservers
+maxrequestsperchild, CD_maxrequestsperchild
+timeout, CD_timeout
+connectport, CD_connectport
+user, CD_user
+group, CD_group
+listen, CD_listen
+allow, CD_allow
+deny, CD_deny
+bind, CD_bind
+basicauth, CD_basicauth
+errorfile, CD_errorfile
+addheader, CD_addheader
+filter, CD_filter
+filterurls, CD_filterurls
+filterextended, CD_filterextended
+filterdefaultdeny, CD_filterdefaultdeny
+filtercasesensitive, CD_filtercasesensitive
+reversebaseurl, CD_reversebaseurl
+reverseonly, CD_reverseonly
+reversemagic, CD_reversemagic
+reversepath, CD_reversepath
+upstream, CD_upstream
+loglevel, CD_loglevel
+%%
+
diff --git a/src/conf-tokens.h b/src/conf-tokens.h
new file mode 100644
index 0000000..e99f0de
--- /dev/null
+++ b/src/conf-tokens.h
@@ -0,0 +1,55 @@
+#ifndef CONF_TOKENS_H
+#define CONF_TOKENS_H
+
+enum config_directive {
+CD_NIL = 0,
+CD_logfile,
+CD_pidfile,
+CD_anonymous,
+CD_viaproxyname,
+CD_defaulterrorfile,
+CD_statfile,
+CD_stathost,
+CD_xtinyproxy,
+CD_syslog,
+CD_bindsame,
+CD_disableviaheader,
+CD_port,
+CD_maxclients,
+CD_maxspareservers,
+CD_minspareservers,
+CD_startservers,
+CD_maxrequestsperchild,
+CD_timeout,
+CD_connectport,
+CD_user,
+CD_group,
+CD_listen,
+CD_allow,
+CD_deny,
+CD_bind,
+CD_basicauth,
+CD_errorfile,
+CD_addheader,
+CD_filter,
+CD_filterurls,
+CD_filterextended,
+CD_filterdefaultdeny,
+CD_filtercasesensitive,
+CD_reversebaseurl,
+CD_reverseonly,
+CD_reversemagic,
+CD_reversepath,
+CD_upstream,
+CD_loglevel,
+CD_bindipv4mapped,
+CD_bindipv6mapped,
+};
+
+struct config_directive_entry { const char* name; enum config_directive value; };
+
+const struct config_directive_entry *
+config_directive_find (register const char *str, register size_t len);
+
+#endif
+
diff --git a/src/conf.c b/src/conf.c
index bf2458f..c883da1 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -23,6 +23,8 @@
* add new directives to. Who knows if I'm right though.
*/
+#include "common.h"
+#include <regex.h>
#include "conf.h"
#include "acl.h"
@@ -36,6 +38,7 @@
#include "upstream.h"
#include "connect-ports.h"
#include "basicauth.h"
+#include "conf-tokens.h"
/*
* The configuration directives are defined in the structure below. Each
@@ -46,46 +49,35 @@
* can (and likely should) be used when building the regex for the
* given directive.
*/
-#define WS "[[:space:]]+"
+#define DIGIT "[0-9]"
+#define SPACE "[ \t]"
+#define WS SPACE "+"
#define STR "\"([^\"]+)\""
#define BOOL "(yes|on|no|off)"
-#define INT "((0x)?[[:digit:]]+)"
+#define INT "(()" DIGIT "+)"
#define ALNUM "([-a-z0-9._]+)"
#define USERNAME "([^:]*)"
#define PASSWORD "([^@]*)"
#define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})"
-#define IPMASK "(" IP "(/[[:digit:]]+)?)"
+#define IPMASK "(" IP "(/" DIGIT "+)?)"
#define IPV6 "(" \
- "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6})|" \
- "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5})|" \
- "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4})|" \
- "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3})|" \
- "(([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2})|" \
- "(([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1})|" \
- "((([0-9a-f]{1,4}:){1,7}|:):)|" \
- "(:(:[0-9a-f]{1,4}){1,7})|" \
- "([0-9a-f]{1,4}(:[0-9a-f]{1,4}){1,7})|" \
- "(((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \
- "((([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \
- "(([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "((([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \
- "(:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})" \
+ "(([0-9a-f:]{2,39}))|" \
+ "(([0-9a-f:]{0,29}:" IP "))" \
")"
-#define IPV6MASK "(" IPV6 "(/[[:digit:]]+)?)"
-#define BEGIN "^[[:space:]]*"
-#define END "[[:space:]]*$"
+#define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)"
+#define BEGIN "^" SPACE "*"
+#define END SPACE "*$"
/*
* Limit the maximum number of substring matches to a reasonably high
* number. Given the usual structure of the configuration file, sixteen
* substring matches should be plenty.
*/
-#define RE_MAX_MATCHES 16
+#define RE_MAX_MATCHES 24
+
+#define CP_WARN(FMT, ...) \
+ log_message (LOG_WARNING, "line %lu: " FMT, lineno, __VA_ARGS__)
/*
* All configuration handling functions are REQUIRED to be defined
@@ -113,15 +105,20 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *,
* List all the handling functions. These are defined later, but they need
* to be in-scope before the big structure below.
*/
-static HANDLE_FUNC (handle_nop)
+static HANDLE_FUNC (handle_disabled_feature)
{
- return 0;
-} /* do nothing function */
+ fprintf (stderr, "ERROR: accessing feature that was disabled at compiletime on line %lu\n",
+ lineno);
+
+ return -1;
+}
static HANDLE_FUNC (handle_allow);
static HANDLE_FUNC (handle_basicauth);
static HANDLE_FUNC (handle_anonymous);
static HANDLE_FUNC (handle_bind);
+static HANDLE_FUNC (handle_bindipv4mapped);
+static HANDLE_FUNC (handle_bindipv6mapped);
static HANDLE_FUNC (handle_bindsame);
static HANDLE_FUNC (handle_connectport);
static HANDLE_FUNC (handle_defaulterrorfile);
@@ -161,7 +158,6 @@ static HANDLE_FUNC (handle_xtinyproxy);
#ifdef UPSTREAM_SUPPORT
static HANDLE_FUNC (handle_upstream);
-static HANDLE_FUNC (handle_upstream_no);
#endif
static void config_free_regex (void);
@@ -177,7 +173,7 @@ static void config_free_regex (void);
* do not follow the pattern above. This macro is for convenience
* only.
*/
-#define STDCONF(d, re, func) { BEGIN "(" d ")" WS re END, func, NULL }
+#define STDCONF(d, re, func) [CD_ ## d] = { BEGIN "()" WS re END, func, NULL }
/*
* Holds the regular expression used to match the configuration directive,
@@ -190,109 +186,118 @@ struct {
CONFFILE_HANDLER handler;
regex_t *cre;
} directives[] = {
- /* comments */
- {
- 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),
+ 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),
+ 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),
- STDCONF ("connectport", INT, handle_connectport),
+ 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),
+ STDCONF (connectport, INT, handle_connectport),
/* alphanumeric arguments */
- STDCONF ("user", ALNUM, handle_user),
- STDCONF ("group", ALNUM, handle_group),
+ STDCONF (user, ALNUM, handle_user),
+ STDCONF (group, ALNUM, handle_group),
/* ip arguments */
- STDCONF ("listen", "(" IP "|" IPV6 ")", handle_listen),
- STDCONF ("allow", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",
+ STDCONF (listen, "(" IP "|" IPV6 ")", handle_listen),
+ STDCONF (allow, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",
handle_allow),
- STDCONF ("deny", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",
+ STDCONF (deny, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",
handle_deny),
- STDCONF ("bind", "(" IP "|" IPV6 ")", handle_bind),
+ STDCONF (bind, "(" IP "|" IPV6 ")", handle_bind),
+ STDCONF (bindipv4mapped, "(" IPV6 ")", handle_bindipv4mapped),
+ STDCONF (bindipv6mapped, "(" IP ")", handle_bindipv6mapped),
/* other */
- STDCONF ("basicauth", ALNUM WS ALNUM, handle_basicauth),
- STDCONF ("errorfile", INT WS STR, handle_errorfile),
- STDCONF ("addheader", STR WS STR, handle_addheader),
+ 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),
+ 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),
+ 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 "(http|socks4|socks5)" WS
- "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?"
- "(" IP "|" ALNUM ")"
- ":" INT "(" WS STR ")?"
- END, handle_upstream, NULL
- },
+ STDCONF (upstream,
+ "(" "(none)" WS STR ")|" \
+ "(" "(http|socks4|socks5)" WS \
+ "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?"
+ "(" IP "|" ALNUM ")"
+ ":" INT "(" WS STR ")?" ")", handle_upstream),
#endif
/* loglevel */
- STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)",
+ 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)
+free_added_headers (sblist* add_headers)
{
- ssize_t i;
+ size_t i;
- for (i = 0; i < vector_length (add_headers); i++) {
- http_header_t *header = (http_header_t *)
- vector_getentry (add_headers, i, NULL);
+ if (!add_headers) return;
+
+ for (i = 0; i < sblist_getsize (add_headers); i++) {
+ http_header_t *header = sblist_get (add_headers, i);
safefree (header->name);
safefree (header->value);
}
- vector_delete (add_headers);
+ sblist_free (add_headers);
+}
+
+static void stringlist_free(sblist *sl) {
+ size_t i;
+ char **s;
+ if(sl) {
+ for(i = 0; i < sblist_getsize(sl); i++) {
+ s = sblist_get(sl, i);
+ if(s) safefree(*s);
+ }
+ sblist_free(sl);
+ }
}
-static void free_config (struct config_s *conf)
+void free_config (struct config_s *conf)
{
+ char *k;
+ htab_value *v;
+ size_t it;
safefree (conf->logf_name);
safefree (conf->stathost);
safefree (conf->user);
safefree (conf->group);
- vector_delete(conf->listen_addrs);
- vector_delete(conf->basicauth_list);
+ safefree (conf->bind_ipv4mapped);
+ safefree (conf->bind_ipv6mapped);
+ stringlist_free(conf->basicauth_list);
+ stringlist_free(conf->listen_addrs);
+ stringlist_free(conf->bind_addrs);
#ifdef FILTER_ENABLE
safefree (conf->filter);
#endif /* FILTER_ENABLE */
@@ -304,34 +309,50 @@ static void free_config (struct config_s *conf)
free_upstream_list (conf->upstream_list);
#endif /* UPSTREAM_SUPPORT */
safefree (conf->pidpath);
- safefree (conf->bind_address);
safefree (conf->via_proxy_name);
- hashmap_delete (conf->errorpages);
+ if (conf->errorpages) {
+ it = 0;
+ while((it = htab_next(conf->errorpages, it, &k, &v))) {
+ safefree(k);
+ safefree(v->p);
+ }
+ htab_destroy (conf->errorpages);
+ }
free_added_headers (conf->add_headers);
safefree (conf->errorpage_undef);
safefree (conf->statpage);
flush_access_list (conf->access_list);
free_connect_ports_list (conf->connect_ports);
- hashmap_delete (conf->anonymous_map);
+ if (conf->anonymous_map) {
+ it = 0;
+ while((it = htab_next(conf->anonymous_map, it, &k, &v)))
+ safefree(k);
+ htab_destroy (conf->anonymous_map);
+ }
memset (conf, 0, sizeof(*conf));
}
/*
+ * Initializes Config parser. Currently this means:
* Compiles the regular expressions used by the configuration file. This
* routine MUST be called before trying to parse the configuration file.
*
* Returns 0 on success; negative upon failure.
*/
int
-config_compile_regex (void)
+config_init (void)
{
unsigned int i, r;
for (i = 0; i != ndirectives; ++i) {
- assert (directives[i].handler);
assert (!directives[i].cre);
+ if (!directives[i].handler) {
+ directives[i].handler = handle_disabled_feature;
+ continue;
+ }
+
directives[i].cre = (regex_t *) safemalloc (sizeof (regex_t));
if (!directives[i].cre)
return -1;
@@ -375,20 +396,17 @@ config_free_regex (void)
* a negative number is returned.
*/
static int check_match (struct config_s *conf, const char *line,
- unsigned long lineno)
+ unsigned long lineno, enum config_directive cd)
{
regmatch_t match[RE_MAX_MATCHES];
- unsigned int i;
+ unsigned int i = cd;
- assert (ndirectives > 0);
-
- for (i = 0; i != ndirectives; ++i) {
- assert (directives[i].cre);
- if (!regexec
- (directives[i].cre, line, RE_MAX_MATCHES, match, 0))
- return (*directives[i].handler) (conf, line, lineno, match);
- }
+ if (!directives[i].cre)
+ return (*directives[i].handler) (conf, line, lineno, match);
+ if (!regexec
+ (directives[i].cre, line, RE_MAX_MATCHES, match, 0))
+ return (*directives[i].handler) (conf, line, lineno, match);
return -1;
}
@@ -397,15 +415,25 @@ static int check_match (struct config_s *conf, const char *line,
*/
static int config_parse (struct config_s *conf, FILE * f)
{
- char buffer[LINE_MAX];
+ char buffer[LINE_MAX], *p, *q, c;
+ const struct config_directive_entry *e;
unsigned long lineno = 1;
- while (fgets (buffer, sizeof (buffer), f)) {
- if (check_match (conf, buffer, lineno)) {
- printf ("Syntax error on line %ld\n", lineno);
+ for (;fgets (buffer, sizeof (buffer), f);++lineno) {
+ if(buffer[0] == '#') continue;
+ p = buffer;
+ while(isspace(*p))p++;
+ if(!*p) continue;
+ q = p;
+ while(!isspace(*q))q++;
+ c = *q;
+ *q = 0;
+ e = config_directive_find(p, strlen(p));
+ *q = c;
+ if (!e || e->value == CD_NIL || check_match (conf, q, lineno, e->value)) {
+ fprintf (stderr, "ERROR: Syntax error on line %lu\n", lineno);
return 1;
}
- ++lineno;
}
return 0;
}
@@ -464,10 +492,6 @@ int reload_config_file (const char *config_fname, struct config_s *conf)
{
int ret;
- log_message (LOG_NOTICE, "Reloading config file");
-
- free_config (conf);
-
initialize_config_defaults (conf);
ret = load_config_file (config_fname, conf);
@@ -487,7 +511,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf)
goto done;
}
- if (!conf->user) {
+ if (!conf->user && !geteuid()) {
log_message (LOG_WARNING, "You SHOULD set a UserName in the "
"config file. Using current user instead.");
}
@@ -624,8 +648,12 @@ static HANDLE_FUNC (handle_anonymous)
if (!arg)
return -1;
- anonymous_insert (conf, arg);
- safefree (arg);
+ if(anonymous_insert (conf, arg) < 0) {
+ CP_WARN ("anonymous_insert() failed: '%s'", arg);
+ safefree(arg);
+ return -1;
+ }
+
return 0;
}
@@ -748,11 +776,16 @@ static HANDLE_FUNC (handle_group)
return set_string_arg (&conf->group, line, &match[2]);
}
+static void warn_invalid_address(char *arg, unsigned long lineno) {
+ CP_WARN ("Invalid address %s", arg);
+}
+
static HANDLE_FUNC (handle_allow)
{
char *arg = get_string_arg (line, &match[2]);
- insert_acl (arg, ACL_ALLOW, &conf->access_list);
+ if(insert_acl (arg, ACL_ALLOW, &conf->access_list) < 0)
+ warn_invalid_address (arg, lineno);
safefree (arg);
return 0;
}
@@ -761,19 +794,35 @@ static HANDLE_FUNC (handle_deny)
{
char *arg = get_string_arg (line, &match[2]);
- insert_acl (arg, ACL_DENY, &conf->access_list);
+ if(insert_acl (arg, ACL_DENY, &conf->access_list) < 0)
+ warn_invalid_address (arg, lineno);
safefree (arg);
return 0;
}
static HANDLE_FUNC (handle_bind)
{
- int r = set_string_arg (&conf->bind_address, line, &match[2]);
+ char *arg = get_string_arg (line, &match[2]);
+
+ if (arg == NULL) {
+ return -1;
+ }
+
+ if (conf->bind_addrs == NULL) {
+ conf->bind_addrs = sblist_new(sizeof(char*), 16);
+ if (conf->bind_addrs == NULL) {
+ CP_WARN ("Could not create a list "
+ "of bind addresses.", "");
+ safefree(arg);
+ return -1;
+ }
+ }
+
+ sblist_add (conf->bind_addrs, &arg);
- if (r)
- return r;
log_message (LOG_INFO,
- "Outgoing connections bound to IP %s", conf->bind_address);
+ "Added bind address [%s] for outgoing connections.", arg);
+
return 0;
}
@@ -786,23 +835,32 @@ static HANDLE_FUNC (handle_listen)
}
if (conf->listen_addrs == NULL) {
- conf->listen_addrs = vector_create();
+ conf->listen_addrs = sblist_new(sizeof(char*), 16);
if (conf->listen_addrs == NULL) {
- log_message(LOG_WARNING, "Could not create a list "
- "of listen addresses.");
+ CP_WARN ("Could not create a list "
+ "of listen addresses.", "");
safefree(arg);
return -1;
}
}
- vector_append (conf->listen_addrs, arg, strlen(arg) + 1);
+ sblist_add (conf->listen_addrs, &arg);
log_message(LOG_INFO, "Added address [%s] to listen addresses.", arg);
- safefree (arg);
return 0;
}
+static HANDLE_FUNC (handle_bindipv4mapped)
+{
+ return set_string_arg(&conf->bind_ipv4mapped, line, &match[2]);
+}
+
+static HANDLE_FUNC (handle_bindipv6mapped)
+{
+ return set_string_arg(&conf->bind_ipv6mapped, line, &match[2]);
+}
+
static HANDLE_FUNC (handle_errorfile)
{
/*
@@ -815,8 +873,10 @@ static HANDLE_FUNC (handle_errorfile)
unsigned long int err = get_long_arg (line, &match[2]);
char *page = get_string_arg (line, &match[4]);
- add_new_errorpage (page, err);
- safefree (page);
+ if(add_new_errorpage (conf, page, err) < 0) {
+ CP_WARN ("add_new_errorpage() failed: '%s'", page);
+ safefree (page);
+ }
return 0;
}
@@ -824,19 +884,16 @@ static HANDLE_FUNC (handle_addheader)
{
char *name = get_string_arg (line, &match[2]);
char *value = get_string_arg (line, &match[3]);
- http_header_t *header;
+ http_header_t header;
if (!conf->add_headers) {
- conf->add_headers = vector_create ();
+ conf->add_headers = sblist_new (sizeof(http_header_t), 16);
}
- header = (http_header_t *) safemalloc (sizeof (http_header_t));
- header->name = name;
- header->value = value;
-
- vector_prepend (conf->add_headers, header, sizeof *header);
+ header.name = name;
+ header.value = value;
- safefree (header);
+ sblist_add (conf->add_headers, &header);
/* Don't free name or value here, as they are referenced in the
* struct inserted into the vector. */
@@ -893,7 +950,7 @@ static HANDLE_FUNC (handle_basicauth)
return -1;
}
if (!conf->basicauth_list) {
- conf->basicauth_list = vector_create ();
+ conf->basicauth_list = sblist_new (sizeof(char*), 16);
}
basicauth_add (conf->basicauth_list, user, pass);
@@ -997,9 +1054,26 @@ static enum proxy_type pt_from_string(const char *s)
static HANDLE_FUNC (handle_upstream)
{
char *ip;
- int port, mi = 2;
+ int port, mi;
char *domain = 0, *user = 0, *pass = 0, *tmp;
enum proxy_type pt;
+ enum upstream_build_error ube;
+
+ if (match[3].rm_so != -1) {
+ tmp = get_string_arg (line, &match[3]);
+ if(!strcmp(tmp, "none")) {
+ safefree(tmp);
+ if (match[4].rm_so == -1) return -1;
+ domain = get_string_arg (line, &match[4]);
+ if (!domain)
+ return -1;
+ ube = upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list);
+ safefree (domain);
+ goto check_err;
+ }
+ }
+
+ mi = 6;
tmp = get_string_arg (line, &match[mi]);
pt = pt_from_string(tmp);
@@ -1025,27 +1099,17 @@ static HANDLE_FUNC (handle_upstream)
if (match[mi].rm_so != -1)
domain = get_string_arg (line, &match[mi]);
- upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list);
+ ube = upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list);
safefree (user);
safefree (pass);
safefree (domain);
safefree (ip);
+check_err:;
+ if(ube != UBE_SUCCESS)
+ CP_WARN("%s", upstream_build_error_string(ube));
return 0;
}
-static HANDLE_FUNC (handle_upstream_no)
-{
- char *domain;
-
- domain = get_string_arg (line, &match[3]);
- if (!domain)
- return -1;
-
- upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list);
- safefree (domain);
-
- return 0;
-}
#endif
diff --git a/src/conf.h b/src/conf.h
index e89806a..572aeb1 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -22,8 +22,8 @@
#ifndef TINYPROXY_CONF_H
#define TINYPROXY_CONF_H
-#include "hashmap.h"
-#include "vector.h"
+#include "hsearch.h"
+#include "sblist.h"
#include "acl.h"
/*
@@ -38,7 +38,7 @@ typedef struct {
* Hold all the configuration time information.
*/
struct config_s {
- vector_t basicauth_list;
+ sblist *basicauth_list;
char *logf_name;
unsigned int syslog; /* boolean */
unsigned int port;
@@ -47,7 +47,7 @@ struct config_s {
unsigned int maxclients;
char *user;
char *group;
- vector_t listen_addrs;
+ sblist *listen_addrs;
#ifdef FILTER_ENABLE
char *filter;
unsigned int filter_url; /* boolean */
@@ -68,8 +68,10 @@ struct config_s {
#endif /* UPSTREAM_SUPPORT */
char *pidpath;
unsigned int idletimeout;
- char *bind_address;
+ sblist *bind_addrs;
unsigned int bindsame;
+ char *bind_ipv4mapped;
+ char *bind_ipv6mapped;
/*
* The configured name to use in the HTTP "Via" header field.
@@ -81,7 +83,7 @@ struct config_s {
/*
* Error page support. Map error numbers to file paths.
*/
- hashmap_t errorpages;
+ struct htab *errorpages;
/*
* Error page to be displayed if appropriate page cannot be located
@@ -99,22 +101,23 @@ struct config_s {
/*
* Store the list of port allowed by CONNECT.
*/
- vector_t connect_ports;
+ sblist *connect_ports;
/*
* Map of headers which should be let through when the
* anonymous feature is turned on.
*/
- hashmap_t anonymous_map;
+ struct htab *anonymous_map;
/*
* Extra headers to be added to outgoing HTTP requests.
*/
- vector_t add_headers;
+ sblist* add_headers;
};
extern int reload_config_file (const char *config_fname, struct config_s *conf);
-int config_compile_regex (void);
+int config_init (void);
+void free_config (struct config_s *conf);
#endif
diff --git a/src/connect-ports.c b/src/connect-ports.c
index 41b4e3d..6070e92 100644
--- a/src/connect-ports.c
+++ b/src/connect-ports.c
@@ -25,10 +25,10 @@
* 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, sblist **connect_ports)
{
if (!*connect_ports) {
- *connect_ports = vector_create ();
+ *connect_ports = sblist_new (sizeof(int), 16);
if (!*connect_ports) {
log_message (LOG_WARNING,
"Could not create a list of allowed CONNECT ports");
@@ -38,7 +38,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports)
log_message (LOG_INFO,
"Adding Port [%d] to the list allowed by CONNECT", port);
- vector_append (*connect_ports, &port, sizeof (port));
+ sblist_add (*connect_ports, &port);
}
/*
@@ -47,7 +47,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports)
* Returns: 1 if allowed
* 0 if denied
*/
-int check_allowed_connect_ports (int port, vector_t connect_ports)
+int check_allowed_connect_ports (int port, sblist *connect_ports)
{
size_t i;
int *data;
@@ -59,8 +59,8 @@ int check_allowed_connect_ports (int port, vector_t connect_ports)
if (!connect_ports)
return 1;
- for (i = 0; i != (size_t) vector_length (connect_ports); ++i) {
- data = (int *) vector_getentry (connect_ports, i, NULL);
+ for (i = 0; i < sblist_getsize (connect_ports); ++i) {
+ data = sblist_get (connect_ports, i);
if (data && *data == port)
return 1;
}
@@ -71,7 +71,7 @@ int check_allowed_connect_ports (int port, vector_t connect_ports)
/**
* Free a connect_ports list.
*/
-void free_connect_ports_list (vector_t connect_ports)
+void free_connect_ports_list (sblist *connect_ports)
{
- vector_delete (connect_ports);
+ sblist_free (connect_ports);
}
diff --git a/src/connect-ports.h b/src/connect-ports.h
index 4b3aaf7..38f511c 100644
--- a/src/connect-ports.h
+++ b/src/connect-ports.h
@@ -22,10 +22,10 @@
#define _TINYPROXY_CONNECT_PORTS_H_
#include "common.h"
-#include "vector.h"
+#include "sblist.h"
-extern void add_connect_port_allowed (int port, vector_t *connect_ports);
-int check_allowed_connect_ports (int port, vector_t connect_ports);
-void free_connect_ports_list (vector_t connect_ports);
+extern void add_connect_port_allowed (int port, sblist **connect_ports);
+int check_allowed_connect_ports (int port, sblist *connect_ports);
+void free_connect_ports_list (sblist *connect_ports);
#endif /* _TINYPROXY_CONNECT_PORTS_ */
diff --git a/src/conns.c b/src/conns.c
index 505b5c4..feac522 100644
--- a/src/conns.c
+++ b/src/conns.c
@@ -30,13 +30,21 @@
#include "log.h"
#include "stats.h"
-struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
- const char *sock_ipaddr)
+void conn_struct_init(struct conn_s *connptr) {
+ connptr->error_number = -1;
+ connptr->client_fd = -1;
+ connptr->server_fd = -1;
+ /* There is _no_ content length initially */
+ connptr->content_length.server = connptr->content_length.client = -1;
+}
+
+int conn_init_contents (struct conn_s *connptr, const char *ipaddr,
+ const char *sock_ipaddr,
+ const char *sock_ipaddr_alt)
{
- struct conn_s *connptr;
struct buffer_s *cbuffer, *sbuffer;
- assert (client_fd >= 0);
+ assert (connptr->client_fd >= 0);
/*
* Allocate the memory for all the internal components
@@ -47,47 +55,18 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
if (!cbuffer || !sbuffer)
goto error_exit;
- /*
- * Allocate the space for the conn_s structure itself.
- */
- connptr = (struct conn_s *) safemalloc (sizeof (struct conn_s));
- if (!connptr)
- goto error_exit;
-
- connptr->client_fd = client_fd;
- connptr->server_fd = -1;
-
connptr->cbuffer = cbuffer;
connptr->sbuffer = sbuffer;
- connptr->request_line = NULL;
-
- /* These store any error strings */
- connptr->error_variables = NULL;
- connptr->error_string = NULL;
- connptr->error_number = -1;
-
- connptr->connect_method = FALSE;
- connptr->show_stats = FALSE;
-
- connptr->protocol.major = connptr->protocol.minor = 0;
-
- /* There is _no_ content length initially */
- connptr->content_length.server = connptr->content_length.client = -1;
-
connptr->server_ip_addr = (sock_ipaddr ?
safestrdup (sock_ipaddr) : NULL);
+ connptr->server_ip_addr_alt = (sock_ipaddr_alt ?
+ safestrdup (sock_ipaddr_alt) : NULL);
connptr->client_ip_addr = safestrdup (ipaddr);
- connptr->upstream_proxy = NULL;
-
update_stats (STAT_OPEN);
-#ifdef REVERSE_SUPPORT
- connptr->reversepath = NULL;
-#endif
-
- return connptr;
+ return 1;
error_exit:
/*
@@ -98,10 +77,10 @@ error_exit:
if (sbuffer)
delete_buffer (sbuffer);
- return NULL;
+ return 0;
}
-void destroy_conn (struct conn_s *connptr)
+void conn_destroy_contents (struct conn_s *connptr)
{
assert (connptr != NULL);
@@ -109,10 +88,12 @@ void destroy_conn (struct conn_s *connptr)
if (close (connptr->client_fd) < 0)
log_message (LOG_INFO, "Client (%d) close message: %s",
connptr->client_fd, strerror (errno));
+ connptr->client_fd = -1;
if (connptr->server_fd != -1)
if (close (connptr->server_fd) < 0)
log_message (LOG_INFO, "Server (%d) close message: %s",
connptr->server_fd, strerror (errno));
+ connptr->server_fd = -1;
if (connptr->cbuffer)
delete_buffer (connptr->cbuffer);
@@ -122,8 +103,16 @@ void destroy_conn (struct conn_s *connptr)
if (connptr->request_line)
safefree (connptr->request_line);
- if (connptr->error_variables)
- hashmap_delete (connptr->error_variables);
+ if (connptr->error_variables) {
+ char *k;
+ htab_value *v;
+ size_t it = 0;
+ while((it = htab_next(connptr->error_variables, it, &k, &v))) {
+ safefree(v->p);
+ safefree(k);
+ }
+ htab_destroy (connptr->error_variables);
+ }
if (connptr->error_string)
safefree (connptr->error_string);
@@ -138,7 +127,5 @@ void destroy_conn (struct conn_s *connptr)
safefree (connptr->reversepath);
#endif
- safefree (connptr);
-
update_stats (STAT_CLOSE);
}
diff --git a/src/conns.h b/src/conns.h
index 393e5d4..36082f2 100644
--- a/src/conns.h
+++ b/src/conns.h
@@ -22,7 +22,7 @@
#define TINYPROXY_CONNS_H
#include "main.h"
-#include "hashmap.h"
+#include "hsearch.h"
/*
* Connection Definition
@@ -45,7 +45,7 @@ struct conn_s {
* This structure stores key -> value mappings for substitution
* in the error HTML files.
*/
- hashmap_t error_variables;
+ struct htab *error_variables;
int error_number;
char *error_string;
@@ -62,6 +62,11 @@ struct conn_s {
char *server_ip_addr;
/*
+ * Store the server's alternative IP (for BindIPv4/6Mapped)
+ */
+ char *server_ip_addr_alt;
+
+ /*
* Store the client's IP information
*/
char *client_ip_addr;
@@ -87,11 +92,14 @@ struct conn_s {
struct upstream *upstream_proxy;
};
-/*
- * Functions for the creation and destruction of a connection structure.
- */
-extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
- const char *sock_ipaddr);
-extern void destroy_conn (struct conn_s *connptr);
+/* expects pointer to zero-initialized struct, set up struct
+ with default values for initial use */
+extern void conn_struct_init(struct conn_s *connptr);
+
+/* second stage initializiation, sets up buffers and connection details */
+extern int conn_init_contents (struct conn_s *connptr, const char *ipaddr,
+ const char *sock_ipaddr,
+ const char *sock_ipaddr_alt);
+extern void conn_destroy_contents (struct conn_s *connptr);
#endif
diff --git a/src/filter.c b/src/filter.c
index 8a0b085..b9b5066 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -24,6 +24,7 @@
#include "main.h"
+#include <regex.h>
#include "filter.h"
#include "heap.h"
#include "log.h"
@@ -60,7 +61,8 @@ void filter_init (void)
fd = fopen (config->filter, "r");
if (!fd) {
- return;
+ perror ("filter file");
+ exit (EX_DATAERR);
}
cflags = REG_NEWLINE | REG_NOSUB;
diff --git a/src/hashmap.c b/src/hashmap.c
deleted file mode 100644
index 7793d08..0000000
--- a/src/hashmap.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/* tinyproxy - A fast light-weight HTTP proxy
- * Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
- *
- * 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 of the License, 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* A hashmap implementation. The keys are case-insensitive NULL terminated
- * strings, and the data is arbitrary lumps of data. Copies of both the
- * key and the data in the hashmap itself, so you must free the original
- * key and data to avoid a memory leak. The hashmap returns a pointer
- * to the data when a key is searched for, so take care in modifying the
- * data as it's modifying the data stored in the hashmap. (In other words,
- * don't try to free the data, or realloc the memory. :)
- */
-
-#include "main.h"
-
-#include "hashmap.h"
-#include "heap.h"
-
-/*
- * These structures are the storage for the hashmap. Entries are stored in
- * struct hashentry_s (the key, data, and length), and all the "buckets" are
- * grouped together in hashmap_s. The hashmap_s.size member is for
- * internal use. It stores the number of buckets the hashmap was created
- * with.
- */
-struct hashentry_s {
- char *key;
- void *data;
- size_t len;
-
- struct hashentry_s *prev, *next;
-};
-
-struct hashbucket_s {
- struct hashentry_s *head, *tail;
-};
-
-struct hashmap_s {
- uint32_t seed;
- unsigned int size;
- hashmap_iter end_iterator;
-
- struct hashbucket_s *buckets;
-};
-
-/*
- * A NULL terminated string is passed to this function and a "hash" value
- * is produced within the range of [0 .. size) (In other words, 0 to one
- * less than size.)
- * The contents of the key are converted to lowercase, so this function
- * is not case-sensitive.
- *
- * This is Dan Bernstein's hash function as described, for example, here:
- * http://www.cse.yorku.ca/~oz/hash.html
- *
- * If any of the arguments are invalid a negative number is returned.
- */
-static int hashfunc (const char *key, unsigned int size, uint32_t seed)
-{
- uint32_t hash;
-
- if (key == NULL)
- return -EINVAL;
- if (size == 0)
- return -ERANGE;
-
- for (hash = seed; *key != '\0'; key++) {
- hash = ((hash << 5) + hash) ^ tolower (*key);
- }
-
- /* Keep the hash within the table limits */
- return hash % size;
-}
-
-/*
- * Create a hashmap with the requested number of buckets. If "nbuckets" is
- * not greater than zero a NULL is returned; otherwise, a _token_ to the
- * hashmap is returned.
- *
- * NULLs are also returned if memory could not be allocated for hashmap.
- */
-hashmap_t hashmap_create (unsigned int nbuckets)
-{
- struct hashmap_s *ptr;
-
- if (nbuckets == 0)
- return NULL;
-
- ptr = (struct hashmap_s *) safecalloc (1, sizeof (struct hashmap_s));
- if (!ptr)
- return NULL;
-
- ptr->seed = (uint32_t)rand();
- ptr->size = nbuckets;
- ptr->buckets = (struct hashbucket_s *) safecalloc (nbuckets,
- sizeof (struct
- hashbucket_s));
- if (!ptr->buckets) {
- safefree (ptr);
- return NULL;
- }
-
- /* This points to "one" past the end of the hashmap. */
- ptr->end_iterator = 0;
-
- return ptr;
-}
-
-/*
- * Follow the chain of hashentries and delete them (including the data and
- * the key.)
- *
- * Returns: 0 if the function completed successfully
- * negative number is returned if "entry" was NULL
- */
-static int delete_hashbucket (struct hashbucket_s *bucket)
-{
- struct hashentry_s *nextptr;
- struct hashentry_s *ptr;
-
- if (bucket == NULL || bucket->head == NULL)
- return -EINVAL;
-
- ptr = bucket->head;
- while (ptr) {
- nextptr = ptr->next;
-
- safefree (ptr->key);
- safefree (ptr->data);
- safefree (ptr);
-
- ptr = nextptr;
- }
-
- return 0;
-}
-
-/*
- * Deletes a hashmap. All the key/data pairs are also deleted.
- *
- * Returns: 0 on success
- * negative if a NULL "map" was supplied
- */
-int hashmap_delete (hashmap_t map)
-{
- unsigned int i;
-
- if (map == NULL)
- return -EINVAL;
-
- for (i = 0; i != map->size; i++) {
- if (map->buckets[i].head != NULL) {
- delete_hashbucket (&map->buckets[i]);
- }
- }
-
- safefree (map->buckets);
- safefree (map);
-
- return 0;
-}
-
-/*
- * Inserts a NULL terminated string (as the key), plus any arbitrary "data"
- * of "len" bytes. Both the key and the data are copied, so the original
- * key/data must be freed to avoid a memory leak.
- * The "data" must be non-NULL and "len" must be greater than zero. You
- * cannot insert NULL data in association with the key.
- *
- * Returns: 0 on success
- * negative number if there are errors
- */
-int
-hashmap_insert (hashmap_t map, const char *key, const void *data, size_t len)
-{
- struct hashentry_s *ptr;
- int hash;
- char *key_copy;
- void *data_copy;
-
- assert (map != NULL);
- assert (key != NULL);
- assert (data != NULL);
- assert (len > 0);
-
- if (map == NULL || key == NULL)
- return -EINVAL;
- if (!data || len < 1)
- return -ERANGE;
-
- hash = hashfunc (key, map->size, map->seed);
- if (hash < 0)
- return hash;
-
- /*
- * First make copies of the key and data in case there is a memory
- * problem later.
- */
- key_copy = safestrdup (key);
- if (!key_copy)
- return -ENOMEM;
-
- data_copy = safemalloc (len);
- if (!data_copy) {
- safefree (key_copy);
- return -ENOMEM;
- }
- memcpy (data_copy, data, len);
-
- ptr = (struct hashentry_s *) safemalloc (sizeof (struct hashentry_s));
- if (!ptr) {
- safefree (key_copy);
- safefree (data_copy);
- return -ENOMEM;
- }
-
- ptr->key = key_copy;
- ptr->data = data_copy;
- ptr->len = len;
-
- /*
- * Now add the entry to the end of the bucket chain.
- */
- ptr->next = NULL;
- ptr->prev = map->buckets[hash].tail;
- if (map->buckets[hash].tail)
- map->buckets[hash].tail->next = ptr;
-
- map->buckets[hash].tail = ptr;
- if (!map->buckets[hash].head)
- map->buckets[hash].head = ptr;
-
- map->end_iterator++;
- return 0;
-}
-
-/*
- * Get an iterator to the first entry.
- *
- * Returns: an negative value upon error.
- */
-hashmap_iter hashmap_first (hashmap_t map)
-{
- assert (map != NULL);
-
- if (!map)
- return -EINVAL;
-
- if (map->end_iterator == 0)
- return -1;
- else
- return 0;
-}
-
-/*
- * Checks to see if the iterator is pointing at the "end" of the entries.
- *
- * Returns: 1 if it is the end
- * 0 otherwise
- */
-int hashmap_is_end (hashmap_t map, hashmap_iter iter)
-{
- assert (map != NULL);
- assert (iter >= 0);
-
- if (!map || iter < 0)
- return -EINVAL;
-
- if (iter == map->end_iterator)
- return 1;
- else
- return 0;
-}
-
-/*
- * Return a "pointer" to the first instance of the particular key. It can
- * be tested against hashmap_is_end() to see if the key was not found.
- *
- * Returns: negative upon an error
- * an "iterator" pointing at the first key
- * an "end-iterator" if the key wasn't found
- */
-hashmap_iter hashmap_find (hashmap_t map, const char *key)
-{
- unsigned int i;
- hashmap_iter iter = 0;
- struct hashentry_s *ptr;
-
- assert (map != NULL);
- assert (key != NULL);
-
- if (!map || !key)
- return -EINVAL;
-
- /*
- * Loop through all the keys and look for the first occurrence
- * of a particular key.
- */
- for (i = 0; i != map->size; i++) {
- ptr = map->buckets[i].head;
-
- while (ptr) {
- if (strcasecmp (ptr->key, key) == 0) {
- /* Found it, so return the current count */
- return iter;
- }
-
- iter++;
- ptr = ptr->next;
- }
- }
-
- return iter;
-}
-
-/*
- * Retrieve the data associated with a particular iterator.
- *
- * Returns: the length of the data block upon success
- * negative upon error
- */
-ssize_t
-hashmap_return_entry (hashmap_t map, hashmap_iter iter, char **key, void **data)
-{
- unsigned int i;
- struct hashentry_s *ptr;
- hashmap_iter count = 0;
-
- assert (map != NULL);
- assert (iter >= 0);
- assert (iter != map->end_iterator);
- assert (key != NULL);
- assert (data != NULL);
-
- if (!map || iter < 0 || !key || !data)
- return -EINVAL;
-
- for (i = 0; i != map->size; i++) {
- ptr = map->buckets[i].head;
- while (ptr) {
- if (count == iter) {
- /* This is the data so return it */
- *key = ptr->key;
- *data = ptr->data;
- return ptr->len;
- }
-
- ptr = ptr->next;
- count++;
- }
- }
-
- return -EFAULT;
-}
-
-/*
- * Searches for _any_ occurrences of "key" within the hashmap.
- *
- * Returns: negative upon an error
- * zero if no key is found
- * count found
- */
-ssize_t hashmap_search (hashmap_t map, const char *key)
-{
- int hash;
- struct hashentry_s *ptr;
- ssize_t count = 0;
-
- if (map == NULL || key == NULL)
- return -EINVAL;
-
- hash = hashfunc (key, map->size, map->seed);
- if (hash < 0)
- return hash;
-
- ptr = map->buckets[hash].head;
-
- /* All right, there is an entry here, now see if it's the one we want */
- while (ptr) {
- if (strcasecmp (ptr->key, key) == 0)
- ++count;
-
- /* This entry didn't contain the key; move to the next one */
- ptr = ptr->next;
- }
-
- return count;
-}
-
-/*
- * Get the first entry (assuming there is more than one) for a particular
- * key. The data MUST be non-NULL.
- *
- * Returns: negative upon error
- * zero if no entry is found
- * length of data for the entry
- */
-ssize_t hashmap_entry_by_key (hashmap_t map, const char *key, void **data)
-{
- int hash;
- struct hashentry_s *ptr;
-
- if (!map || !key || !data)
- return -EINVAL;
-
- hash = hashfunc (key, map->size, map->seed);
- if (hash < 0)
- return hash;
-
- ptr = map->buckets[hash].head;
-
- while (ptr) {
- if (strcasecmp (ptr->key, key) == 0) {
- *data = ptr->data;
- return ptr->len;
- }
-
- ptr = ptr->next;
- }
-
- return 0;
-}
-
-/*
- * Go through the hashmap and remove the particular key.
- * NOTE: This will invalidate any iterators which have been created.
- *
- * Remove: negative upon error
- * 0 if the key was not found
- * positive count of entries deleted
- */
-ssize_t hashmap_remove (hashmap_t map, const char *key)
-{
- int hash;
- struct hashentry_s *ptr, *next;
- short int deleted = 0;
-
- if (map == NULL || key == NULL)
- return -EINVAL;
-
- hash = hashfunc (key, map->size, map->seed);
- if (hash < 0)
- return hash;
-
- ptr = map->buckets[hash].head;
- while (ptr) {
- if (strcasecmp (ptr->key, key) == 0) {
- /*
- * Found the data, now need to remove everything
- * and update the hashmap.
- */
- next = ptr->next;
-
- if (ptr->prev)
- ptr->prev->next = ptr->next;
- if (ptr->next)
- ptr->next->prev = ptr->prev;
-
- if (map->buckets[hash].head == ptr)
- map->buckets[hash].head = ptr->next;
- if (map->buckets[hash].tail == ptr)
- map->buckets[hash].tail = ptr->prev;
-
- safefree (ptr->key);
- safefree (ptr->data);
- safefree (ptr);
-
- ++deleted;
- --map->end_iterator;
-
- ptr = next;
- continue;
- }
-
- /* This entry didn't contain the key; move to the next one */
- ptr = ptr->next;
- }
-
- /* The key was not found, so return 0 */
- return deleted;
-}
-
-/*
- * Look up the value for a variable.
- */
-char *lookup_variable (hashmap_t map, const char *varname)
-{
- hashmap_iter result_iter;
- char *key;
- char *data;
-
- result_iter = hashmap_find (map, varname);
-
- if (hashmap_is_end (map, result_iter))
- return (NULL);
-
- if (hashmap_return_entry (map, result_iter,
- &key, (void **) &data) < 0)
- return (NULL);
-
- return (data);
-}
diff --git a/src/hashmap.h b/src/hashmap.h
deleted file mode 100644
index 9206737..0000000
--- a/src/hashmap.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* tinyproxy - A fast light-weight HTTP proxy
- * Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
- *
- * 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 of the License, 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* See 'hashmap.c' for detailed information. */
-
-#ifndef _HASHMAP_H
-#define _HASHMAP_H
-
-#include "common.h"
-
-/*
- * We're using a typedef here to "hide" the implementation details of the
- * hash map. Sure, it's a pointer, but the struct is hidden in the C file.
- * So, just use the hashmap_t like it's a cookie. :)
- */
-typedef struct hashmap_s *hashmap_t;
-typedef int hashmap_iter;
-
-/*
- * hashmap_create() takes one argument, which is the number of buckets to
- * use internally. hashmap_delete() is self explanatory.
- */
-extern hashmap_t hashmap_create (unsigned int nbuckets);
-extern int hashmap_delete (hashmap_t map);
-
-/*
- * When the you insert a key/data pair into the hashmap it will the key
- * and data are duplicated, so you must free your copy if it was created
- * on the heap. The key must be a NULL terminated string. "data" must be
- * non-NULL and length must be greater than zero.
- *
- * Returns: negative on error
- * 0 upon successful insert
- */
-extern int hashmap_insert (hashmap_t map, const char *key,
- const void *data, size_t len);
-
-/*
- * Get an iterator to the first entry.
- *
- * Returns: an negative value upon error.
- */
-extern hashmap_iter hashmap_first (hashmap_t map);
-
-/*
- * Checks to see if the iterator is pointing at the "end" of the entries.
- *
- * Returns: 1 if it is the end
- * 0 otherwise
- */
-extern int hashmap_is_end (hashmap_t map, hashmap_iter iter);
-
-/*
- * Return a "pointer" to the first instance of the particular key. It can
- * be tested against hashmap_is_end() to see if the key was not found.
- *
- * Returns: negative upon an error
- * an "iterator" pointing at the first key
- * an "end-iterator" if the key wasn't found
- */
-extern hashmap_iter hashmap_find (hashmap_t map, const char *key);
-
-/*
- * Retrieve the key/data associated with a particular iterator.
- * NOTE: These are pointers to the actual data, so don't mess around with them
- * too much.
- *
- * Returns: the length of the data block upon success
- * negative upon error
- */
-extern ssize_t hashmap_return_entry (hashmap_t map, hashmap_iter iter,
- char **key, void **data);
-
-/*
- * Get the first entry (assuming there is more than one) for a particular
- * key. The data MUST be non-NULL.
- *
- * Returns: negative upon error
- * zero if no entry is found
- * length of data for the entry
- */
-extern ssize_t hashmap_entry_by_key (hashmap_t map, const char *key,
- void **data);
-
-/*
- * Searches for _any_ occurrances of "key" within the hashmap and returns the
- * number of matching entries.
- *
- * Returns: negative upon an error
- * zero if no key is found
- * count found (positive value)
- */
-extern ssize_t hashmap_search (hashmap_t map, const char *key);
-
-/*
- * Go through the hashmap and remove the particular key.
- * NOTE: This will invalidate any iterators which have been created.
- *
- * Remove: negative upon error
- * 0 if the key was not found
- * positive count of entries deleted
- */
-extern ssize_t hashmap_remove (hashmap_t map, const char *key);
-
-/*
- * Look up the value for a variable.
- */
-extern char *lookup_variable (hashmap_t map, const char *varname);
-
-#endif /* _HASHMAP_H */
diff --git a/src/hostspec.c b/src/hostspec.c
new file mode 100644
index 0000000..adbad53
--- /dev/null
+++ b/src/hostspec.c
@@ -0,0 +1,166 @@
+#include "common.h"
+#include "hostspec.h"
+#include "heap.h"
+#include "network.h"
+
+/*
+ * Fills in the netmask array given a numeric value.
+ *
+ * Returns:
+ * 0 on success
+ * -1 on failure (invalid mask value)
+ *
+ */
+static int
+fill_netmask_array (char *bitmask_string, int v6,
+ unsigned char array[], size_t len)
+{
+ unsigned int i;
+ unsigned long int mask;
+ char *endptr;
+
+ errno = 0; /* to distinguish success/failure after call */
+ mask = strtoul (bitmask_string, &endptr, 10);
+
+ /* check for various conversion errors */
+ if ((errno == ERANGE && mask == ULONG_MAX)
+ || (errno != 0 && mask == 0) || (endptr == bitmask_string))
+ return -1;
+
+ if (v6 == 0) {
+ /* The mask comparison is done as an IPv6 address, so
+ * convert to a longer mask in the case of IPv4
+ * addresses. */
+ mask += 12 * 8;
+ }
+
+ /* check valid range for a bit mask */
+ if (mask > (8 * len))
+ return -1;
+
+ /* we have a valid range to fill in the array */
+ for (i = 0; i != len; ++i) {
+ if (mask >= 8) {
+ array[i] = 0xff;
+ mask -= 8;
+ } else if (mask > 0) {
+ array[i] = (unsigned char) (0xff << (8 - mask));
+ mask = 0;
+ } else {
+ array[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* parse a location string containing either an ipv4/ipv4 + hostmask tuple
+ or a dnsname into a struct hostspec.
+ returns 0 on success, non-0 on error (might be memory allocation, bogus
+ ip address or mask).
+*/
+int hostspec_parse(char *location, struct hostspec *h) {
+ char *mask, ip_dst[IPV6_LEN];
+
+ h->type = HST_NONE;
+ if(!location) return 0;
+
+ memset(h, 0, sizeof(*h));
+ if ((mask = strrchr(location, '/')))
+ *(mask++) = 0;
+
+ /*
+ * Check for a valid IP address (the simplest case) first.
+ */
+ if (full_inet_pton (location, ip_dst) > 0) {
+ h->type = HST_NUMERIC;
+ memcpy (h->address.ip.network, ip_dst, IPV6_LEN);
+ if(!mask) memset (h->address.ip.mask, 0xff, IPV6_LEN);
+ else {
+ char dst[sizeof(struct in6_addr)];
+ int v6, i;
+ /* Check if the IP address before the netmask is
+ * an IPv6 address */
+ if (inet_pton(AF_INET6, location, dst) > 0)
+ v6 = 1;
+ else
+ v6 = 0;
+
+ if (fill_netmask_array
+ (mask, v6, &(h->address.ip.mask[0]), IPV6_LEN)
+ < 0)
+ goto err;
+
+ for (i = 0; i < IPV6_LEN; i++)
+ h->address.ip.network[i] = ip_dst[i] &
+ h->address.ip.mask[i];
+ }
+ } else {
+ /* either bogus IP or hostname */
+ /* bogus ipv6 ? */
+ if (mask || strchr (location, ':'))
+ goto err;
+
+ /* In all likelihood a string */
+ h->type = HST_STRING;
+ h->address.string = safestrdup (location);
+ if (!h->address.string)
+ goto err;
+ }
+ /* restore mask */
+ if(mask) *(--mask) = '/';
+ return 0;
+err:;
+ if(mask) *(--mask) = '/';
+ return -1;
+}
+
+static int string_match(const char *ip, const char *addrspec)
+{
+ size_t test_length, match_length;
+ if(!strcasecmp(ip, addrspec)) return 1;
+ if(addrspec[0] != '.') return 0;
+ test_length = strlen (ip);
+ match_length = strlen (addrspec);
+ if (test_length < match_length) return 0;
+ return (strcasecmp
+ (ip + (test_length - match_length),
+ addrspec) == 0);
+}
+
+static int numeric_match(const uint8_t addr[], const struct hostspec *h)
+{
+ uint8_t x, y;
+ int i;
+
+ for (i = 0; i != IPV6_LEN; ++i) {
+ x = addr[i] & h->address.ip.mask[i];
+ y = h->address.ip.network[i];
+
+ /* If x and y don't match, the IP addresses don't match */
+ if (x != y)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* check whether ip matches hostspec.
+ return 1 on match, 0 on non-match */
+int hostspec_match(const char *ip, const struct hostspec *h) {
+ int is_numeric_addr;
+ uint8_t numeric_addr[IPV6_LEN];
+ if (ip[0] == '\0') return 0;
+ is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0);
+ switch (h->type) {
+ case HST_STRING:
+ if(is_numeric_addr) return 0;
+ return string_match (ip, h->address.string);
+ case HST_NUMERIC:
+ return numeric_match (numeric_addr, h);
+ case HST_NONE:
+ return 0;
+ }
+ return 0;
+}
diff --git a/src/hostspec.h b/src/hostspec.h
new file mode 100644
index 0000000..9d1d7bf
--- /dev/null
+++ b/src/hostspec.h
@@ -0,0 +1,26 @@
+#ifndef HOSTSPEC_H
+#define HOSTSPEC_H
+
+#define IPV6_LEN 16
+
+enum hostspec_type {
+ HST_NONE,
+ HST_STRING,
+ HST_NUMERIC,
+};
+
+struct hostspec {
+ enum hostspec_type type;
+ union {
+ char *string;
+ struct {
+ unsigned char network[IPV6_LEN];
+ unsigned char mask[IPV6_LEN];
+ } ip;
+ } address;
+};
+
+int hostspec_parse(char *domain, struct hostspec *h);
+int hostspec_match(const char *ip, const struct hostspec *h);
+
+#endif
diff --git a/src/hsearch.c b/src/hsearch.c
new file mode 100644
index 0000000..be0434c
--- /dev/null
+++ b/src/hsearch.c
@@ -0,0 +1,219 @@
+/*
+musl license, hsearch.c originally written by Szabolcs Nagy
+
+Copyright © 2005-2020 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "hsearch.h"
+
+/*
+open addressing hash table with 2^n table size
+quadratic probing is used in case of hash collision
+tab indices and hash are size_t
+after resize fails with ENOMEM the state of tab is still usable
+*/
+
+typedef struct htab_entry {
+ char *key;
+ htab_value data;
+} htab_entry;
+
+struct elem {
+ htab_entry item;
+ size_t hash;
+};
+
+struct htab {
+ struct elem *elems;
+ size_t mask;
+ size_t used;
+ size_t seed;
+ size_t dead;
+};
+
+#define MINSIZE 8
+#define MAXSIZE ((size_t)-1/2 + 1)
+
+#define CASE_INSENSITIVE
+#ifdef CASE_INSENSITIVE
+#include <ctype.h>
+#include <strings.h>
+#define LOWER_OR_NOT(X) tolower(X)
+#define STRCMP(X, Y) strcasecmp(X, Y)
+#else
+#define LOWER_OR_NOT(X) X
+#define STRCMP(X, Y) strcmp(X, Y)
+#endif
+
+static size_t keyhash(const char *k, size_t seed)
+{
+ const unsigned char *p = (const void *)k;
+ size_t h = seed;
+
+ while (*p)
+ h = 31*h + LOWER_OR_NOT(*p++);
+ return h;
+}
+
+static int resize(struct htab *htab, size_t nel)
+{
+ size_t newsize;
+ size_t i, j;
+ struct elem *e, *newe;
+ struct elem *oldtab = htab->elems;
+ struct elem *oldend = htab->elems + htab->mask + 1;
+
+ if (nel > MAXSIZE)
+ nel = MAXSIZE;
+ for (newsize = MINSIZE; newsize < nel; newsize *= 2);
+ htab->elems = calloc(newsize, sizeof *htab->elems);
+ if (!htab->elems) {
+ htab->elems = oldtab;
+ return 0;
+ }
+ htab->mask = newsize - 1;
+ if (!oldtab)
+ return 1;
+ for (e = oldtab; e < oldend; e++)
+ if (e->item.key) {
+ for (i=e->hash,j=1; ; i+=j++) {
+ newe = htab->elems + (i & htab->mask);
+ if (!newe->item.key)
+ break;
+ }
+ *newe = *e;
+ }
+ free(oldtab);
+ return 1;
+}
+
+static struct elem *lookup(struct htab *htab, const char *key, size_t hash, size_t dead)
+{
+ size_t i, j;
+ struct elem *e;
+
+ for (i=hash,j=1; ; i+=j++) {
+ e = htab->elems + (i & htab->mask);
+ if ((!e->item.key && (!e->hash || e->hash == dead)) ||
+ (e->hash==hash && STRCMP(e->item.key, key)==0))
+ break;
+ }
+ return e;
+}
+
+struct htab *htab_create(size_t nel)
+{
+ struct htab *r = calloc(1, sizeof *r);
+ if(r && !resize(r, nel)) {
+ free(r);
+ r = 0;
+ }
+ r->seed = rand();
+ return r;
+}
+
+void htab_destroy(struct htab *htab)
+{
+ free(htab->elems);
+ free(htab);
+}
+
+static struct elem *htab_find_elem(struct htab *htab, const char* key)
+{
+ size_t hash = keyhash(key, htab->seed);
+ struct elem *e = lookup(htab, key, hash, 0);
+
+ if (e->item.key) {
+ return e;
+ }
+ return 0;
+}
+
+htab_value* htab_find(struct htab *htab, const char* key)
+{
+ struct elem *e = htab_find_elem(htab, key);
+ if(!e) return 0;
+ return &e->item.data;
+}
+
+htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key)
+{
+ struct elem *e = htab_find_elem(htab, key);
+ if(!e) return 0;
+ *saved_key = e->item.key;
+ return &e->item.data;
+}
+
+int htab_delete(struct htab *htab, const char* key)
+{
+ struct elem *e = htab_find_elem(htab, key);
+ if(!e) return 0;
+ e->item.key = 0;
+ e->hash = 0xdeadc0de;
+ --htab->used;
+ ++htab->dead;
+ return 1;
+}
+
+int htab_insert(struct htab *htab, char* key, htab_value value)
+{
+ size_t hash = keyhash(key, htab->seed), oh;
+ struct elem *e = lookup(htab, key, hash, 0xdeadc0de);
+ if(e->item.key) {
+ /* it's not allowed to overwrite existing data */
+ return 0;
+ }
+
+ oh = e->hash; /* save old hash in case it's tombstone marker */
+ e->item.key = key;
+ e->item.data = value;
+ e->hash = hash;
+ if (++htab->used + htab->dead > htab->mask - htab->mask/4) {
+ if (!resize(htab, 2*htab->used)) {
+ htab->used--;
+ e->item.key = 0;
+ e->hash = oh;
+ return 0;
+ }
+ htab->dead = 0;
+ } else if (oh == 0xdeadc0de) {
+ /* re-used tomb */
+ --htab->dead;
+ }
+ return 1;
+}
+
+size_t htab_next(struct htab *htab, size_t iterator, char** key, htab_value **v)
+{
+ size_t i;
+ for(i=iterator;i<htab->mask+1;++i) {
+ struct elem *e = htab->elems + i;
+ if(e->item.key) {
+ *key = e->item.key;
+ *v = &e->item.data;
+ return i+1;
+ }
+ }
+ return 0;
+}
diff --git a/src/hsearch.h b/src/hsearch.h
new file mode 100644
index 0000000..7e9d770
--- /dev/null
+++ b/src/hsearch.h
@@ -0,0 +1,23 @@
+#ifndef HSEARCH_H
+#define HSEARCH_H
+
+#include <stdlib.h>
+
+typedef union htab_value {
+ void *p;
+ size_t n;
+} htab_value;
+
+#define HTV_N(N) (htab_value) {.n = N}
+#define HTV_P(P) (htab_value) {.p = P}
+
+struct htab * htab_create(size_t);
+void htab_destroy(struct htab *);
+htab_value* htab_find(struct htab *, const char* key);
+/* same as htab_find, but can retrieve the saved key (for freeing) */
+htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key);
+int htab_insert(struct htab *, char*, htab_value);
+int htab_delete(struct htab *htab, const char* key);
+size_t htab_next(struct htab *, size_t iterator, char** key, htab_value **v);
+
+#endif
diff --git a/src/html-error.c b/src/html-error.c
index 95ee9cf..998a6ee 100644
--- a/src/html-error.c
+++ b/src/html-error.c
@@ -20,9 +20,9 @@
* HTML error pages with variable substitution.
*/
+#include "common.h"
#include "main.h"
-#include "common.h"
#include "buffer.h"
#include "conns.h"
#include "heap.h"
@@ -31,25 +31,33 @@
#include "utils.h"
#include "conf.h"
+#include <regex.h>
+
/*
* Add an error number -> filename mapping to the errorpages list.
*/
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
#define ERRPAGES_BUCKETCOUNT 16
-int add_new_errorpage (char *filepath, unsigned int errornum)
+int add_new_errorpage (struct config_s *conf, char *filepath,
+ unsigned int errornum)
{
- char errornbuf[ERRORNUM_BUFSIZE];
+ char errornbuf[ERRORNUM_BUFSIZE], *k;
- config->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
- if (!config->errorpages)
+ if (!conf->errorpages)
+ conf->errorpages = htab_create (ERRPAGES_BUCKETCOUNT);
+ if (!conf->errorpages)
return (-1);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
- if (hashmap_insert (config->errorpages, errornbuf,
- filepath, strlen (filepath) + 1) < 0)
+ k = safestrdup(errornbuf);
+ if (!k) return -1;
+
+ if (!htab_insert (conf->errorpages, k, HTV_P(filepath))) {
+ safefree(k);
return (-1);
+ }
return (0);
}
@@ -59,10 +67,8 @@ int add_new_errorpage (char *filepath, unsigned int errornum)
*/
static char *get_html_file (unsigned int errornum)
{
- hashmap_iter result_iter;
char errornbuf[ERRORNUM_BUFSIZE];
- char *key;
- char *val;
+ htab_value *hv;
assert (errornum >= 100 && errornum < 1000);
@@ -71,16 +77,15 @@ static char *get_html_file (unsigned int errornum)
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
- result_iter = hashmap_find (config->errorpages, errornbuf);
-
- if (hashmap_is_end (config->errorpages, result_iter))
- return (config->errorpage_undef);
-
- if (hashmap_return_entry (config->errorpages, result_iter,
- &key, (void **) &val) < 0)
- return (config->errorpage_undef);
+ hv = htab_find (config->errorpages, errornbuf);
+ if (!hv) return (config->errorpage_undef);
+ return hv->p;
+}
- return (val);
+static char *lookup_variable (struct htab *map, const char *varname) {
+ htab_value *v;
+ v = htab_find(map, varname);
+ return v ? v->p : 0;
}
static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) {
@@ -128,7 +133,9 @@ send_html_file (FILE *infile, struct conn_s *connptr)
return 1;
}
-int send_http_headers (struct conn_s *connptr, int code, const char *message)
+int send_http_headers (
+ struct conn_s *connptr, int code,
+ const char *message, const char *extra)
{
const char headers[] =
"HTTP/1.%u %d %s\r\n"
@@ -137,22 +144,10 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message)
"%s"
"Connection: close\r\n" "\r\n";
- const char p_auth_str[] =
- "Proxy-Authenticate: Basic realm=\""
- PACKAGE_NAME "\"\r\n";
-
- const char w_auth_str[] =
- "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 : "");
-
return (write_message (connptr->client_fd, headers,
connptr->protocol.major != 1 ? 0 : connptr->protocol.minor,
code, message, PACKAGE, VERSION,
- add));
+ extra));
}
/*
@@ -176,8 +171,21 @@ int send_http_error_message (struct conn_s *connptr)
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n"
"</html>\n";
+ const char p_auth_str[] =
+ "Proxy-Authenticate: Basic realm=\""
+ PACKAGE_NAME "\"\r\n";
+
+ const char w_auth_str[] =
+ "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 = connptr->error_number == 407 ? p_auth_str :
+ (connptr->error_number == 401 ? w_auth_str : "");
+
send_http_headers (connptr, connptr->error_number,
- connptr->error_string);
+ connptr->error_string, add);
error_file = get_html_file (connptr->error_number);
if (!(infile = fopen (error_file, "r"))) {
@@ -203,14 +211,25 @@ int send_http_error_message (struct conn_s *connptr)
int
add_error_variable (struct conn_s *connptr, const char *key, const char *val)
{
+ char *k, *v;
+
if (!connptr->error_variables)
if (!
(connptr->error_variables =
- hashmap_create (ERRVAR_BUCKETCOUNT)))
+ htab_create (ERRVAR_BUCKETCOUNT)))
return (-1);
- return hashmap_insert (connptr->error_variables, key, val,
- strlen (val) + 1);
+ k = safestrdup(key);
+ v = safestrdup(val);
+
+ if (!v || !k) goto oom;
+
+ if(htab_insert (connptr->error_variables, k, HTV_P(v)))
+ return 1;
+oom:;
+ safefree(k);
+ safefree(v);
+ return -1;
}
#define ADD_VAR_RET(x, y) \
@@ -229,6 +248,7 @@ int add_standard_vars (struct conn_s *connptr)
char errnobuf[16];
char timebuf[30];
time_t global_time;
+ struct tm tm_buf;
snprintf (errnobuf, sizeof errnobuf, "%d", connptr->error_number);
ADD_VAR_RET ("errno", errnobuf);
@@ -244,7 +264,7 @@ int add_standard_vars (struct conn_s *connptr)
global_time = time (NULL);
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
- gmtime (&global_time));
+ gmtime_r (&global_time, &tm_buf));
add_error_variable (connptr, "date", timebuf);
add_error_variable (connptr, "website",
diff --git a/src/html-error.h b/src/html-error.h
index 03cec98..bc9b7ce 100644
--- a/src/html-error.h
+++ b/src/html-error.h
@@ -23,8 +23,9 @@
/* Forward declaration */
struct conn_s;
+struct config_s;
-extern int add_new_errorpage (char *filepath, unsigned int errornum);
+extern int add_new_errorpage (struct config_s *, char *filepath, unsigned int errornum);
extern int send_http_error_message (struct conn_s *connptr);
extern int indicate_http_error (struct conn_s *connptr, int number,
const char *message, ...);
@@ -32,7 +33,7 @@ extern int add_error_variable (struct conn_s *connptr, const char *key,
const char *val);
extern int send_html_file (FILE * infile, struct conn_s *connptr);
extern int send_http_headers (struct conn_s *connptr, int code,
- const char *message);
+ const char *message, const char *extra);
extern int add_standard_vars (struct conn_s *connptr);
#endif /* !TINYPROXY_HTML_ERROR_H */
diff --git a/src/http-message.c b/src/http-message.c
index 8b94f19..4ff37ae 100644
--- a/src/http-message.c
+++ b/src/http-message.c
@@ -232,6 +232,7 @@ int http_message_send (http_message_t msg, int fd)
char timebuf[30];
time_t global_time;
unsigned int i;
+ struct tm tm_buf;
assert (is_http_message_valid (msg));
@@ -254,11 +255,11 @@ int http_message_send (http_message_t msg, int fd)
/* Output the date */
global_time = time (NULL);
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
- gmtime (&global_time));
+ gmtime_r (&global_time, &tm_buf));
write_message (fd, "Date: %s\r\n", timebuf);
/* Output the content-length */
- write_message (fd, "Content-length: %u\r\n", msg->body.length);
+ write_message (fd, "Content-length: %lu\r\n", (unsigned long) msg->body.length);
/* Write the separator between the headers and body */
safe_write (fd, "\r\n", 2);
diff --git a/src/log.c b/src/log.c
index 755a913..14fc3fe 100644
--- a/src/log.c
+++ b/src/log.c
@@ -27,7 +27,7 @@
#include "heap.h"
#include "log.h"
#include "utils.h"
-#include "vector.h"
+#include "sblist.h"
#include "conf.h"
#include <pthread.h>
@@ -64,7 +64,7 @@ static int log_level = LOG_INFO;
* The key is the actual messages (already filled in full), and the value
* is the log level.
*/
-static vector_t log_message_storage;
+static sblist *log_message_storage;
static unsigned int logging_initialized = FALSE; /* boolean */
@@ -108,7 +108,8 @@ void set_log_level (int level)
void log_message (int level, const char *fmt, ...)
{
va_list args;
- time_t nowtime;
+ struct timespec nowtime;
+ struct tm tm_buf;
char time_string[TIME_LENGTH];
char str[STRING_LENGTH];
@@ -142,7 +143,7 @@ void log_message (int level, const char *fmt, ...)
char *entry_buffer;
if (!log_message_storage) {
- log_message_storage = vector_create ();
+ log_message_storage = sblist_new (sizeof(char*), 64);
if (!log_message_storage)
goto out;
}
@@ -154,10 +155,8 @@ void log_message (int level, const char *fmt, ...)
goto out;
sprintf (entry_buffer, "%d %s", level, str);
- vector_append (log_message_storage, entry_buffer,
- strlen (entry_buffer) + 1);
-
- safefree (entry_buffer);
+ if(!sblist_add (log_message_storage, &entry_buffer))
+ safefree (entry_buffer);
goto out;
}
@@ -176,13 +175,14 @@ void log_message (int level, const char *fmt, ...)
} else {
char *p;
- nowtime = time (NULL);
+ clock_gettime(CLOCK_REALTIME, &nowtime);
/* Format is month day hour:minute:second (24 time) */
strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S",
- localtime (&nowtime));
+ localtime_r (&nowtime.tv_sec, &tm_buf));
- snprintf (str, STRING_LENGTH, "%-9s %s [%ld]: ",
+ snprintf (str, STRING_LENGTH, "%-9s %s.%03lu [%ld]: ",
syslog_level[level], time_string,
+ (unsigned long) nowtime.tv_nsec/1000000ul,
(long int) getpid ());
/*
@@ -227,7 +227,7 @@ out:
*/
static void send_stored_logs (void)
{
- char *string;
+ char **string;
char *ptr;
int level;
size_t i;
@@ -237,12 +237,12 @@ static void send_stored_logs (void)
log_message(LOG_DEBUG, "sending stored logs");
- for (i = 0; (ssize_t) i != vector_length (log_message_storage); ++i) {
- string =
- (char *) vector_getentry (log_message_storage, i, NULL);
+ for (i = 0; i < sblist_getsize (log_message_storage); ++i) {
+ string = sblist_get (log_message_storage, i);
+ if (!string || !*string) continue;
- ptr = strchr (string, ' ') + 1;
- level = atoi (string);
+ ptr = strchr (*string, ' ') + 1;
+ level = atoi (*string);
#ifdef NDEBUG
if (log_level == LOG_CONN && level == LOG_INFO)
@@ -255,9 +255,10 @@ static void send_stored_logs (void)
#endif
log_message (level, "%s", ptr);
+ safefree(*string);
}
- vector_delete (log_message_storage);
+ sblist_free (log_message_storage);
log_message_storage = NULL;
log_message(LOG_DEBUG, "done sending stored logs");
diff --git a/src/loop.c b/src/loop.c
index 77b073d..d696760 100644
--- a/src/loop.c
+++ b/src/loop.c
@@ -18,6 +18,11 @@ void loop_records_init(void) {
loop_records = sblist_new(sizeof (struct loop_record), 32);
}
+void loop_records_destroy(void) {
+ sblist_free(loop_records);
+ loop_records = 0;
+}
+
#if 0
static void su_to_str(union sockaddr_union *addr, char *buf) {
int af = addr->v4.sin_family;
diff --git a/src/loop.h b/src/loop.h
index 19877d3..27a26c7 100644
--- a/src/loop.h
+++ b/src/loop.h
@@ -4,6 +4,7 @@
#include "sock.h"
void loop_records_init(void);
+void loop_records_destroy(void);
void loop_records_add(union sockaddr_union *addr);
int connection_loops (union sockaddr_union *addr);
diff --git a/src/main.c b/src/main.c
index 7b702ab..1309284 100644
--- a/src/main.c
+++ b/src/main.c
@@ -38,6 +38,7 @@
#include "heap.h"
#include "filter.h"
#include "child.h"
+#include "loop.h"
#include "log.h"
#include "reqs.h"
#include "sock.h"
@@ -256,6 +257,8 @@ int reload_config (int reload_logging)
int ret;
struct config_s *c_next = get_next_config();
+ log_message (LOG_NOTICE, "Reloading config file");
+
if (reload_logging) shutdown_logging ();
ret = reload_config_file (config_file, c_next);
@@ -264,9 +267,11 @@ int reload_config (int reload_logging)
goto done;
}
+ if(config) free_config (config);
config = c_next;
if (reload_logging) ret = setup_logging ();
+ log_message (LOG_NOTICE, "Reloading config file finished");
done:
return ret;
@@ -295,7 +300,8 @@ main (int argc, char **argv)
log_message (LOG_NOTICE, "Initializing " PACKAGE " ...");
- if (config_compile_regex()) {
+ if (config_init()) {
+ fprintf(stderr, "ERROR: config_init() failed\n");
exit (EX_SOFTWARE);
}
@@ -336,8 +342,8 @@ main (int argc, char **argv)
* HTTP/1.0 request. Also add the Content-Type header since it
* goes hand in hand with Content-Length. */
if (is_anonymous_enabled (config)) {
- anonymous_insert (config, "Content-Length");
- anonymous_insert (config, "Content-Type");
+ anonymous_insert (config, safestrdup("Content-Length"));
+ anonymous_insert (config, safestrdup("Content-Type"));
}
if (daemonized == TRUE) {
@@ -375,7 +381,7 @@ main (int argc, char **argv)
if (geteuid () == 0)
change_user (argv[0]);
else
- log_message (LOG_WARNING,
+ log_message (LOG_INFO,
"Not running as root, so not changing UID/GID.");
/* Create log file after we drop privileges */
@@ -388,9 +394,12 @@ main (int argc, char **argv)
setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]);
setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]);
+ setup_sig (SIGINT, takesig, "SIGINT", argv[0]);
if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]);
setup_sig (SIGUSR1, takesig, "SIGUSR1", argv[0]);
+ loop_records_init();
+
/* Start the main loop */
log_message (LOG_INFO, "Starting main loop. Accepting connections.");
@@ -400,6 +409,9 @@ main (int argc, char **argv)
child_kill_children (SIGTERM);
child_close_sock ();
+ child_free_children();
+
+ loop_records_destroy();
/* Remove the PID file */
if (config->pidpath != NULL && unlink (config->pidpath) < 0) {
@@ -413,6 +425,8 @@ main (int argc, char **argv)
filter_destroy ();
#endif /* FILTER_ENABLE */
+ free_config (config);
+
shutdown_logging ();
return EXIT_SUCCESS;
diff --git a/src/mypoll.c b/src/mypoll.c
new file mode 100644
index 0000000..495e2c3
--- /dev/null
+++ b/src/mypoll.c
@@ -0,0 +1,48 @@
+#include "mypoll.h"
+
+#ifdef HAVE_POLL_H
+int mypoll(pollfd_struct* fds, int nfds, int timeout) {
+ int i, ret;
+ for(i=0; i<nfds; ++i) if(!fds[i].events) fds[i].fd=~fds[i].fd;
+ ret = poll(fds, nfds, timeout <= 0 ? timeout : timeout*1000);
+ for(i=0; i<nfds; ++i) if(!fds[i].events) fds[i].fd=~fds[i].fd;
+ return ret;
+}
+#else
+int mypoll(pollfd_struct* fds, int nfds, int timeout) {
+ fd_set rset, wset, *r=0, *w=0;
+ int i, ret, maxfd=-1;
+ struct timeval tv = {0}, *t = 0;
+
+ for(i=0; i<nfds; ++i) {
+ if(fds[i].events & MYPOLL_READ) r = &rset;
+ if(fds[i].events & MYPOLL_WRITE) w = &wset;
+ if(r && w) break;
+ }
+ if(r) FD_ZERO(r);
+ if(w) FD_ZERO(w);
+ for(i=0; i<nfds; ++i) {
+ if(fds[i].fd > maxfd) maxfd = fds[i].fd;
+ if(fds[i].events & MYPOLL_READ) FD_SET(fds[i].fd, r);
+ if(fds[i].events & MYPOLL_WRITE) FD_SET(fds[i].fd, w);
+ }
+
+ if(timeout >= 0) t = &tv;
+ if(timeout > 0) tv.tv_sec = timeout;
+
+ ret = select(maxfd+1, r, w, 0, t);
+
+ switch(ret) {
+ case -1:
+ case 0:
+ return ret;
+ }
+
+ for(i=0; i<nfds; ++i) {
+ fds[i].revents = 0;
+ if(r && FD_ISSET(fds[i].fd, r)) fds[i].revents |= MYPOLL_READ;
+ if(w && FD_ISSET(fds[i].fd, w)) fds[i].revents |= MYPOLL_WRITE;
+ }
+ return ret;
+}
+#endif
diff --git a/src/mypoll.h b/src/mypoll.h
new file mode 100644
index 0000000..8736015
--- /dev/null
+++ b/src/mypoll.h
@@ -0,0 +1,31 @@
+#ifndef MYPOLL_H
+#define MYPOLL_H
+
+#include "config.h"
+
+#ifdef HAVE_POLL_H
+#define SELECT_OR_POLL "poll"
+
+#include <poll.h>
+typedef struct pollfd pollfd_struct;
+
+#define MYPOLL_READ POLLIN
+#define MYPOLL_WRITE POLLOUT
+
+#else
+
+#define SELECT_OR_POLL "select"
+#include <sys/select.h>
+typedef struct mypollfd {
+ int fd;
+ short events;
+ short revents;
+} pollfd_struct;
+
+#define MYPOLL_READ (1<<1)
+#define MYPOLL_WRITE (1<<2)
+#endif
+
+int mypoll(pollfd_struct* fds, int nfds, int timeout);
+
+#endif
diff --git a/src/orderedmap.c b/src/orderedmap.c
new file mode 100644
index 0000000..1818e27
--- /dev/null
+++ b/src/orderedmap.c
@@ -0,0 +1,115 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#else
+# define _ALL_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <string.h>
+#include "sblist.h"
+#include "orderedmap.h"
+
+static void orderedmap_destroy_contents(struct orderedmap *o) {
+ char **p, *q;
+ size_t i;
+ htab_value *v;
+ if(!o) return;
+ if(o->values) {
+ while(sblist_getsize(o->values)) {
+ p = sblist_get(o->values, 0);
+ if(p) free(*p);
+ sblist_delete(o->values, 0);
+ }
+ sblist_free(o->values);
+ }
+ if(o->map) {
+ i = 0;
+ while((i = htab_next(o->map, i, &q, &v)))
+ free(q);
+ htab_destroy(o->map);
+ }
+}
+
+struct orderedmap *orderedmap_create(size_t nbuckets) {
+ struct orderedmap o = {0}, *new;
+ o.values = sblist_new(sizeof(void*), 32);
+ if(!o.values) goto oom;
+ o.map = htab_create(nbuckets);
+ if(!o.map) goto oom;
+ new = malloc(sizeof o);
+ if(!new) goto oom;
+ memcpy(new, &o, sizeof o);
+ return new;
+ oom:;
+ orderedmap_destroy_contents(&o);
+ return 0;
+}
+
+void* orderedmap_destroy(struct orderedmap *o) {
+ orderedmap_destroy_contents(o);
+ free(o);
+ return 0;
+}
+
+int orderedmap_append(struct orderedmap *o, const char *key, char *value) {
+ size_t index;
+ char *nk, *nv;
+ nk = nv = 0;
+ nk = strdup(key);
+ nv = strdup(value);
+ if(!nk || !nv) goto oom;
+ index = sblist_getsize(o->values);
+ if(!sblist_add(o->values, &nv)) goto oom;
+ if(!htab_insert(o->map, nk, HTV_N(index))) {
+ sblist_delete(o->values, index);
+ goto oom;
+ }
+ return 1;
+oom:;
+ free(nk);
+ free(nv);
+ return 0;
+}
+
+char* orderedmap_find(struct orderedmap *o, const char *key) {
+ char **p;
+ htab_value *v = htab_find(o->map, key);
+ if(!v) return 0;
+ p = sblist_get(o->values, v->n);
+ return p?*p:0;
+}
+
+int orderedmap_remove(struct orderedmap *o, const char *key) {
+ size_t i;
+ char *lk;
+ char *sk;
+ char **sv;
+ htab_value *lv, *v = htab_find2(o->map, key, &sk);
+ if(!v) return 0;
+ sv = sblist_get(o->values, v->n);
+ free(*sv);
+ sblist_delete(o->values, v->n);
+ i = 0;
+ while((i = htab_next(o->map, i, &lk, &lv))) {
+ if(lv->n > v->n) lv->n--;
+ }
+ htab_delete(o->map, key);
+ free(sk);
+ return 1;
+}
+
+size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value) {
+ size_t h_iter;
+ htab_value* hval;
+ char **p;
+ if(iter < sblist_getsize(o->values)) {
+ h_iter = 0;
+ while((h_iter = htab_next(o->map, h_iter, key, &hval))) {
+ if(hval->n == iter) {
+ p = sblist_get(o->values, iter);
+ *value = p?*p:0;
+ return iter+1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/src/orderedmap.h b/src/orderedmap.h
new file mode 100644
index 0000000..e3f4b0e
--- /dev/null
+++ b/src/orderedmap.h
@@ -0,0 +1,20 @@
+#ifndef ORDEREDMAP_H
+#define ORDEREDMAP_H
+
+#include <stdlib.h>
+#include "sblist.h"
+#include "hsearch.h"
+
+typedef struct orderedmap {
+ sblist* values;
+ struct htab *map;
+} *orderedmap;
+
+struct orderedmap *orderedmap_create(size_t nbuckets);
+void* orderedmap_destroy(struct orderedmap *o);
+int orderedmap_append(struct orderedmap *o, const char *key, char *value );
+char* orderedmap_find(struct orderedmap *o, const char *key);
+int orderedmap_remove(struct orderedmap *o, const char *key);
+size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value);
+
+#endif
diff --git a/src/reqs.c b/src/reqs.c
index 76209db..2671efd 100644
--- a/src/reqs.c
+++ b/src/reqs.c
@@ -32,7 +32,8 @@
#include "buffer.h"
#include "conns.h"
#include "filter.h"
-#include "hashmap.h"
+#include "hsearch.h"
+#include "orderedmap.h"
#include "heap.h"
#include "html-error.h"
#include "log.h"
@@ -42,7 +43,7 @@
#include "stats.h"
#include "text.h"
#include "utils.h"
-#include "vector.h"
+#include "sblist.h"
#include "reverse-proxy.h"
#include "transparent-proxy.h"
#include "upstream.h"
@@ -50,6 +51,7 @@
#include "conf.h"
#include "basicauth.h"
#include "loop.h"
+#include "mypoll.h"
/*
* Maximum length of a HTTP line
@@ -321,7 +323,7 @@ static int send_ssl_response (struct conn_s *connptr)
* build a new request line. Finally connect to the remote server.
*/
static struct request_s *process_request (struct conn_s *connptr,
- hashmap_t hashofheaders)
+ orderedmap hashofheaders)
{
char *url;
struct request_s *request;
@@ -387,10 +389,17 @@ BAD_REQUEST_ERROR:
* we'll be closing anyway.
*/
char *reverse_url;
+ int reverse_status;
- reverse_url = reverse_rewrite_url (connptr, hashofheaders, url);
+ reverse_url = reverse_rewrite_url (connptr, hashofheaders, url, &reverse_status);
if (reverse_url != NULL) {
+ if (reverse_status == 301) {
+ char buf[PATH_MAX];
+ snprintf (buf, sizeof buf, "Location: %s\r\n", reverse_url);
+ send_http_headers (connptr, 301, "Moved Permanently", buf);
+ goto fail;
+ }
safefree (url);
url = reverse_url;
} else if (config->reverseonly) {
@@ -602,7 +611,7 @@ static int add_xtinyproxy_header (struct conn_s *connptr)
* can be retrieved and manipulated later.
*/
static int
-add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len)
+add_header_to_connection (orderedmap hashofheaders, char *header, size_t len)
{
char *sep;
@@ -620,7 +629,7 @@ add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len)
/* Calculate the new length of just the data */
len -= sep - header - 1;
- return hashmap_insert (hashofheaders, header, sep, len);
+ return orderedmap_append (hashofheaders, header, sep);
}
/*
@@ -633,7 +642,7 @@ add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len)
/*
* Read all the headers from the stream
*/
-static int get_all_headers (int fd, hashmap_t hashofheaders)
+static int get_all_headers (int fd, orderedmap hashofheaders)
{
char *line = NULL;
char *header = NULL;
@@ -726,7 +735,7 @@ static int get_all_headers (int fd, hashmap_t hashofheaders)
* Extract the headers to remove. These headers were listed in the Connection
* and Proxy-Connection headers.
*/
-static int remove_connection_headers (hashmap_t hashofheaders)
+static int remove_connection_headers (orderedmap hashofheaders)
{
static const char *headers[] = {
"connection",
@@ -740,12 +749,13 @@ static int remove_connection_headers (hashmap_t hashofheaders)
for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i) {
/* Look for the connection header. If it's not found, return. */
- len =
- hashmap_entry_by_key (hashofheaders, headers[i],
- (void **) &data);
- if (len <= 0)
+ data = orderedmap_find(hashofheaders, headers[i]);
+
+ if (!data)
return 0;
+ len = strlen(data);
+
/*
* Go through the data line and replace any special characters
* with a NULL.
@@ -760,7 +770,7 @@ static int remove_connection_headers (hashmap_t hashofheaders)
*/
ptr = data;
while (ptr < data + len) {
- hashmap_remove (hashofheaders, ptr);
+ orderedmap_remove (hashofheaders, ptr);
/* Advance ptr to the next token */
ptr += strlen (ptr) + 1;
@@ -769,7 +779,7 @@ static int remove_connection_headers (hashmap_t hashofheaders)
}
/* Now remove the connection header it self. */
- hashmap_remove (hashofheaders, headers[i]);
+ orderedmap_remove (hashofheaders, headers[i]);
}
return 0;
@@ -779,16 +789,14 @@ static int remove_connection_headers (hashmap_t hashofheaders)
* If there is a Content-Length header, then return the value; otherwise, return
* a negative number.
*/
-static long get_content_length (hashmap_t hashofheaders)
+static long get_content_length (orderedmap hashofheaders)
{
- ssize_t len;
char *data;
long content_length = -1;
- len =
- hashmap_entry_by_key (hashofheaders, "content-length",
- (void **) &data);
- if (len > 0)
+ data = orderedmap_find (hashofheaders, "content-length");
+
+ if (data)
content_length = atol (data);
return content_length;
@@ -802,10 +810,9 @@ static long get_content_length (hashmap_t hashofheaders)
* purposes.
*/
static int
-write_via_header (int fd, hashmap_t hashofheaders,
+write_via_header (int fd, orderedmap hashofheaders,
unsigned int major, unsigned int minor)
{
- ssize_t len;
char hostname[512];
char *data;
int ret;
@@ -825,14 +832,14 @@ write_via_header (int fd, hashmap_t hashofheaders,
* See if there is a "Via" header. If so, again we need to do a bit
* of processing.
*/
- len = hashmap_entry_by_key (hashofheaders, "via", (void **) &data);
- if (len > 0) {
+ data = orderedmap_find (hashofheaders, "via");
+ if (data) {
ret = write_message (fd,
"Via: %s, %hu.%hu %s (%s/%s)\r\n",
data, major, minor, hostname, PACKAGE,
VERSION);
- hashmap_remove (hashofheaders, "via");
+ orderedmap_remove (hashofheaders, "via");
} else {
ret = write_message (fd,
"Via: %hu.%hu %s (%s/%s)\r\n",
@@ -846,7 +853,7 @@ done:
/*
* Number of buckets to use internally in the hashmap.
*/
-#define HEADER_BUCKETS 256
+#define HEADER_BUCKETS 32
/*
* Here we loop through all the headers the client is sending. If we
@@ -855,7 +862,7 @@ done:
* - rjkaes
*/
static int
-process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
+process_client_headers (struct conn_s *connptr, orderedmap hashofheaders)
{
static const char *skipheaders[] = {
"host",
@@ -866,7 +873,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
"upgrade"
};
int i;
- hashmap_iter iter;
+ size_t iter;
int ret = 0;
char *data, *header;
@@ -899,7 +906,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
* Delete the headers listed in the skipheaders list
*/
for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) {
- hashmap_remove (hashofheaders, skipheaders[i]);
+ orderedmap_remove (hashofheaders, skipheaders[i]);
}
/* Send, or add the Via header */
@@ -919,27 +926,22 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
/*
* Output all the remaining headers to the remote machine.
*/
- iter = hashmap_first (hashofheaders);
- if (iter >= 0) {
- for (; !hashmap_is_end (hashofheaders, iter); ++iter) {
- hashmap_return_entry (hashofheaders,
- iter, &data, (void **) &header);
-
- if (!is_anonymous_enabled (config)
- || anonymous_search (config, data) > 0) {
- ret =
- write_message (connptr->server_fd,
- "%s: %s\r\n", data, header);
- if (ret < 0) {
- indicate_http_error (connptr, 503,
- "Could not send data to remote server",
- "detail",
- "A network error occurred while "
- "trying to write data to the "
- "remote web server.",
- NULL);
- goto PULL_CLIENT_DATA;
- }
+ iter = 0;
+ while((iter = orderedmap_next(hashofheaders, iter, &data, &header))) {
+ if (!is_anonymous_enabled (config)
+ || anonymous_search (config, data) > 0) {
+ ret =
+ write_message (connptr->server_fd,
+ "%s: %s\r\n", data, header);
+ if (ret < 0) {
+ indicate_http_error (connptr, 503,
+ "Could not send data to remote server",
+ "detail",
+ "A network error occurred while "
+ "trying to write data to the "
+ "remote web server.",
+ NULL);
+ goto PULL_CLIENT_DATA;
}
}
}
@@ -979,8 +981,8 @@ static int process_server_headers (struct conn_s *connptr)
char *response_line;
- hashmap_t hashofheaders;
- hashmap_iter iter;
+ orderedmap hashofheaders;
+ size_t iter;
char *data, *header;
ssize_t len;
int i;
@@ -1009,7 +1011,7 @@ retry:
goto retry;
}
- hashofheaders = hashmap_create (HEADER_BUCKETS);
+ hashofheaders = orderedmap_create (HEADER_BUCKETS);
if (!hashofheaders) {
safefree (response_line);
return -1;
@@ -1021,7 +1023,7 @@ retry:
if (get_all_headers (connptr->server_fd, hashofheaders) < 0) {
log_message (LOG_WARNING,
"Could not retrieve all the headers from the remote server.");
- hashmap_delete (hashofheaders);
+ orderedmap_destroy (hashofheaders);
safefree (response_line);
indicate_http_error (connptr, 503,
@@ -1040,7 +1042,7 @@ retry:
* Instead we'll free all the memory and return.
*/
if (connptr->protocol.major < 1) {
- hashmap_delete (hashofheaders);
+ orderedmap_destroy (hashofheaders);
safefree (response_line);
return 0;
}
@@ -1067,7 +1069,7 @@ retry:
* Delete the headers listed in the skipheaders list
*/
for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) {
- hashmap_remove (hashofheaders, skipheaders[i]);
+ orderedmap_remove (hashofheaders, skipheaders[i]);
}
/* Send, or add the Via header */
@@ -1089,8 +1091,7 @@ retry:
/* Rewrite the HTTP redirect if needed */
if (config->reversebaseurl &&
- hashmap_entry_by_key (hashofheaders, "location",
- (void **) &header) > 0) {
+ (header = orderedmap_find (hashofheaders, "location"))) {
/* Look for a matching entry in the reversepath list */
while (reverse) {
@@ -1115,7 +1116,7 @@ retry:
"Rewriting HTTP redirect: %s -> %s%s%s",
header, config->reversebaseurl,
(reverse->path + 1), (header + len));
- hashmap_remove (hashofheaders, "location");
+ orderedmap_remove (hashofheaders, "location");
}
}
#endif
@@ -1123,19 +1124,15 @@ retry:
/*
* All right, output all the remaining headers to the client.
*/
- iter = hashmap_first (hashofheaders);
- if (iter >= 0) {
- for (; !hashmap_is_end (hashofheaders, iter); ++iter) {
- hashmap_return_entry (hashofheaders,
- iter, &data, (void **) &header);
-
- ret = write_message (connptr->client_fd,
- "%s: %s\r\n", data, header);
- if (ret < 0)
- goto ERROR_EXIT;
- }
+ iter = 0;
+ while ((iter = orderedmap_next(hashofheaders, iter, &data, &header))) {
+
+ ret = write_message (connptr->client_fd,
+ "%s: %s\r\n", data, header);
+ if (ret < 0)
+ goto ERROR_EXIT;
}
- hashmap_delete (hashofheaders);
+ orderedmap_destroy (hashofheaders);
/* Write the final blank line to signify the end of the headers */
if (safe_write (connptr->client_fd, "\r\n", 2) < 0)
@@ -1144,7 +1141,7 @@ retry:
return 0;
ERROR_EXIT:
- hashmap_delete (hashofheaders);
+ orderedmap_destroy (hashofheaders);
return -1;
}
@@ -1158,74 +1155,39 @@ ERROR_EXIT:
*/
static void relay_connection (struct conn_s *connptr)
{
- fd_set rset, wset;
- struct timeval tv;
- time_t last_access;
int ret;
- double tdiff;
- int maxfd = max (connptr->client_fd, connptr->server_fd) + 1;
ssize_t bytes_received;
- 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));
- 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));
- return;
- }
-
- last_access = time (NULL);
-
for (;;) {
- FD_ZERO (&rset);
- FD_ZERO (&wset);
-
- tv.tv_sec =
- config->idletimeout - difftime (time (NULL), last_access);
- tv.tv_usec = 0;
+ pollfd_struct fds[2] = {0};
+ fds[0].fd = connptr->client_fd;
+ fds[1].fd = connptr->server_fd;
if (buffer_size (connptr->sbuffer) > 0)
- FD_SET (connptr->client_fd, &wset);
+ fds[0].events |= MYPOLL_WRITE;
if (buffer_size (connptr->cbuffer) > 0)
- FD_SET (connptr->server_fd, &wset);
+ fds[1].events |= MYPOLL_WRITE;
if (buffer_size (connptr->sbuffer) < MAXBUFFSIZE)
- FD_SET (connptr->server_fd, &rset);
+ fds[1].events |= MYPOLL_READ;
if (buffer_size (connptr->cbuffer) < MAXBUFFSIZE)
- FD_SET (connptr->client_fd, &rset);
+ fds[0].events |= MYPOLL_READ;
- ret = select (maxfd, &rset, &wset, NULL, &tv);
+ ret = mypoll(fds, 2, config->idletimeout);
if (ret == 0) {
- tdiff = difftime (time (NULL), last_access);
- if (tdiff > config->idletimeout) {
- log_message (LOG_INFO,
- "Idle Timeout (after select) as %g > %u.",
- tdiff, config->idletimeout);
+ log_message (LOG_INFO,
+ "Idle Timeout (after " SELECT_OR_POLL ")");
return;
- } else {
- continue;
- }
} else if (ret < 0) {
log_message (LOG_ERR,
- "relay_connection: select() error \"%s\". "
+ "relay_connection: " SELECT_OR_POLL "() error \"%s\". "
"Closing connection (client_fd:%d, server_fd:%d)",
strerror (errno), connptr->client_fd,
connptr->server_fd);
return;
- } else {
- /*
- * All right, something was actually selected so mark it.
- */
- last_access = time (NULL);
}
- if (FD_ISSET (connptr->server_fd, &rset)) {
+ if (fds[1].revents & MYPOLL_READ) {
bytes_received =
read_buffer (connptr->server_fd, connptr->sbuffer);
if (bytes_received < 0)
@@ -1235,32 +1197,20 @@ static void relay_connection (struct conn_s *connptr)
if (connptr->content_length.server == 0)
break;
}
- if (FD_ISSET (connptr->client_fd, &rset)
+ if ((fds[0].revents & MYPOLL_READ)
&& read_buffer (connptr->client_fd, connptr->cbuffer) < 0) {
break;
}
- if (FD_ISSET (connptr->server_fd, &wset)
+ if ((fds[1].revents & MYPOLL_WRITE)
&& write_buffer (connptr->server_fd, connptr->cbuffer) < 0) {
break;
}
- if (FD_ISSET (connptr->client_fd, &wset)
+ if ((fds[0].revents & MYPOLL_WRITE)
&& write_buffer (connptr->client_fd, connptr->sbuffer) < 0) {
break;
}
}
- /*
- * Here the server has closed the connection... write the
- * remainder to the client and then exit.
- */
- ret = socket_blocking (connptr->client_fd);
- if (ret != 0) {
- log_message(LOG_ERR,
- "Failed to set client socket to blocking: %s",
- strerror(errno));
- return;
- }
-
while (buffer_size (connptr->sbuffer) > 0) {
if (write_buffer (connptr->client_fd, connptr->sbuffer) < 0)
break;
@@ -1431,7 +1381,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request)
connptr->server_fd =
opensock (cur_upstream->host, cur_upstream->port,
- connptr->server_ip_addr);
+ connptr->server_ip_addr, NULL);
if (connptr->server_fd < 0) {
log_message (LOG_WARNING,
@@ -1487,27 +1437,27 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request)
#endif
}
+/* this function "drains" remaining bytes in the read pipe from
+ the client. it's usually only called on error before displaying
+ an error code/page. */
static int
get_request_entity(struct conn_s *connptr)
{
int ret;
- fd_set rset, wset;
- struct timeval tv;
+ pollfd_struct fds[1] = {0};
- FD_ZERO (&rset);
- FD_SET (connptr->client_fd, &rset);
- memcpy(&wset, &rset, sizeof wset);
- tv.tv_sec = config->idletimeout;
- tv.tv_usec = 0;
- ret = select (connptr->client_fd + 1, &rset, &wset, NULL, &tv);
+ fds[0].fd = connptr->client_fd;
+ fds[0].events |= MYPOLL_READ;
+
+ ret = mypoll(fds, 1, config->idletimeout);
if (ret == -1) {
log_message (LOG_ERR,
- "Error calling select on client fd %d: %s",
+ "Error calling " SELECT_OR_POLL " on client fd %d: %s",
connptr->client_fd, strerror(errno));
} else if (ret == 0) {
log_message (LOG_INFO, "no entity");
- } else if (ret == 1 && FD_ISSET (connptr->client_fd, &rset)) {
+ } else if (ret == 1 && (fds[0].revents & MYPOLL_READ)) {
ssize_t nread;
nread = read_buffer (connptr->client_fd, connptr->cbuffer);
if (nread < 0) {
@@ -1517,14 +1467,12 @@ 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 %ld bytes",
+ (long) nread);
ret = 0;
}
- } else if (ret == 1 && FD_ISSET (connptr->client_fd, &wset) && connptr->connect_method) {
- ret = 0;
} else {
- log_message (LOG_ERR, "strange situation after select: "
+ log_message (LOG_ERR, "strange situation after " SELECT_OR_POLL ": "
"ret = %d, but client_fd (%d) is not readable...",
ret, connptr->client_fd);
ret = -1;
@@ -1533,6 +1481,31 @@ get_request_entity(struct conn_s *connptr)
return ret;
}
+static void handle_connection_failure(struct conn_s *connptr, int got_headers)
+{
+ /*
+ * First, get the body if there is one.
+ * If we don't read all there is from the socket first,
+ * it is still marked for reading and we won't be able
+ * to send our data properly.
+ */
+ if (!got_headers && get_request_entity (connptr) < 0) {
+ log_message (LOG_WARNING,
+ "Could not retrieve request entity");
+ indicate_http_error (connptr, 400, "Bad Request",
+ "detail",
+ "Could not retrieve the request entity "
+ "the client.", NULL);
+ update_stats (STAT_BADCONN);
+ }
+
+ if (connptr->error_variables) {
+ send_http_error_message (connptr);
+ } else if (connptr->show_stats) {
+ showstats (connptr);
+ }
+}
+
/*
* This is the main drive for each connection. As you can tell, for the
@@ -1543,15 +1516,21 @@ get_request_entity(struct conn_s *connptr)
* tinyproxy code, which was confusing, redundant. Hail progress.
* - rjkaes
*/
-void handle_connection (int fd, union sockaddr_union* addr)
+void handle_connection (struct conn_s *connptr, union sockaddr_union* addr)
{
- ssize_t i;
- struct conn_s *connptr;
+
+#define HC_FAIL() \
+ do {handle_connection_failure(connptr, got_headers); goto done;} \
+ while(0)
+
+ int got_headers = 0, fd = connptr->client_fd;
+ size_t i;
struct request_s *request = NULL;
struct timeval tv;
- hashmap_t hashofheaders = NULL;
+ orderedmap hashofheaders = NULL;
char sock_ipaddr[IP_LENGTH];
+ char sock_ipaddr_alt[IP_LENGTH]="";
char peer_ipaddr[IP_LENGTH];
getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr));
@@ -1559,14 +1538,25 @@ void handle_connection (int fd, union sockaddr_union* addr)
if (config->bindsame)
getsock_ip (fd, sock_ipaddr);
+ switch (SOCKADDR_UNION_AF(addr)) {
+ case AF_INET:
+ if (config->bind_ipv4mapped)
+ getmapped_ipv6 (config->bind_ipv4mapped, sock_ipaddr, sock_ipaddr_alt);
+ break;
+ case AF_INET6:
+ if (config->bind_ipv6mapped)
+ getmapped_ipv4 (config->bind_ipv6mapped, sock_ipaddr, sock_ipaddr_alt);
+ break;
+ }
+
log_message (LOG_CONN, config->bindsame ?
"Connect (file descriptor %d): %s at [%s]" :
"Connect (file descriptor %d): %s",
fd, peer_ipaddr, sock_ipaddr);
- connptr = initialize_conn (fd, peer_ipaddr,
- config->bindsame ? sock_ipaddr : NULL);
- if (!connptr) {
+ if(!conn_init_contents (connptr, peer_ipaddr,
+ config->bindsame ? sock_ipaddr : NULL,
+ sock_ipaddr_alt[0] ? sock_ipaddr_alt : NULL)) {
close (fd);
return;
}
@@ -1588,7 +1578,7 @@ void handle_connection (int fd, union sockaddr_union* addr)
"You tried to connect to the "
"machine the proxy is running on",
NULL);
- goto fail;
+ HC_FAIL();
}
@@ -1599,7 +1589,7 @@ void handle_connection (int fd, union sockaddr_union* addr)
"The administrator of this proxy has not configured "
"it to service requests from your host.",
NULL);
- goto fail;
+ HC_FAIL();
}
if (read_request_line (connptr) < 0) {
@@ -1608,13 +1598,13 @@ void handle_connection (int fd, union sockaddr_union* addr)
"detail",
"Server timeout waiting for the HTTP request "
"from the client.", NULL);
- goto fail;
+ HC_FAIL();
}
/*
* The "hashofheaders" store the client's headers.
*/
- hashofheaders = hashmap_create (HEADER_BUCKETS);
+ hashofheaders = orderedmap_create (HEADER_BUCKETS);
if (hashofheaders == NULL) {
update_stats (STAT_BADCONN);
indicate_http_error (connptr, 503, "Internal error",
@@ -1622,7 +1612,7 @@ void handle_connection (int fd, union sockaddr_union* addr)
"An internal server error occurred while processing "
"your request. Please contact the administrator.",
NULL);
- goto fail;
+ HC_FAIL();
}
/*
@@ -1636,34 +1626,31 @@ void handle_connection (int fd, union sockaddr_union* addr)
"Could not retrieve all the headers from "
"the client.", NULL);
update_stats (STAT_BADCONN);
- goto fail;
+ HC_FAIL();
}
+ got_headers = 1;
if (config->basicauth_list != NULL) {
- ssize_t len;
char *authstring;
int failure = 1, stathost_connect = 0;
- 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);
+ authstring = orderedmap_find (hashofheaders, "proxy-authorization");
+
+ if (!authstring && config->stathost) {
+ authstring = orderedmap_find (hashofheaders, "host");
+ if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) {
+ authstring = orderedmap_find (hashofheaders, "authorization");
stathost_connect = 1;
- } else len = 0;
+ } else authstring = 0;
}
- if (len == 0) {
+ if (!authstring) {
if (stathost_connect) goto e401;
update_stats (STAT_DENIED);
indicate_http_error (connptr, 407, "Proxy Authentication Required",
"detail",
"This proxy requires authentication.",
NULL);
- goto fail;
+ HC_FAIL();
}
if ( /* currently only "basic" auth supported */
(strncmp(authstring, "Basic ", 6) == 0 ||
@@ -1678,22 +1665,20 @@ e401:
"The administrator of this proxy has not configured "
"it to service requests from you.",
NULL);
- goto fail;
+ HC_FAIL();
}
- hashmap_remove (hashofheaders, "proxy-authorization");
+ orderedmap_remove (hashofheaders, "proxy-authorization");
}
/*
* Add any user-specified headers (AddHeader directive) to the
* outgoing HTTP request.
*/
- for (i = 0; i < vector_length (config->add_headers); i++) {
- http_header_t *header = (http_header_t *)
- vector_getentry (config->add_headers, i, NULL);
+ if (config->add_headers)
+ for (i = 0; i < sblist_getsize (config->add_headers); i++) {
+ http_header_t *header = sblist_get (config->add_headers, i);
- hashmap_insert (hashofheaders,
- header->name,
- header->value, strlen (header->value) + 1);
+ orderedmap_append (hashofheaders, header->name, header->value);
}
request = process_request (connptr, hashofheaders);
@@ -1701,24 +1686,25 @@ e401:
if (!connptr->show_stats) {
update_stats (STAT_BADCONN);
}
- goto fail;
+ HC_FAIL();
}
connptr->upstream_proxy = UPSTREAM_HOST (request->host);
if (connptr->upstream_proxy != NULL) {
if (connect_to_upstream (connptr, request) < 0) {
- goto fail;
+ HC_FAIL();
}
} else {
connptr->server_fd = opensock (request->host, request->port,
- connptr->server_ip_addr);
+ connptr->server_ip_addr,
+ connptr->server_ip_addr_alt);
if (connptr->server_fd < 0) {
indicate_http_error (connptr, 500, "Unable to connect",
"detail",
PACKAGE_NAME " "
"was unable to connect to the remote web server.",
"error", strerror (errno), NULL);
- goto fail;
+ HC_FAIL();
}
log_message (LOG_CONN,
@@ -1732,13 +1718,25 @@ e401:
if (process_client_headers (connptr, hashofheaders) < 0) {
update_stats (STAT_BADCONN);
- goto fail;
+ log_message (LOG_INFO,
+ "process_client_headers failed: %s. host \"%s\" using "
+ "file descriptor %d.", strerror(errno),
+ request->host,
+ connptr->server_fd);
+
+ HC_FAIL();
}
if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) {
if (process_server_headers (connptr) < 0) {
update_stats (STAT_BADCONN);
- goto fail;
+ log_message (LOG_INFO,
+ "process_server_headers failed: %s. host \"%s\" using "
+ "file descriptor %d.", strerror(errno),
+ request->host,
+ connptr->server_fd);
+
+ HC_FAIL();
}
} else {
if (send_ssl_response (connptr) < 0) {
@@ -1746,7 +1744,7 @@ e401:
"handle_connection: Could not send SSL greeting "
"to client.");
update_stats (STAT_BADCONN);
- goto fail;
+ HC_FAIL();
}
}
@@ -1757,34 +1755,10 @@ e401:
"and remote client (fd:%d)",
connptr->client_fd, connptr->server_fd);
- goto done;
-
-fail:
- /*
- * First, get the body if there is one.
- * If we don't read all there is from the socket first,
- * it is still marked for reading and we won't be able
- * to send our data properly.
- */
- if (get_request_entity (connptr) < 0) {
- log_message (LOG_WARNING,
- "Could not retrieve request entity");
- indicate_http_error (connptr, 400, "Bad Request",
- "detail",
- "Could not retrieve the request entity "
- "the client.", NULL);
- update_stats (STAT_BADCONN);
- }
-
- if (connptr->error_variables) {
- send_http_error_message (connptr);
- } else if (connptr->show_stats) {
- showstats (connptr);
- }
-
done:
free_request_struct (request);
- hashmap_delete (hashofheaders);
- destroy_conn (connptr);
+ orderedmap_destroy (hashofheaders);
+ conn_destroy_contents (connptr);
return;
+#undef HC_FAIL
}
diff --git a/src/reqs.h b/src/reqs.h
index c1c5100..4a0374f 100644
--- a/src/reqs.h
+++ b/src/reqs.h
@@ -24,6 +24,7 @@
#include "common.h"
#include "sock.h"
+#include "conns.h"
/*
* Port constants for HTTP (80) and SSL (443)
@@ -44,6 +45,6 @@ struct request_s {
char *path;
};
-extern void handle_connection (int fd, union sockaddr_union* addr);
+extern void handle_connection (struct conn_s *, union sockaddr_union* addr);
#endif
diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c
index 32bd2a7..68f04a6 100644
--- a/src/reverse-proxy.c
+++ b/src/reverse-proxy.c
@@ -34,6 +34,7 @@ void reversepath_add (const char *path, const char *url,
struct reversepath **reversepath_list)
{
struct reversepath *reverse;
+ size_t l;
if (url == NULL) {
log_message (LOG_WARNING,
@@ -65,8 +66,17 @@ void reversepath_add (const char *path, const char *url,
if (!path)
reverse->path = safestrdup ("/");
- else
- reverse->path = safestrdup (path);
+ else {
+ l = strlen (path);
+ if (l && path[l-1] == '/')
+ reverse->path = safestrdup (path);
+ else {
+ reverse->path = safemalloc (l + 2);
+ memcpy (reverse->path, path, l);
+ reverse->path[l] = '/';
+ reverse->path[l+1] = 0;
+ }
+ }
reverse->url = safestrdup (url);
@@ -83,10 +93,16 @@ void reversepath_add (const char *path, const char *url,
*/
struct reversepath *reversepath_get (char *url, struct reversepath *reverse)
{
+ size_t l, lu, lp;
while (reverse) {
- if (strstr (url, reverse->path) == url)
+ lu = strlen (url);
+ lp = strlen (reverse->path);
+ if ((
+ (l = lu) == lp-1 ||
+ (l = lp) <= lu
+ ) &&
+ !memcmp(url, reverse->path, l))
return reverse;
-
reverse = reverse->next;
}
@@ -111,28 +127,34 @@ void free_reversepath_list (struct reversepath *reverse)
/*
* Rewrite the URL for reverse proxying.
*/
-char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
- char *url)
+char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders,
+ char *url, int *status)
{
char *rewrite_url = NULL;
char *cookie = NULL;
char *cookieval;
struct reversepath *reverse = NULL;
+ *status = 0;
+
/* Reverse requests always start with a slash */
if (*url == '/') {
/* First try locating the reverse mapping by request url */
reverse = reversepath_get (url, config->reversepath_list);
if (reverse) {
- rewrite_url = (char *)
- safemalloc (strlen (url) + strlen (reverse->url) +
- 1);
- strcpy (rewrite_url, reverse->url);
- strcat (rewrite_url, url + strlen (reverse->path));
+ size_t lu = strlen (url);
+ size_t lrp = strlen (reverse->path);
+ if (lrp > lu) {
+ rewrite_url = safestrdup (reverse->path);
+ *status = 301;
+ } else {
+ rewrite_url = safemalloc (
+ strlen (reverse->url) + lu + 1);
+ sprintf (rewrite_url, "%s%s", reverse->url, url + lrp);
+ }
} else if (config->reversemagic
- && hashmap_entry_by_key (hashofheaders,
- "cookie",
- (void **) &cookie) > 0) {
+ && (cookie = orderedmap_find (hashofheaders,
+ "cookie"))) {
/* No match - try the magical tracking cookie next */
if ((cookieval = strstr (cookie, REVERSE_COOKIE "="))
diff --git a/src/reverse-proxy.h b/src/reverse-proxy.h
index 64b4acd..a9a5bdd 100644
--- a/src/reverse-proxy.h
+++ b/src/reverse-proxy.h
@@ -22,6 +22,7 @@
#define TINYPROXY_REVERSE_PROXY_H
#include "conns.h"
+#include "orderedmap.h"
struct reversepath {
struct reversepath *next;
@@ -37,6 +38,7 @@ extern struct reversepath *reversepath_get (char *url,
struct reversepath *reverse);
void free_reversepath_list (struct reversepath *reverse);
extern char *reverse_rewrite_url (struct conn_s *connptr,
- hashmap_t hashofheaders, char *url);
+ orderedmap hashofheaders, char *url,
+ int *status);
#endif
diff --git a/src/sock.c b/src/sock.c
index 73ddebd..7aa43e2 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -34,6 +34,7 @@
#include "text.h"
#include "conf.h"
#include "loop.h"
+#include "sblist.h"
/*
* Return a human readable error for getaddrinfo() and getnameinfo().
@@ -68,7 +69,7 @@ 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: %s", addr, get_gai_error (n));
return -1;
}
@@ -87,12 +88,32 @@ bind_socket (int sockfd, const char *addr, int family)
return sockfd;
}
+/**
+ * Try binding the given socket to supplied addresses, stopping when one succeeds.
+ */
+static int
+bind_socket_list (int sockfd, sblist *addresses, int family)
+{
+ size_t nb_addresses = sblist_getsize(addresses);
+ size_t i;
+
+ for (i = 0; i < nb_addresses; i++) {
+ const char *address = *(const char **)sblist_get(addresses, i);
+ if (bind_socket(sockfd, address, family) >= 0) {
+ log_message(LOG_INFO, "Bound to %s", address);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
/*
* Open a connection to a remote host. It's been re-written to use
* the getaddrinfo() library function, which allows for a protocol
* independent implementation (mostly for IPv4 and IPv6 addresses.)
*/
-int opensock (const char *host, int port, const char *bind_to)
+int opensock (const char *host, int port, const char *bind_to, const char *bind_to_alt)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave;
@@ -103,6 +124,10 @@ int opensock (const char *host, int port, const char *bind_to)
log_message(LOG_INFO,
"opensock: opening connection to %s:%d", host, port);
+ if (bind_to) {
+ log_message(LOG_INFO,
+ "opensock: bind to %s or %s", bind_to, bind_to_alt);
+ }
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
@@ -130,13 +155,16 @@ 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) {
+ res->ai_family) < 0 &&
+ (!bind_to_alt ||
+ bind_socket (sockfd, bind_to_alt,
+ res->ai_family) < 0)) {
close (sockfd);
continue; /* can't bind, so try again */
}
- } else if (config->bind_address) {
- if (bind_socket (sockfd, config->bind_address,
- res->ai_family) < 0) {
+ } else if (config->bind_addrs) {
+ if (bind_socket_list (sockfd, config->bind_addrs,
+ res->ai_family) < 0) {
close (sockfd);
continue; /* can't bind, so try again */
}
@@ -277,7 +305,7 @@ static int listen_on_one_socket(struct addrinfo *ad)
* Upon success, the listen-fds are added to the listen_fds list
* and 0 is returned. Upon error, -1 is returned.
*/
-int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
+int listen_sock (const char *addr, uint16_t port, sblist* listen_fds)
{
struct addrinfo hints, *result, *rp;
char portstr[6];
@@ -315,7 +343,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
continue;
}
- vector_append (listen_fds, &listenfd, sizeof(int));
+ sblist_add (listen_fds, &listenfd);
/* success */
ret = 0;
@@ -361,6 +389,28 @@ int getsock_ip (int fd, char *ipaddr)
return 0;
}
+int getmapped_ipv4 (const char *ipv6mapped_conf, const char *ipaddr, char *ipaddr_mapped)
+{
+ struct in6_addr addr6;
+ struct in_addr addr;
+
+ memset(&addr6, 0, sizeof(addr6));
+ if ( inet_pton(AF_INET6, ipaddr, &addr6) < 0 )
+ return -1;
+
+ memcpy(&addr, addr6.s6_addr + 12, sizeof(addr));
+ return inet_ntop(AF_INET, &addr, ipaddr_mapped, IP_LENGTH) == NULL ? -1 : 0;
+}
+
+int getmapped_ipv6 (const char *ipv4mapped_conf, const char *ipaddr, char *ipaddr_mapped)
+{
+ memset(ipaddr_mapped, 0, IP_LENGTH);
+ strncpy(ipaddr_mapped, ipv4mapped_conf, IP_LENGTH-1);
+ strncat(ipaddr_mapped, ipaddr, IP_LENGTH-1-strlen(ipaddr_mapped));
+ return 0;
+}
+
+
/*
* Return the peer's socket information.
*/
diff --git a/src/sock.h b/src/sock.h
index 033e179..170dc3c 100644
--- a/src/sock.h
+++ b/src/sock.h
@@ -29,20 +29,36 @@
#define MAXLINE (1024 * 4)
#include "common.h"
-#include "vector.h"
+#include "sblist.h"
+
+#define SOCKADDR_UNION_AF(PTR) (PTR)->v4.sin_family
+
+#define SOCKADDR_UNION_LENGTH(PTR) ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? sizeof((PTR)->v4) : ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? sizeof((PTR)->v6) : 0 ) )
+
+#define SOCKADDR_UNION_ADDRESS(PTR) ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (void*) &(PTR)->v4.sin_addr : ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (void*) &(PTR)->v6.sin6_addr : (void*) 0 ) )
+
+#define SOCKADDR_UNION_PORT(PTR) ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (PTR)->v4.sin_port : ( \
+ ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (PTR)->v6.sin6_port : 0 ) )
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);
+extern int opensock (const char *host, int port, const char *bind_to, const char *bind_to_alt);
+extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds);
extern int socket_nonblocking (int sock);
extern int socket_blocking (int sock);
extern int getsock_ip (int fd, char *ipaddr);
+extern int getmapped_ipv6 (const char *ipv4mapped_conf, const char *ipaddr, char *ipaddr_mapped);
+extern int getmapped_ipv4 (const char *ipv6mapped_conf, const char *ipaddr, char *ipaddr_mapped);
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 9f4acc3..1228aa3 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -43,7 +43,7 @@ struct stat_s {
unsigned long int num_denied;
};
-static struct stat_s *stats;
+static struct stat_s stats_buf, *stats;
static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -52,11 +52,7 @@ static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER;
*/
void init_stats (void)
{
- stats = (struct stat_s *) safemalloc (sizeof (struct stat_s));
- if (!stats)
- return;
-
- memset (stats, 0, sizeof (struct stat_s));
+ stats = &stats_buf;
}
/*
@@ -126,7 +122,7 @@ err_minus_one:
add_error_variable (connptr, "deniedconns", denied);
add_error_variable (connptr, "refusedconns", refused);
add_standard_vars (connptr);
- send_http_headers (connptr, 200, "Statistic requested");
+ send_http_headers (connptr, 200, "Statistic requested", "");
send_html_file (statfile, connptr);
fclose (statfile);
pthread_mutex_unlock(&stats_file_lock);
diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c
index 2c1e069..d090ae3 100644
--- a/src/transparent-proxy.c
+++ b/src/transparent-proxy.c
@@ -53,17 +53,17 @@ static int build_url (char **url, const char *host, int port, const char *path)
}
int
-do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
+do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
struct request_s *request, struct config_s *conf,
char **url)
{
socklen_t length;
char *data;
size_t ulen = strlen (*url);
- ssize_t i;
+ size_t i;
- length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
- if (length <= 0) {
+ data = orderedmap_find (hashofheaders, "host");
+ if (!data) {
union sockaddr_union dest_addr;
const void *dest_inaddr;
char namebuf[INET6_ADDRSTRLEN+1];
@@ -83,16 +83,14 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
return 0;
}
- af = length == sizeof(dest_addr.v4) ? AF_INET : AF_INET6;
- if (af == AF_INET) dest_inaddr = &dest_addr.v4.sin_addr;
- else dest_inaddr = &dest_addr.v6.sin6_addr;
+ af = SOCKADDR_UNION_AF(&dest_addr);
+ dest_inaddr = SOCKADDR_UNION_ADDRESS(&dest_addr);
if (!inet_ntop(af, dest_inaddr, namebuf, sizeof namebuf))
goto addr_err;
request->host = safestrdup (namebuf);
- request->port = ntohs (af == AF_INET ? dest_addr.v4.sin_port
- : dest_addr.v6.sin6_port);
+ request->port = ntohs (SOCKADDR_UNION_PORT(&dest_addr));
request->path = (char *) safemalloc (ulen + 1);
strlcpy (request->path, *url, ulen + 1);
@@ -102,6 +100,7 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
"process_request: trans IP %s %s for %d",
request->method, *url, connptr->client_fd);
} else {
+ length = strlen (data);
request->host = (char *) safemalloc (length + 1);
if (sscanf (data, "%[^:]:%hu", request->host, &request->port) !=
2) {
@@ -122,12 +121,12 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
return 1;
}
- for (i = 0; i < vector_length(conf->listen_addrs); i++) {
- const char *addr;
+ for (i = 0; i < sblist_getsize(conf->listen_addrs); i++) {
+ char **addr;
- addr = (char *)vector_getentry(conf->listen_addrs, i, NULL);
+ addr = sblist_get(conf->listen_addrs, i);
- if (addr && strcmp(request->host, addr) == 0) {
+ if (addr && *addr && strcmp(request->host, *addr) == 0) {
log_message(LOG_ERR,
"transparent: destination IP %s is local "
"on socket fd %d",
diff --git a/src/transparent-proxy.h b/src/transparent-proxy.h
index 4fc3a4d..b56c446 100644
--- a/src/transparent-proxy.h
+++ b/src/transparent-proxy.h
@@ -26,11 +26,11 @@
#ifdef TRANSPARENT_PROXY
#include "conns.h"
-#include "hashmap.h"
+#include "orderedmap.h"
#include "reqs.h"
extern int do_transparent_proxy (struct conn_s *connptr,
- hashmap_t hashofheaders,
+ orderedmap hashofheaders,
struct request_s *request,
struct config_s *config, char **url);
diff --git a/src/upstream.c b/src/upstream.c
index 0f7f24f..52dad80 100644
--- a/src/upstream.c
+++ b/src/upstream.c
@@ -43,34 +43,46 @@ proxy_type_name(proxy_type type)
}
}
+
+const char* upstream_build_error_string(enum upstream_build_error ube) {
+ static const char *emap[] = {
+ [UBE_SUCCESS] = "",
+ [UBE_OOM] = "Unable to allocate memory in upstream_build()",
+ [UBE_USERLEN] = "User / pass in upstream config too long",
+ [UBE_EDOMAIN] = "Nonsense upstream none rule: empty domain",
+ [UBE_INVHOST] = "Nonsense upstream rule: invalid host or port",
+ [UBE_INVPARAMS] = "Nonsense upstream rule: invalid parameters",
+ [UBE_NETMASK] = "Nonsense upstream rule: failed to parse netmask",
+ };
+ return emap[ube];
+}
+
/**
* Construct an upstream struct from input data.
*/
-static struct upstream *upstream_build (const char *host, int port, const char *domain,
+static struct upstream *upstream_build (const char *host, int port, char *domain,
const char *user, const char *pass,
- proxy_type type)
+ proxy_type type, enum upstream_build_error *ube)
{
- char *ptr;
struct upstream *up;
+ *ube = UBE_SUCCESS;
up = (struct upstream *) safemalloc (sizeof (struct upstream));
if (!up) {
- log_message (LOG_ERR,
- "Unable to allocate memory in upstream_build()");
+ *ube = UBE_OOM;
return NULL;
}
up->type = type;
- up->host = up->domain = up->ua.user = up->pass = NULL;
- up->ip = up->mask = 0;
+ up->target.type = HST_NONE;
+ up->host = up->ua.user = up->pass = NULL;
if (user) {
if (type == PT_HTTP) {
char b[BASE64ENC_BYTES((256+2)-1) + 1];
ssize_t ret;
ret = basicauth_string(user, pass, b, sizeof b);
if (ret == 0) {
- log_message (LOG_ERR,
- "User / pass in upstream config too long");
+ *ube = UBE_USERLEN;
return NULL;
}
up->ua.authstr = safestrdup (b);
@@ -83,13 +95,11 @@ static struct upstream *upstream_build (const char *host, int port, const char *
if (domain == NULL) {
if (type == PT_NONE) {
e_nonedomain:;
- log_message (LOG_WARNING,
- "Nonsense upstream none rule: empty domain");
+ *ube = UBE_EDOMAIN;
goto fail;
}
if (!host || !host[0] || port < 1) {
- log_message (LOG_WARNING,
- "Nonsense upstream rule: invalid host or port");
+ *ube = UBE_INVHOST;
goto fail;
}
@@ -103,39 +113,17 @@ static struct upstream *upstream_build (const char *host, int port, const char *
if (!domain[0]) goto e_nonedomain;
} else {
if (!host || !host[0] || !domain[0]) {
- log_message (LOG_WARNING,
- "Nonsense upstream rule: invalid parameters");
+ *ube = UBE_INVPARAMS;
goto fail;
}
up->host = safestrdup (host);
up->port = port;
}
- ptr = strchr (domain, '/');
- if (ptr) {
- struct in_addr addrstruct;
-
- *ptr = '\0';
- if (inet_aton (domain, &addrstruct) != 0) {
- up->ip = ntohl (addrstruct.s_addr);
- *ptr++ = '/';
-
- if (strchr (ptr, '.')) {
- if (inet_aton (ptr, &addrstruct) != 0)
- up->mask =
- ntohl (addrstruct.s_addr);
- } else {
- up->mask =
- ~((1 << (32 - atoi (ptr))) - 1);
- }
- up->ip = up->ip & up->mask;
- } else {
- log_message (LOG_WARNING,
- "Nonsense upstream rule: failed to parse netmask");
- goto fail;
- }
- } else {
- up->domain = safestrdup (domain);
+ if (hostspec_parse(domain, &up->target)
+ || up->target.type == HST_NONE) {
+ *ube = UBE_NETMASK;
+ goto fail;
}
if (type == PT_NONE)
@@ -151,7 +139,8 @@ fail:
safefree (up->ua.user);
safefree (up->pass);
safefree (up->host);
- safefree (up->domain);
+ if(up->target.type == HST_STRING)
+ safefree (up->target.address.string);
safefree (up);
return NULL;
@@ -160,22 +149,24 @@ fail:
/*
* Add an entry to the upstream list
*/
-void upstream_add (const char *host, int port, const char *domain,
+enum upstream_build_error upstream_add (
+ const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list)
{
struct upstream *up;
+ enum upstream_build_error ube;
- up = upstream_build (host, port, domain, user, pass, type);
+ up = upstream_build (host, port, domain, user, pass, type, &ube);
if (up == NULL) {
- return;
+ return ube;
}
- if (!up->domain && !up->ip) { /* always add default to end */
+ if (up->target.type == HST_NONE) { /* always add default to end */
struct upstream *tmp = *upstream_list;
while (tmp) {
- if (!tmp->domain && !tmp->ip) {
+ if (tmp->target.type == HST_NONE) {
log_message (LOG_WARNING,
"Duplicate default upstream");
goto upstream_cleanup;
@@ -184,7 +175,7 @@ void upstream_add (const char *host, int port, const char *domain,
if (!tmp->next) {
up->next = NULL;
tmp->next = up;
- return;
+ return ube;
}
tmp = tmp->next;
@@ -194,14 +185,15 @@ void upstream_add (const char *host, int port, const char *domain,
up->next = *upstream_list;
*upstream_list = up;
- return;
+ return ube;
upstream_cleanup:
safefree (up->host);
- safefree (up->domain);
+ if(up->target.type == HST_STRING)
+ safefree (up->target.address.string);
safefree (up);
- return;
+ return ube;
}
/*
@@ -209,34 +201,12 @@ upstream_cleanup:
*/
struct upstream *upstream_get (char *host, struct upstream *up)
{
- in_addr_t my_ip = INADDR_NONE;
-
while (up) {
- if (up->domain) {
- if (strcasecmp (host, up->domain) == 0)
- break; /* exact match */
-
- if (up->domain[0] == '.') {
- char *dot = strchr (host, '.');
-
- if (!dot && !up->domain[1])
- break; /* local host matches "." */
-
- while (dot && strcasecmp (dot, up->domain))
- dot = strchr (dot + 1, '.');
+ if (up->target.type == HST_NONE)
+ break;
- if (dot)
- break; /* subdomain match */
- }
- } else if (up->ip) {
- if (my_ip == INADDR_NONE)
- my_ip = ntohl (inet_addr (host));
-
- if ((my_ip & up->mask) == up->ip)
- break;
- } else {
- break; /* No domain or IP, default upstream */
- }
+ if (hostspec_match(host, &up->target))
+ break;
up = up->next;
}
@@ -258,7 +228,8 @@ void free_upstream_list (struct upstream *up)
while (up) {
struct upstream *tmp = up;
up = up->next;
- safefree (tmp->domain);
+ if(tmp->target.type == HST_STRING)
+ safefree (tmp->target.address.string);
safefree (tmp->host);
safefree (tmp);
}
diff --git a/src/upstream.h b/src/upstream.h
index c112784..9a2314d 100644
--- a/src/upstream.h
+++ b/src/upstream.h
@@ -26,6 +26,17 @@
#define _TINYPROXY_UPSTREAM_H_
#include "common.h"
+#include "hostspec.h"
+
+enum upstream_build_error {
+ UBE_SUCCESS = 0,
+ UBE_OOM,
+ UBE_USERLEN,
+ UBE_EDOMAIN,
+ UBE_INVHOST,
+ UBE_INVPARAMS,
+ UBE_NETMASK,
+};
/*
* Even if upstream support is not compiled into tinyproxy, this
@@ -40,7 +51,6 @@ typedef enum proxy_type {
struct upstream {
struct upstream *next;
- char *domain; /* optional */
char *host;
union {
char *user;
@@ -48,17 +58,19 @@ struct upstream {
} ua;
char *pass;
int port;
- in_addr_t ip, mask;
+ struct hostspec target;
proxy_type type;
};
#ifdef UPSTREAM_SUPPORT
const char *proxy_type_name(proxy_type type);
-extern void upstream_add (const char *host, int port, const char *domain,
+extern enum upstream_build_error upstream_add (
+ const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list);
extern struct upstream *upstream_get (char *host, struct upstream *up);
extern void free_upstream_list (struct upstream *up);
+extern const char* upstream_build_error_string(enum upstream_build_error);
#endif /* UPSTREAM_SUPPORT */
#endif /* _TINYPROXY_UPSTREAM_H_ */
diff --git a/src/vector.c b/src/vector.c
deleted file mode 100644
index cf9fc75..0000000
--- a/src/vector.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/* tinyproxy - A fast light-weight HTTP proxy
- * Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
- *
- * 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 of the License, 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* A vector implementation. The vector can be of an arbitrary length, and
- * the data for each entry is an lump of data (the size is stored in the
- * vector.)
- */
-
-#include "main.h"
-
-#include "heap.h"
-#include "vector.h"
-
-/*
- * These structures are the storage for the "vector". Entries are
- * stored in struct vectorentry_s (the data and the length), and the
- * "vector" structure is implemented as a linked-list. The struct
- * vector_s stores a pointer to the first vector (vector[0]) and a
- * count of the number of entries (or how long the vector is.)
- */
-struct vectorentry_s {
- void *data;
- size_t len;
-
- struct vectorentry_s *next;
-};
-
-struct vector_s {
- size_t num_entries;
- struct vectorentry_s *head;
- struct vectorentry_s *tail;
-};
-
-/*
- * Create an vector. The vector initially has no elements and no
- * storage has been allocated for the entries.
- *
- * A NULL is returned if memory could not be allocated for the
- * vector.
- */
-vector_t vector_create (void)
-{
- vector_t vector;
-
- vector = (vector_t) safemalloc (sizeof (struct vector_s));
- if (!vector)
- return NULL;
-
- vector->num_entries = 0;
- vector->head = vector->tail = NULL;
-
- return vector;
-}
-
-/*
- * Deletes an vector. All the entries when this function is run.
- *
- * Returns: 0 on success
- * negative if a NULL vector is supplied
- */
-int vector_delete (vector_t vector)
-{
- struct vectorentry_s *ptr, *next;
-
- if (!vector)
- return -EINVAL;
-
- ptr = vector->head;
- while (ptr) {
- next = ptr->next;
- safefree (ptr->data);
- safefree (ptr);
-
- ptr = next;
- }
-
- safefree (vector);
-
- return 0;
-}
-
-/*
- * Appends an entry into the vector. The entry is an arbitrary
- * collection of bytes of _len_ octets. The data is copied into the
- * vector, so the original data must be freed to avoid a memory leak.
- * The "data" must be non-NULL and the "len" must be greater than zero.
- * "pos" is either 0 to prepend the data, or 1 to append the data.
- *
- * Returns: 0 on success
- * negative number if there are errors
- */
-
-typedef enum {
- INSERT_PREPEND,
- INSERT_APPEND
-} vector_pos_t;
-
-static int
-vector_insert (vector_t vector,
- void *data,
- size_t len,
- vector_pos_t pos)
-{
- struct vectorentry_s *entry;
-
- if (!vector || !data || len <= 0 ||
- (pos != INSERT_PREPEND && pos != INSERT_APPEND))
- return -EINVAL;
-
- entry =
- (struct vectorentry_s *) safemalloc (sizeof (struct vectorentry_s));
- if (!entry)
- return -ENOMEM;
-
- entry->data = safemalloc (len);
- if (!entry->data) {
- safefree (entry);
- return -ENOMEM;
- }
-
- memcpy (entry->data, data, len);
- entry->len = len;
- entry->next = NULL;
-
- /* If there is no head or tail, create them */
- if (!vector->head && !vector->tail)
- vector->head = vector->tail = entry;
- else if (pos == INSERT_PREPEND) {
- /* prepend the entry */
- entry->next = vector->head;
- vector->head = entry;
- } else {
- /* append the entry */
- vector->tail->next = entry;
- vector->tail = entry;
- }
-
- vector->num_entries++;
-
- return 0;
-}
-
-/*
- * The following two function are used to make the API clearer. As you
- * can see they simply call the vector_insert() function with appropriate
- * arguments.
- */
-int vector_append (vector_t vector, void *data, size_t len)
-{
- return vector_insert (vector, data, len, INSERT_APPEND);
-}
-
-int vector_prepend (vector_t vector, void *data, size_t len)
-{
- return vector_insert (vector, data, len, INSERT_PREPEND);
-}
-
-/*
- * A pointer to the data at position "pos" (zero based) is returned.
- * If the vector is out of bound, data is set to NULL.
- *
- * Returns: negative upon an error
- * length of data if position is valid
- */
-void *vector_getentry (vector_t vector, size_t pos, size_t * size)
-{
- struct vectorentry_s *ptr;
- size_t loc;
-
- if (!vector || pos >= vector->num_entries)
- return NULL;
-
- loc = 0;
- ptr = vector->head;
-
- while (loc != pos) {
- ptr = ptr->next;
- loc++;
- }
-
- if (size)
- *size = ptr->len;
-
- return ptr->data;
-}
-
-/*
- * Returns the number of entries (or the length) of the vector.
- *
- * Returns: negative if vector is not valid
- * positive length of vector otherwise
- */
-ssize_t vector_length (vector_t vector)
-{
- if (!vector)
- return -EINVAL;
-
- return vector->num_entries;
-}
diff --git a/src/vector.h b/src/vector.h
deleted file mode 100644
index ef8f953..0000000
--- a/src/vector.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* tinyproxy - A fast light-weight HTTP proxy
- * Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
- *
- * 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 of the License, 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* See 'vector.c' for detailed information. */
-
-#ifndef _VECTOR_H
-#define _VECTOR_H
-
-/*
- * We're using a typedef here to "hide" the implementation details of the
- * vector. Sure, it's a pointer, but the struct is hidden in the C file.
- * So, just use the vector_t like it's a cookie. :)
- */
-typedef struct vector_s *vector_t;
-
-/*
- * vector_create() takes no arguments.
- * vector_delete() is self explanatory.
- */
-extern vector_t vector_create (void);
-extern int vector_delete (vector_t vector);
-
-/*
- * When you insert a piece of data into the vector, the data will be
- * duplicated, so you must free your copy if it was created on the heap.
- * The data must be non-NULL and the length must be greater than zero.
- *
- * Returns: negative on error
- * 0 upon successful insert.
- */
-extern int vector_append (vector_t vector, void *data, size_t len);
-extern int vector_prepend (vector_t vector, void *data, size_t len);
-
-/*
- * A pointer to the data at position "pos" (zero based) is returned and the
- * size pointer contains the length of the data stored.
- *
- * The pointer points to the actual data in the vector, so you have
- * the power to modify the data, but do it responsibly since the
- * library doesn't take any steps to prevent you from messing up the
- * vector. (A better rule is, don't modify the data since you'll
- * likely mess up the "length" parameter of the data.) However, DON'T
- * try to realloc or free the data; doing so will break the vector.
- *
- * If "size" is NULL the size of the data is not returned.
- *
- * Returns: NULL on error
- * valid pointer to data
- */
-extern void *vector_getentry (vector_t vector, size_t pos, size_t * size);
-
-/*
- * Returns the number of enteries (or the length) of the vector.
- *
- * Returns: negative if vector is not valid
- * positive length of vector otherwise
- */
-extern ssize_t vector_length (vector_t vector);
-
-#endif /* _VECTOR_H */
diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh
index 799c9d6..eb1d3c4 100755
--- a/tests/scripts/run_tests.sh
+++ b/tests/scripts/run_tests.sh
@@ -80,6 +80,9 @@ Listen $TINYPROXY_IP
Timeout 600
StatHost "$TINYPROXY_STATHOST_IP"
DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html"
+ErrorFile 400 "$TINYPROXY_DATA_DIR/debug.html"
+ErrorFile 403 "$TINYPROXY_DATA_DIR/debug.html"
+ErrorFile 501 "$TINYPROXY_DATA_DIR/debug.html"
StatFile "$TINYPROXY_DATA_DIR/stats.html"
Logfile "$TINYPROXY_LOG_FILE"
PidFile "$TINYPROXY_PID_FILE"
@@ -90,12 +93,17 @@ ViaProxyName "tinyproxy"
#DisableViaHeader Yes
ConnectPort 443
ConnectPort 563
-FilterURLs On
+#FilterURLs On
Filter "$TINYPROXY_FILTER_FILE"
XTinyproxy Yes
+AddHeader "X-My-Header1" "Powered by Tinyproxy"
+AddHeader "X-My-Header2" "Powered by Tinyproxy"
+AddHeader "X-My-Header3" "Powered by Tinyproxy"
EOF
- touch $TINYPROXY_FILTER_FILE
+cat << 'EOF' > $TINYPROXY_FILTER_FILE
+.*\.google-analytics\.com$
+EOF
}
start_tinyproxy() {
@@ -104,6 +112,13 @@ start_tinyproxy() {
echo " done (listening on $TINYPROXY_IP:$TINYPROXY_PORT)"
}
+reload_config() {
+ echo -n "signaling tinyproxy to reload config..."
+ pid=$(cat $TINYPROXY_PID_FILE)
+ #1: SIGHUP
+ kill -1 $pid && echo "ok" || echo "fail"
+}
+
stop_tinyproxy() {
echo -n "killing tinyproxy..."
pid=$(cat $TINYPROXY_PID_FILE)
@@ -157,7 +172,7 @@ wait_for_some_seconds() {
}
run_basic_webclient_request() {
- $WEBCLIENT_BIN $1 $2 >> $WEBCLIENT_LOG 2>&1
+ $WEBCLIENT_BIN $1 $2 > $WEBCLIENT_LOG 2>&1
WEBCLIENT_EXIT_CODE=$?
if test "x$WEBCLIENT_EXIT_CODE" = "x0" ; then
echo " ok"
@@ -165,11 +180,31 @@ run_basic_webclient_request() {
echo "ERROR ($WEBCLIENT_EXIT_CODE)"
echo "webclient output:"
cat $WEBCLIENT_LOG
+ echo "######################################"
fi
return $WEBCLIENT_EXIT_CODE
}
+run_failure_webclient_request() {
+ ec=$1
+ expected_error=$(($1 - 399))
+ shift
+ $WEBCLIENT_BIN "$1" "$2" "$3" "$4" > $WEBCLIENT_LOG 2>&1
+ WEBCLIENT_EXIT_CODE=$?
+ if test "x$WEBCLIENT_EXIT_CODE" = "x$expected_error" ; then
+ echo " ok, got expected error code $ec"
+ return 0
+ else
+ echo "ERROR ($WEBCLIENT_EXIT_CODE)"
+ echo "webclient output:"
+ cat $WEBCLIENT_LOG
+ echo "######################################"
+ fi
+
+ return 1
+}
+
# "main"
provision_initial
@@ -179,10 +214,11 @@ provision_webserver
start_webserver
start_tinyproxy
-wait_for_some_seconds 3
+wait_for_some_seconds 1
FAILED=0
+basic_test() {
echo -n "checking direct connection to web server..."
run_basic_webclient_request "$WEBSERVER_IP:$WEBSERVER_PORT" /
test "x$?" = "x0" || FAILED=$((FAILED + 1))
@@ -194,6 +230,26 @@ test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "requesting statspage via stathost url..."
run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$TINYPROXY_STATHOST_IP"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
+}
+
+ext_test() {
+echo -n "checking bogus request..."
+run_failure_webclient_request 400 --method="BIG FART" "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$WEBSERVER_IP:$WEBSERVER_PORT"
+test "x$?" = "x0" || FAILED=$((FAILED + 1))
+
+echo -n "testing connection to filtered domain..."
+run_failure_webclient_request 403 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://badgoy.google-analytics.com/"
+test "x$?" = "x0" || FAILED=$((FAILED + 1))
+
+echo -n "requesting connect method to denied port..."
+run_failure_webclient_request 403 --method=CONNECT "$TINYPROXY_IP:$TINYPROXY_PORT" "localhost:12345"
+test "x$?" = "x0" || FAILED=$((FAILED + 1))
+}
+
+basic_test
+reload_config
+basic_test
+ext_test
echo "$FAILED errors"
diff --git a/tests/scripts/run_tests_valgrind.sh b/tests/scripts/run_tests_valgrind.sh
index b93371f..72b2115 100755
--- a/tests/scripts/run_tests_valgrind.sh
+++ b/tests/scripts/run_tests_valgrind.sh
@@ -24,5 +24,5 @@ TESTS_DIR=$SCRIPTS_DIR/..
TESTENV_DIR=$TESTS_DIR/env
LOG_DIR=$TESTENV_DIR/var/log
-VALGRIND="valgrind -q --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh
+VALGRIND="valgrind --track-origins=yes --show-leak-kinds=all --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh
diff --git a/tests/scripts/webclient.pl b/tests/scripts/webclient.pl
index 25c0fb7..4dddb69 100755
--- a/tests/scripts/webclient.pl
+++ b/tests/scripts/webclient.pl
@@ -122,11 +122,16 @@ foreach my $document (@ARGV) {
print $remote $request;
+ $_ = <$remote>;
+ print; # /* HTTP/1.0 400 Bad Request */
+ my($errn) = ($_ =~ /HTTP\/\d\.\d (\d{3})/);
+
while (<$remote>) {
print;
}
close $remote;
+ exit($errn - 399) if($errn > 399);
}
exit(0);