diff options
Diffstat (limited to 'src/vmod_remoteip.c')
-rw-r--r-- | src/vmod_remoteip.c | 313 |
1 files changed, 21 insertions, 292 deletions
diff --git a/src/vmod_remoteip.c b/src/vmod_remoteip.c index 528b0a4..8bba147 100644 --- a/src/vmod_remoteip.c +++ b/src/vmod_remoteip.c @@ -26,314 +26,43 @@ #include <cache/cache.h> #include <vcl.h> #include <vcc_if.h> -#include <vqueue.h> -#ifdef VPFX -# define VEVENT(a) VPFX(a) -#else -/* For compatibility with varnish prior to 6.2 */ -# define VEVENT(a) a -#endif - -/* Max. number of bytes in an IPv6 address */ -#define MU_INADDR_BYTES 16 - -/* CIDR representation */ -struct cidr { - int family; /* Address family */ - int len; /* Number of bytes in the address */ - unsigned char address[MU_INADDR_BYTES]; - unsigned char netmask[MU_INADDR_BYTES]; - VSLIST_ENTRY(cidr) next; -}; - -typedef VSLIST_HEAD(,cidr) CIDRHEAD; - -/* Returns 1 if ADDR is a valid string representation of IPv4 address */ -static int -str_is_ipv4(const char *addr, size_t len) -{ - int dot_count = 0; - int digit_count = 0; - - for (; len; addr++, len--) { - if (!isascii(*addr)) - return 0; - if (*addr == '.') { - if (++dot_count > 3) - break; - digit_count = 0; - } - else if (!(isdigit(*addr) && ++digit_count <= 3)) - return 0; - } - return (dot_count == 3); -} - -#define PFXSTR_IPV4_MAPPED "::ffff:" -#define PFXLEN_IPV4_MAPPED (sizeof PFXSTR_IPV4_MAPPED - 1) - -static int -str_is_ipv4mapped(const char *addr, size_t len) -{ - return len > PFXLEN_IPV4_MAPPED - && strncasecmp(PFXSTR_IPV4_MAPPED, addr, PFXLEN_IPV4_MAPPED) - == 0 - && str_is_ipv4(addr + PFXLEN_IPV4_MAPPED, len - PFXLEN_IPV4_MAPPED); -} - -/* Returns 1 if ADDR is a valid IPv6 address */ -static int -str_is_ipv6(const char *addr, size_t len) -{ - int col_count = 0; /* Number of colons */ - int dcol = 0; /* Did we encounter a double-colon? */ - int dig_count = 0; /* Number of digits in the last group */ - - for (; len; addr++, len--) { - if (!isascii (*addr)) - return 0; - else if (isxdigit(*addr)) { - if (++dig_count > 4) - return 0; - } else if (*addr == ':') { - if (col_count && dig_count == 0 && ++dcol > 1) - return 0; - if (++col_count > 7) - return 0; - dig_count = 0; - } - else - return 0; - } - return (col_count == 7 || dcol); -} - -static void -uint32_to_bytes(unsigned char *bytes, uint32_t u) -{ - int i; - - for (i = 0; i < 4; i++) { - bytes[i] = u & 0xff; - u >>= 8; - } -} +#include <vsa.h> static int -inaddr_to_bytes(int af, void *buf, unsigned char *bytes) -{ - uint32_t u; - - switch (af) { - case AF_INET: - memcpy(&u, buf, sizeof u); - uint32_to_bytes(bytes, u); - return 4; - - case AF_INET6: - memcpy(bytes, buf, 16); - return 16; - } - - abort(); -} - -/* Fill in the BUF (LEN bytes long) with a network mask corresponding to - the mask length MASKLEN */ -static void -masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen) -{ - int i, cnt; - - cnt = masklen / 8; - for (i = 0; i < cnt; i++) - buf[i] = 0xff; - if (i == len) - return; - cnt = 8 - masklen % 8; - buf[i++] = (0xff >> cnt) << cnt; - for (; i < len; i++) - buf[i] = 0; -} - -#define CIDR_MAXBUFSIZE 81 - -/* Convert string STR to CIDR. Return 0 on success and -1 on failure. */ -static int -str_to_cidr(char const *str, struct cidr *cidr, char **endp) +is_trusted_ip(VRT_CTX, VCL_ACL acl, char const *ipstr) { + struct addrinfo hints; + struct addrinfo *res; + struct suckaddr *ip; int rc; - char *ipbuf, *ipstart; - union { - struct in_addr in; - struct in6_addr in6; - } inaddr; - size_t len; - - len = strspn(str, "0123456789abcdefABCDEF.:"); - ipbuf = malloc(len + 1); - AN(ipbuf); - memcpy(ipbuf, str, len); - ipbuf[len] = 0; - str += len; - - ipstart = ipbuf; - - if (str_is_ipv4(ipstart, len)) - cidr->family = AF_INET; - else if (str_is_ipv4mapped(ipstart, len)) { - cidr->family = AF_INET; - ipstart += PFXLEN_IPV4_MAPPED; - } else if (str_is_ipv6(ipbuf, len)) - cidr->family = AF_INET6; - else { - free(ipbuf); - syslog(LOG_DAEMON|LOG_ERR, "invalid CIDR: %s", ipstart); - return -1; - } - rc = inet_pton(cidr->family, ipstart, &inaddr); - free(ipbuf); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; - if (rc != 1) { - if (rc == -1) { - syslog(LOG_DAEMON|LOG_ERR, "invalid address family"); - return -1; - } - if (rc == 0) { - syslog(LOG_DAEMON|LOG_ERR, - "invalid IPv%s address: %s", - cidr->family == AF_INET ? "4" : "6", - str - len); - return -1; - } - } + rc = getaddrinfo(ipstr, NULL, &hints, &res); + if (rc) + return 0; - cidr->len = inaddr_to_bytes(cidr->family, &inaddr, cidr->address); + ip = VSA_Malloc(res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); - if (*str == '/') { - char *end; - unsigned long masklen; - - str++; + rc = VRT_acl_match(ctx, acl, ip); - masklen = strtoul(str, &end, 10); - if (*end == 0) { - masklen_to_netmask(cidr->netmask, cidr->len, masklen); - str = end; - } else { - size_t k = strspn(str, "0123456789abcdefABCDEF.:"); - - if ((cidr->family == AF_INET - && str_is_ipv4(str, k)) - || (cidr->family == AF_INET6 - && str_is_ipv6(str, k))) { - char mbuf[CIDR_MAXBUFSIZE]; - memcpy(mbuf, str, k); - mbuf[k] = 0; - rc = inet_pton(cidr->family, mbuf, &inaddr); - if (rc != 1) { - syslog(LOG_DAEMON|LOG_ERR, - "bad netmask: %s", mbuf); - return -1; - } - inaddr_to_bytes(cidr->family, &inaddr, - cidr->netmask); - } else { - syslog(LOG_DAEMON|LOG_ERR, - "bad CIDR (near %s)", str); - return -1; - } - } - } else - masklen_to_netmask(cidr->netmask, cidr->len, cidr->len * 8); - *endp = (char*) str; - return 0; -} - -/* A is a valid CIDR, and B is a valid IP address represented as CIDR. - Return 0 if B falls within A */ -static int -cidr_match(struct cidr *a, struct cidr *b) -{ - int i; - - if (a->family != b->family) - return 1; - for (i = 0; i < a->len; i++) { - if (a->address[i] != (b->address[i] & a->netmask[i])) - return 1; - } - return 0; -} - -static int -is_trusted_ip(CIDRHEAD *cl, char const *ipstr) -{ - struct cidr cidr, *cp; - char *endp; - - VSLIST_FOREACH(cp, cl, next) { - if (str_to_cidr(ipstr, &cidr, &endp) || *endp) - return 0; - if (cidr_match(cp, &cidr) == 0) - return 1; - } - return 0; + free(ip); + + return rc; } -int -VEVENT(remoteip_event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) -{ - CIDRHEAD *p; - - switch (e) { - case VCL_EVENT_LOAD: - p = calloc(1, sizeof(*p)); - AN(p); - priv->priv = p; - break; - default: - break; - } - return 0; -} - -void -vmod_init(VRT_CTX, struct vmod_priv *priv, VCL_STRING s) -{ - CIDRHEAD *cl = priv->priv; - struct cidr *cidr; - char *endp; - - while (*s) { - cidr = calloc(1, sizeof(cidr[0])); - if (str_to_cidr(s, cidr, &endp)) { - free(cidr); - break; - } - VSLIST_INSERT_HEAD(cl, cidr, next); - while (*endp && isspace(*endp)) - ++endp; - if (*endp == 0) - break; - AN(*endp == ','); - ++endp; - while (*endp && isspace(*endp)) - ++endp; - s = endp; - } -} - VCL_STRING -vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING hdr) +vmod_get(VRT_CTX, VCL_ACL acl, VCL_STRING hdr) { - CIDRHEAD *cl = priv->priv; unsigned u; char const *end; char *buf; int i = 0; - + u = WS_ReserveAll(ctx->ws); buf = ctx->ws->f; @@ -360,7 +89,7 @@ vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING hdr) } buf[i++] = 0; - if (!is_trusted_ip(cl, buf)) { + if (!is_trusted_ip(ctx, acl, buf)) { break; } end = p; |