5
/* Dictionary interface for CIDR data
7
/* #include <dict_cidr.h>
9
/* DICT *dict_cidr_open(name, open_flags, dict_flags)
14
/* dict_cidr_open() opens the named file and stores
15
/* the key/value pairs where the key must be either a
16
/* "naked" IP address or a netblock in CIDR notation.
18
/* dict(3) generic dictionary manager
21
/* kadlec@blackhole.kfki.hu
22
/* KFKI Research Institute for Particle and Nuclear Physics
24
/* 1525 Budapest, Hungary
27
/* IBM T.J. Watson Research
29
/* Yorktown Heights, NY 10598, USA
39
#include <netinet/in.h>
40
#include <arpa/inet.h>
43
#define INADDR_NONE 0xffffffff
46
/* Utility library. */
52
#include <stringops.h>
53
#include <readlline.h>
55
#include <dict_cidr.h>
58
/* Application-specific. */
61
* Each rule in a CIDR table is parsed and stored in a linked list.
62
* Obviously all this is IPV4 specific and needs to be redone for IPV6.
64
typedef struct DICT_CIDR_ENTRY {
65
unsigned long net_bits; /* network portion of address */
66
unsigned long mask_bits; /* network mask */
67
char *value; /* lookup result */
68
struct DICT_CIDR_ENTRY *next; /* next entry */
72
DICT dict; /* generic members */
73
DICT_CIDR_ENTRY *head; /* first entry */
76
#define BITS_PER_ADDR 32
78
/* dict_cidr_lookup - CIDR table lookup */
80
static const char *dict_cidr_lookup(DICT *dict, const char *key)
82
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
83
DICT_CIDR_ENTRY *entry;
87
msg_info("dict_cidr_lookup: %s: %s", dict_cidr->dict.name, key);
89
if ((addr = inet_addr(key)) == INADDR_NONE)
92
for (entry = dict_cidr->head; entry; entry = entry->next)
93
if ((addr & entry->mask_bits) == entry->net_bits)
94
return (entry->value);
99
/* dict_cidr_close - close the CIDR table */
101
static void dict_cidr_close(DICT *dict)
103
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
104
DICT_CIDR_ENTRY *entry;
105
DICT_CIDR_ENTRY *next;
107
for (entry = dict_cidr->head; entry; entry = next) {
109
myfree(entry->value);
110
myfree((char *) entry);
115
/* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
117
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(const char *mapname, int lineno,
120
DICT_CIDR_ENTRY *rule;
125
unsigned long net_bits;
126
unsigned long mask_bits;
127
struct in_addr net_addr;
130
* Split the rule into key and value. We already eliminated leading
131
* whitespace, comments, empty lines or lines with whitespace only. This
132
* means a null key can't happen but we will handle this anyway.
135
while (*p && !ISSPACE(*p)) /* Skip over key */
137
if (*p) /* Terminate key */
139
while (*p && ISSPACE(*p)) /* Skip whitespace */
142
trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */
144
msg_warn("cidr map %s, line %d: no address pattern: skipping this rule",
149
msg_warn("cidr map %s, line %d: no lookup result: skipping this rule",
155
* Parse the key into network and mask, and destroy the key. Treat a bare
156
* network address as /32.
158
* We need explicit code for /0. The result of << is undefined when the
159
* shift is greater or equal to the number of bits in the shifted
162
if ((mask = split_at(key, '/')) != 0) {
163
if (!alldig(mask) || (mask_shift = atoi(mask)) > BITS_PER_ADDR
164
|| (net_bits = inet_addr(key)) == INADDR_NONE) {
165
msg_warn("cidr map %s, line %d: bad net/mask pattern: \"%s/%s\": "
166
"skipping this rule", mapname, lineno, key, mask);
169
mask_bits = mask_shift > 0 ?
170
htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift)) : 0;
171
if (net_bits & ~mask_bits) {
172
net_addr.s_addr = (net_bits & mask_bits);
173
msg_warn("cidr map %s, line %d: net/mask pattern \"%s/%s\" with "
174
"non-null host portion: skipping this rule",
175
mapname, lineno, key, mask);
176
msg_warn("specify \"%s/%d\" if this is really what you want",
177
inet_ntoa(net_addr), mask_shift);
181
if ((net_bits = inet_addr(key)) == INADDR_NONE) {
182
msg_warn("cidr map %s, line %d: bad address pattern: \"%s\": "
183
"skipping this rule", mapname, lineno, key);
187
mask_bits = htonl(0xffffffff);
191
* Bundle up the result.
193
rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
194
rule->net_bits = net_bits;
195
rule->mask_bits = mask_bits;
196
rule->value = mystrdup(value);
200
msg_info("dict_cidr_open: %s: %lu/%d %s",
201
mapname, rule->net_bits, mask_shift, rule->value);
206
/* dict_cidr_open - parse CIDR table */
208
DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
210
DICT_CIDR *dict_cidr;
212
VSTRING *line_buffer = vstring_alloc(100);
213
DICT_CIDR_ENTRY *rule;
214
DICT_CIDR_ENTRY *last_rule = 0;
220
if (open_flags != O_RDONLY)
221
msg_fatal("%s:%s map requires O_RDONLY access mode",
222
DICT_TYPE_CIDR, mapname);
225
* XXX Eliminate unnecessary queries by setting a flag that says "this
226
* map matches network addresses only".
228
dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
230
dict_cidr->dict.lookup = dict_cidr_lookup;
231
dict_cidr->dict.close = dict_cidr_close;
232
dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
235
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
236
msg_fatal("open %s: %m", mapname);
238
while (readlline(line_buffer, map_fp, &lineno)) {
239
rule = dict_cidr_parse_rule(mapname, lineno, vstring_str(line_buffer));
243
dict_cidr->head = rule;
245
last_rule->next = rule;
252
if (vstream_fclose(map_fp))
253
msg_fatal("cidr map %s: read error: %m", mapname);
254
vstring_free(line_buffer);
256
return (DICT_DEBUG (&dict_cidr->dict));