#include <sys_defs.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
#include <mymalloc.h>
#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <stringops.h>
#include <readlline.h>
#include <dict.h>
#include <dict_cidr.h>
#include <split_at.h>
typedef struct DICT_CIDR_ENTRY {
unsigned long net_bits;
unsigned long mask_bits;
char *value;
struct DICT_CIDR_ENTRY *next;
} DICT_CIDR_ENTRY;
typedef struct {
DICT dict;
DICT_CIDR_ENTRY *head;
} DICT_CIDR;
#define BITS_PER_ADDR 32
static const char *dict_cidr_lookup(DICT *dict, const char *key)
{
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
DICT_CIDR_ENTRY *entry;
unsigned long addr;
if (msg_verbose)
msg_info("dict_cidr_lookup: %s: %s", dict_cidr->dict.name, key);
if ((addr = inet_addr(key)) == INADDR_NONE)
return (0);
for (entry = dict_cidr->head; entry; entry = entry->next)
if ((addr & entry->mask_bits) == entry->net_bits)
return (entry->value);
return (0);
}
static void dict_cidr_close(DICT *dict)
{
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
DICT_CIDR_ENTRY *entry;
DICT_CIDR_ENTRY *next;
for (entry = dict_cidr->head; entry; entry = next) {
next = entry->next;
myfree(entry->value);
myfree((char *) entry);
}
dict_free(dict);
}
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(const char *mapname, int lineno,
char *p)
{
DICT_CIDR_ENTRY *rule;
char *key;
char *value;
char *mask;
int mask_shift;
unsigned long net_bits;
unsigned long mask_bits;
struct in_addr net_addr;
key = p;
while (*p && !ISSPACE(*p))
p++;
if (*p)
*p++ = 0;
while (*p && ISSPACE(*p))
p++;
value = p;
trimblanks(value, 0)[0] = 0;
if (*key == 0) {
msg_warn("cidr map %s, line %d: no address pattern: skipping this rule",
mapname, lineno);
return (0);
}
if (*value == 0) {
msg_warn("cidr map %s, line %d: no lookup result: skipping this rule",
mapname, lineno);
return (0);
}
if ((mask = split_at(key, '/')) != 0) {
if (!alldig(mask) || (mask_shift = atoi(mask)) > BITS_PER_ADDR
|| (net_bits = inet_addr(key)) == INADDR_NONE) {
msg_warn("cidr map %s, line %d: bad net/mask pattern: \"%s/%s\": "
"skipping this rule", mapname, lineno, key, mask);
return (0);
}
mask_bits = mask_shift > 0 ?
htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift)) : 0;
if (net_bits & ~mask_bits) {
net_addr.s_addr = (net_bits & mask_bits);
msg_warn("cidr map %s, line %d: net/mask pattern \"%s/%s\" with "
"non-null host portion: skipping this rule",
mapname, lineno, key, mask);
msg_warn("specify \"%s/%d\" if this is really what you want",
inet_ntoa(net_addr), mask_shift);
return (0);
}
} else {
if ((net_bits = inet_addr(key)) == INADDR_NONE) {
msg_warn("cidr map %s, line %d: bad address pattern: \"%s\": "
"skipping this rule", mapname, lineno, key);
return (0);
}
mask_shift = 32;
mask_bits = htonl(0xffffffff);
}
rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
rule->net_bits = net_bits;
rule->mask_bits = mask_bits;
rule->value = mystrdup(value);
rule->next = 0;
if (msg_verbose)
msg_info("dict_cidr_open: %s: %lu/%d %s",
mapname, rule->net_bits, mask_shift, rule->value);
return (rule);
}
DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
{
DICT_CIDR *dict_cidr;
VSTREAM *map_fp;
VSTRING *line_buffer = vstring_alloc(100);
DICT_CIDR_ENTRY *rule;
DICT_CIDR_ENTRY *last_rule = 0;
int lineno = 0;
if (open_flags != O_RDONLY)
msg_fatal("%s:%s map requires O_RDONLY access mode",
DICT_TYPE_CIDR, mapname);
dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
sizeof(*dict_cidr));
dict_cidr->dict.lookup = dict_cidr_lookup;
dict_cidr->dict.close = dict_cidr_close;
dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
dict_cidr->head = 0;
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
while (readlline(line_buffer, map_fp, &lineno)) {
rule = dict_cidr_parse_rule(mapname, lineno, vstring_str(line_buffer));
if (rule == 0)
continue;
if (last_rule == 0)
dict_cidr->head = rule;
else
last_rule->next = rule;
last_rule = rule;
}
if (vstream_fclose(map_fp))
msg_fatal("cidr map %s: read error: %m", mapname);
vstring_free(line_buffer);
return (DICT_DEBUG (&dict_cidr->dict));
}