#include <sys_defs.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <msg.h>
#include <vstring.h>
#include <stringops.h>
#include <split_at.h>
#include <myaddrinfo.h>
#include <mask_addr.h>
#include <cidr_match.h>
#ifdef HAS_IPV6
#define CIDR_MATCH_ADDR_FAMILY(a) (strchr((a), ':') ? AF_INET6 : AF_INET)
#define CIDR_MATCH_ADDR_BIT_COUNT(f) \
((f) == AF_INET6 ? MAI_V6ADDR_BITS : \
(f) == AF_INET ? MAI_V4ADDR_BITS : \
(msg_panic("%s: bad address family %d", myname, (f)), 0))
#define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
((f) == AF_INET6 ? MAI_V6ADDR_BYTES : \
(f) == AF_INET ? MAI_V4ADDR_BYTES : \
(msg_panic("%s: bad address family %d", myname, (f)), 0))
#else
#define CIDR_MATCH_ADDR_FAMILY(a) (AF_INET)
#define CIDR_MATCH_ADDR_BIT_COUNT(f) \
((f) == AF_INET ? MAI_V4ADDR_BITS : \
(msg_panic("%s: bad address family %d", myname, (f)), 0))
#define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
((f) == AF_INET ? MAI_V4ADDR_BYTES : \
(msg_panic("%s: bad address family %d", myname, (f)), 0))
#endif
CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr)
{
unsigned char addr_bytes[CIDR_MATCH_ABYTES];
unsigned addr_family;
unsigned char *mp;
unsigned char *np;
unsigned char *ap;
CIDR_MATCH *entry;
addr_family = CIDR_MATCH_ADDR_FAMILY(addr);
if (inet_pton(addr_family, addr, addr_bytes) != 1)
return (0);
for (entry = list; entry; entry = entry->next) {
if (entry->addr_family == addr_family) {
if (entry->mask_shift < entry->addr_bit_count) {
for (np = entry->net_bytes, mp = entry->mask_bytes,
ap = addr_bytes; ; np++, mp++, ap++) {
if (ap >= addr_bytes + entry->addr_byte_count)
return (entry);
if ((*ap & *mp) != *np)
break;
}
}
else {
for (np = entry->net_bytes,
ap = addr_bytes; ; np++, ap++) {
if (ap >= addr_bytes + entry->addr_byte_count)
return (entry);
if (*ap != *np)
break;
}
}
}
}
return (0);
}
VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, VSTRING *why)
{
const char *myname = "cidr_match_parse";
char *mask_search;
char *mask;
MAI_HOSTADDR_STR hostaddr;
unsigned char *np;
unsigned char *mp;
if (*pattern == '[') {
pattern++;
if ((mask_search = split_at(pattern, ']')) == 0) {
vstring_sprintf(why ? why : (why = vstring_alloc(20)),
"missing ']' character after \"[%s\"", pattern);
return (why);
} else if (*mask_search != '/') {
if (*mask_search != 0) {
vstring_sprintf(why ? why : (why = vstring_alloc(20)),
"garbage after \"[%s]\"", pattern);
return (why);
}
mask_search = pattern;
}
} else
mask_search = pattern;
if ((mask = split_at(mask_search, '/')) != 0) {
ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family);
ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family);
if (!alldig(mask)
|| (ip->mask_shift = atoi(mask)) > ip->addr_bit_count
|| inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
vstring_sprintf(why ? why : (why = vstring_alloc(20)),
"bad net/mask pattern: \"%s/%s\"", pattern, mask);
return (why);
}
if (ip->mask_shift > 0) {
memset(ip->mask_bytes, (unsigned char) -1, ip->addr_byte_count);
mask_addr(ip->mask_bytes, ip->addr_byte_count, ip->mask_shift);
} else
memset(ip->mask_bytes, 0, ip->addr_byte_count);
for (np = ip->net_bytes, mp = ip->mask_bytes;
np < ip->net_bytes + ip->addr_byte_count; np++, mp++) {
if (*np & ~(*mp)) {
mask_addr(ip->net_bytes, ip->addr_byte_count, ip->mask_shift);
if (inet_ntop(ip->addr_family, ip->net_bytes, hostaddr.buf,
sizeof(hostaddr.buf)) == 0)
msg_fatal("inet_ntop: %m");
vstring_sprintf(why ? why : (why = vstring_alloc(20)),
"non-null host address bits in \"%s/%s\", "
"perhaps you should use \"%s/%d\" instead",
pattern, mask, hostaddr.buf, ip->mask_shift);
return (why);
}
}
}
else {
ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family);
ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family);
if (inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
vstring_sprintf(why ? why : (why = vstring_alloc(20)),
"bad address pattern: \"%s\"", pattern);
return (why);
}
ip->mask_shift = ip->addr_bit_count;
memset(ip->mask_bytes, (unsigned char) -1, ip->addr_byte_count);
}
ip->next = 0;
return (0);
}