libfetch: support for CIDR subnets in no_proxy/NO_PROXY

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>
cute-signatures
Timo Teräs 2021-01-12 22:45:00 +02:00
parent ae7c4451d5
commit 167fd86794
1 changed files with 65 additions and 1 deletions

View File

@ -2,6 +2,7 @@
/*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
* 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);