summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am1
-rw-r--r--src/acl.c147
-rw-r--r--src/hostspec.c166
-rw-r--r--src/hostspec.h26
-rw-r--r--src/upstream.c80
-rw-r--r--src/upstream.h6
6 files changed, 237 insertions, 189 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6d806e0..d132a75 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ AM_CPPFLAGS = \
-DLOCALSTATEDIR=\"${localstatedir}\"
tinyproxy_SOURCES = \
+ hostspec.c hostspec.h \
acl.c acl.h \
anonymous.c anonymous.h \
buffer.c buffer.h \
diff --git a/src/acl.c b/src/acl.c
index b989219..63a3d34 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -29,17 +29,10 @@
#include "network.h"
#include "sock.h"
#include "sblist.h"
+#include "hostspec.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
-};
-
/*
* Hold the information about a particular access control. We store
* whether it's an ALLOW or DENY entry, and also whether it's a string
@@ -47,66 +40,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,7 +74,6 @@ int
insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
{
struct acl_s acl;
- char *mask, ip_dst[IPV6_LEN];
assert (location != NULL);
@@ -150,55 +85,11 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
*/
memset (&acl, 0, sizeof (struct acl_s));
acl.access = access_type;
-
- if ((mask = strrchr(location, '/')))
- *(mask++) = 0;
-
- /*
- * 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);
- if(!mask) memset (acl.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, &(acl.address.ip.mask[0]), IPV6_LEN)
- < 0)
- goto err;
-
- for (i = 0; i < IPV6_LEN; i++)
- acl.address.ip.network[i] = ip_dst[i] &
- acl.address.ip.mask[i];
- }
- } else {
- /* either bogus IP or hostname */
- /* bogus ipv6 ? */
- if (mask || strchr (location, ':'))
- goto err;
-
- /* In all likelihood a string */
- acl.type = ACL_STRING;
- acl.address.string = safestrdup (location);
- if (!acl.address.string)
- goto err;
- }
+ if(hostspec_parse(location, &acl.h) || acl.h.type == HST_NONE)
+ return -1;
if(!sblist_add(*access_list, &acl)) return -1;
return 0;
-err:;
- /* restore mask for proper error message */
- if(mask) *(--mask) = '/';
- return -1;
}
/*
@@ -219,7 +110,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);
/*
@@ -227,11 +118,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;
@@ -265,7 +156,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
@@ -276,7 +167,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
@@ -300,11 +191,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)
@@ -345,12 +236,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;
@@ -358,6 +249,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;
}
/*
@@ -394,8 +289,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/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/upstream.c b/src/upstream.c
index c8fee22..52dad80 100644
--- a/src/upstream.c
+++ b/src/upstream.c
@@ -60,11 +60,10 @@ const char* upstream_build_error_string(enum upstream_build_error 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, enum upstream_build_error *ube)
{
- char *ptr;
struct upstream *up;
*ube = UBE_SUCCESS;
@@ -75,8 +74,8 @@ static struct upstream *upstream_build (const char *host, int port, const char *
}
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];
@@ -121,30 +120,10 @@ static struct upstream *upstream_build (const char *host, int port, const char *
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 {
- *ube = UBE_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)
@@ -160,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;
@@ -170,7 +150,7 @@ fail:
* Add an entry to the upstream list
*/
enum upstream_build_error upstream_add (
- const char *host, int port, const char *domain,
+ const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list)
{
@@ -182,11 +162,11 @@ enum upstream_build_error upstream_add (
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;
@@ -209,7 +189,8 @@ enum upstream_build_error upstream_add (
upstream_cleanup:
safefree (up->host);
- safefree (up->domain);
+ if(up->target.type == HST_STRING)
+ safefree (up->target.address.string);
safefree (up);
return ube;
@@ -220,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 (up->target.type == HST_NONE)
+ break;
- if (!dot && !up->domain[1])
- break; /* local host matches "." */
-
- while (dot && strcasecmp (dot, up->domain))
- dot = strchr (dot + 1, '.');
-
- 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;
}
@@ -269,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 a611807..9a2314d 100644
--- a/src/upstream.h
+++ b/src/upstream.h
@@ -26,6 +26,7 @@
#define _TINYPROXY_UPSTREAM_H_
#include "common.h"
+#include "hostspec.h"
enum upstream_build_error {
UBE_SUCCESS = 0,
@@ -50,7 +51,6 @@ typedef enum proxy_type {
struct upstream {
struct upstream *next;
- char *domain; /* optional */
char *host;
union {
char *user;
@@ -58,14 +58,14 @@ 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 enum upstream_build_error upstream_add (
- const char *host, int port, const char *domain,
+ 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);