aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2021-01-12 22:45:00 +0200
committerTimo Teräs <timo.teras@iki.fi>2021-01-12 22:45:00 +0200
commit17adf878ffe42d88ad735ed6dcb32dab79056082 (patch)
treec2992fc5c78a2bac4569c145a79551eca0fdc14e
parent18b0b45b5b4e8d7be19afa1492c32abb75b9da4a (diff)
downloadapk-tools-tt-subnet-match.tar.gz
apk-tools-tt-subnet-match.tar.bz2
apk-tools-tt-subnet-match.tar.xz
Support for CIDR subnets in no_proxy/NO_PROXYtt-subnet-match
This enables matching numeric hosts against CIDR style subnet matches in no_proxy environment variable. Based on work by Thermi. Co-authored-by: Noel Kuntze <noel.kuntze@thermi.consulting>
-rw-r--r--libfetch/common.c66
1 files changed, 65 insertions, 1 deletions
diff --git a/libfetch/common.c b/libfetch/common.c
index 537715b..eb54df5 100644
--- a/libfetch/common.c
+++ b/libfetch/common.c
@@ -2,6 +2,7 @@
/*-
* Copyright (c) 1998-2004 Dag-Erling Codan Smrgrav
* Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
+ * Copyright (c) 2020 Noel Kuntze <noel.kuntze@thermi.consulting>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -1052,6 +1053,63 @@ fetch_netrc_auth(struct url *url)
return (-1);
}
+#define MAX_ADDRESS_BYTES sizeof(struct in6_addr)
+#define MAX_ADDRESS_STRING (4*8+1)
+#define MAX_CIDR_STRING (MAX_ADDRESS_STRING+4)
+
+static size_t host_to_address(uint8_t *buf, size_t buf_len, const char *host, size_t len)
+{
+ char tmp[MAX_ADDRESS_STRING];
+
+ if (len >= sizeof tmp) return 0;
+ if (buf_len < sizeof(struct in6_addr)) return 0;
+
+ /* Make zero terminated copy of the hostname */
+ memcpy(tmp, host, len);
+ tmp[len] = 0;
+
+ if (inet_pton(AF_INET, tmp, (struct in_addr *) buf))
+ return sizeof(struct in_addr);
+ if (inet_pton(AF_INET6, tmp, (struct in6_addr *) buf))
+ return sizeof(struct in6_addr);
+ return 0;
+}
+
+static int bitcmp(const uint8_t *a, const uint8_t *b, int len)
+{
+ int bytes, bits, mask, r;
+
+ bytes = len / 8;
+ bits = len % 8;
+ if (bytes != 0) {
+ r = memcmp(a, b, bytes);
+ if (r != 0) return r;
+ }
+ if (bits != 0) {
+ mask = (0xff << (8 - bits)) & 0xff;
+ return ((int) (a[bytes] & mask)) - ((int) (b[bytes] & mask));
+ }
+ return 0;
+}
+
+static int cidr_match(const uint8_t *addr, size_t addr_len, const char *cidr, size_t cidr_len)
+{
+ const char *slash;
+ uint8_t cidr_addr[MAX_ADDRESS_BYTES];
+ size_t cidr_addrlen;
+ long bits;
+
+ if (!addr_len || cidr_len > MAX_CIDR_STRING) return 0;
+ slash = memchr(cidr, '/', cidr_len);
+ if (!slash) return 0;
+ bits = strtol(slash + 1, NULL, 10);
+ if (!bits || bits > 128) return 0;
+
+ cidr_addrlen = host_to_address(cidr_addr, sizeof cidr_addr, cidr, slash - cidr);
+ if (cidr_addrlen != addr_len || bits > addr_len*8) return 0;
+ return bitcmp(cidr_addr, addr, bits) == 0;
+}
+
/*
* The no_proxy environment variable specifies a set of domains for
* which the proxy should not be consulted; the contents is a comma-,
@@ -1064,7 +1122,8 @@ int
fetch_no_proxy_match(const char *host)
{
const char *no_proxy, *p, *q;
- size_t h_len, d_len;
+ uint8_t addr[MAX_ADDRESS_BYTES];
+ size_t h_len, d_len, addr_len;
if ((no_proxy = getenv("NO_PROXY")) == NULL &&
(no_proxy = getenv("no_proxy")) == NULL)
@@ -1075,6 +1134,7 @@ fetch_no_proxy_match(const char *host)
return (1);
h_len = strlen(host);
+ addr_len = host_to_address(addr, sizeof addr, host, h_len);
p = no_proxy;
do {
/* position p at the beginning of a domain suffix */
@@ -1094,6 +1154,10 @@ fetch_no_proxy_match(const char *host)
return (1);
}
+ if (cidr_match(addr, addr_len, p, d_len)) {
+ return (1);
+ }
+
p = q + 1;
} while (*q);