/* grecs - Gray's Extensible Configuration System Copyright (C) 2007-2016 Sergey Poznyakoff Grecs 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 3 of the License, or (at your option) any later version. Grecs 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 Grecs. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "grecs.h" 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; } } int grecs_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; } return 0; } int grecs_sockaddr_to_bytes(unsigned char *bytes, struct sockaddr const *sa) { switch (sa->sa_family) { case AF_INET: uint32_to_bytes(bytes, ((struct sockaddr_in*)sa)->sin_addr.s_addr); return 4; case AF_INET6: memcpy(bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16); return 16; } return 0; } int grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa) { unsigned char address[GRECS_INADDR_BYTES]; int len; int i; len = grecs_sockaddr_to_bytes(address, sa); if (len == 0) return -1; cidr->family = sa->sa_family; cidr->len = len; memcpy(cidr->address, address, sizeof(cidr->address)); for (i = 0; i < GRECS_INADDR_BYTES; i++) cidr->netmask[i] = 0xff; return 0; } 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 == GRECS_INADDR_BYTES) return; cnt = 8 - masklen % 8; buf[i++] = (0xff >> cnt) << cnt; for (; i < GRECS_INADDR_BYTES; i++) buf[i] = 0; } int grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str, grecs_locus_t const *locus) { int rc; char ipbuf[41]; struct grecs_cidr cidr; char *p; size_t len; union { struct in_addr in; struct in6_addr in6; } inaddr; p = strchr(str, '/'); if (p) len = p - str; else len = strlen(str); if (len > sizeof(ipbuf)) { grecs_error(locus, 0, _("invalid network mask: %s"), str); return -1; } memcpy(ipbuf, str, len); ipbuf[len] = 0; if (grecs_str_is_ipv4(ipbuf)) cidr.family = AF_INET; else if (grecs_str_is_ipv6(ipbuf)) cidr.family = AF_INET6; else { grecs_error(locus, 0, _("unrecognized address family: %s"), str); return -1; } rc = inet_pton(cidr.family, ipbuf, &inaddr); if (rc == -1) { grecs_error(locus, 0, _("unrecognized address family: %s"), str); return -1; } else if (rc != 1) { grecs_error(locus, 0, _("invalid network address: %s"), str); return -1; } cidr.len = grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.address); if (cidr.len == 0) { grecs_error(locus, 0, _("unrecognized address family: %s"), str); return -1; } if (p) { char *end; unsigned long masklen; p++; masklen = strtoul(p, &end, 10); if (*end == 0) masklen_to_netmask(cidr.netmask, cidr.len, masklen); else if ((cidr.family == AF_INET && grecs_str_is_ipv4(p)) || (cidr.family == AF_INET6 && grecs_str_is_ipv6(ipbuf))) { rc = inet_pton(cidr.family, p, &inaddr); if (rc != 1) { grecs_error(locus, 0, _("invalid network mask: %s"), str); return -1; } grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.netmask); } else { grecs_error(locus, 0, _("invalid network mask: %s"), str); return -1; } } else masklen_to_netmask(cidr.netmask, cidr.len, cidr.len * 8); memcpy(pcidr, &cidr, sizeof(*pcidr)); return 0; } int grecs_cidr_match(struct grecs_cidr *a, struct grecs_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; } int grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr) { struct grecs_cidr t; if (grecs_sockaddr_to_cidr(&t, sa)) return 1; return grecs_cidr_match(cidr, &t); }