~ubuntu-branches/ubuntu/hoary/postfix/hoary-security

« back to all changes in this revision

Viewing changes to src/util/dict_cidr.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      dict_cidr 3
 
4
/* SUMMARY
 
5
/*      Dictionary interface for CIDR data
 
6
/* SYNOPSIS
 
7
/*      #include <dict_cidr.h>
 
8
/*
 
9
/*      DICT    *dict_cidr_open(name, open_flags, dict_flags)
 
10
/*      const char *name;
 
11
/*      int     open_flags;
 
12
/*      int     dict_flags;
 
13
/* DESCRIPTION
 
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.
 
17
/* SEE ALSO
 
18
/*      dict(3) generic dictionary manager
 
19
/* AUTHOR(S)
 
20
/*      Jozsef Kadlecsik
 
21
/*      kadlec@blackhole.kfki.hu
 
22
/*      KFKI Research Institute for Particle and Nuclear Physics
 
23
/*      POB. 49
 
24
/*      1525 Budapest, Hungary
 
25
/*
 
26
/*      Wietse Venema
 
27
/*      IBM T.J. Watson Research
 
28
/*      P.O. Box 704
 
29
/*      Yorktown Heights, NY 10598, USA
 
30
/*--*/
 
31
 
 
32
/* System library. */
 
33
 
 
34
#include <sys_defs.h>
 
35
#include <stdlib.h>
 
36
#include <unistd.h>
 
37
#include <string.h>
 
38
#include <ctype.h>
 
39
#include <netinet/in.h>
 
40
#include <arpa/inet.h>
 
41
 
 
42
#ifndef INADDR_NONE
 
43
#define INADDR_NONE 0xffffffff
 
44
#endif
 
45
 
 
46
/* Utility library. */
 
47
 
 
48
#include <mymalloc.h>
 
49
#include <msg.h>
 
50
#include <vstream.h>
 
51
#include <vstring.h>
 
52
#include <stringops.h>
 
53
#include <readlline.h>
 
54
#include <dict.h>
 
55
#include <dict_cidr.h>
 
56
#include <split_at.h>
 
57
 
 
58
/* Application-specific. */
 
59
 
 
60
 /*
 
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.
 
63
  */
 
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 */
 
69
} DICT_CIDR_ENTRY;
 
70
 
 
71
typedef struct {
 
72
    DICT    dict;                       /* generic members */
 
73
    DICT_CIDR_ENTRY *head;              /* first entry */
 
74
} DICT_CIDR;
 
75
 
 
76
#define BITS_PER_ADDR   32
 
77
 
 
78
/* dict_cidr_lookup - CIDR table lookup */
 
79
 
 
80
static const char *dict_cidr_lookup(DICT *dict, const char *key)
 
81
{
 
82
    DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
 
83
    DICT_CIDR_ENTRY *entry;
 
84
    unsigned long addr;
 
85
 
 
86
    if (msg_verbose)
 
87
        msg_info("dict_cidr_lookup: %s: %s", dict_cidr->dict.name, key);
 
88
 
 
89
    if ((addr = inet_addr(key)) == INADDR_NONE)
 
90
        return (0);
 
91
 
 
92
    for (entry = dict_cidr->head; entry; entry = entry->next)
 
93
        if ((addr & entry->mask_bits) == entry->net_bits)
 
94
            return (entry->value);
 
95
 
 
96
    return (0);
 
97
}
 
98
 
 
99
/* dict_cidr_close - close the CIDR table */
 
100
 
 
101
static void dict_cidr_close(DICT *dict)
 
102
{
 
103
    DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
 
104
    DICT_CIDR_ENTRY *entry;
 
105
    DICT_CIDR_ENTRY *next;
 
106
 
 
107
    for (entry = dict_cidr->head; entry; entry = next) {
 
108
        next = entry->next;
 
109
        myfree(entry->value);
 
110
        myfree((char *) entry);
 
111
    }
 
112
    dict_free(dict);
 
113
}
 
114
 
 
115
/* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
 
116
 
 
117
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(const char *mapname, int lineno,
 
118
                                                     char *p)
 
119
{
 
120
    DICT_CIDR_ENTRY *rule;
 
121
    char   *key;
 
122
    char   *value;
 
123
    char   *mask;
 
124
    int     mask_shift;
 
125
    unsigned long net_bits;
 
126
    unsigned long mask_bits;
 
127
    struct in_addr net_addr;
 
128
 
 
129
    /*
 
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.
 
133
     */
 
134
    key = p;
 
135
    while (*p && !ISSPACE(*p))                  /* Skip over key */
 
136
        p++;
 
137
    if (*p)                                     /* Terminate key */
 
138
        *p++ = 0;
 
139
    while (*p && ISSPACE(*p))                   /* Skip whitespace */
 
140
        p++;
 
141
    value = p;
 
142
    trimblanks(value, 0)[0] = 0;                /* Trim trailing blanks */
 
143
    if (*key == 0) {
 
144
        msg_warn("cidr map %s, line %d: no address pattern: skipping this rule",
 
145
                 mapname, lineno);
 
146
        return (0);
 
147
    }
 
148
    if (*value == 0) {
 
149
        msg_warn("cidr map %s, line %d: no lookup result: skipping this rule",
 
150
                 mapname, lineno);
 
151
        return (0);
 
152
    }
 
153
 
 
154
    /*
 
155
     * Parse the key into network and mask, and destroy the key. Treat a bare
 
156
     * network address as /32.
 
157
     * 
 
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
 
160
     * operand.
 
161
     */
 
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);
 
167
            return (0);
 
168
        }
 
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);
 
178
            return (0);
 
179
        }
 
180
    } else {
 
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);
 
184
            return (0);
 
185
        }
 
186
        mask_shift = 32;
 
187
        mask_bits = htonl(0xffffffff);
 
188
    }
 
189
 
 
190
    /*
 
191
     * Bundle up the result.
 
192
     */
 
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);
 
197
    rule->next = 0;
 
198
 
 
199
    if (msg_verbose)
 
200
        msg_info("dict_cidr_open: %s: %lu/%d %s",
 
201
                 mapname, rule->net_bits, mask_shift, rule->value);
 
202
 
 
203
    return (rule);
 
204
}
 
205
 
 
206
/* dict_cidr_open - parse CIDR table */
 
207
 
 
208
DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
 
209
{
 
210
    DICT_CIDR *dict_cidr;
 
211
    VSTREAM *map_fp;
 
212
    VSTRING *line_buffer = vstring_alloc(100);
 
213
    DICT_CIDR_ENTRY *rule;
 
214
    DICT_CIDR_ENTRY *last_rule = 0;
 
215
    int     lineno = 0;
 
216
 
 
217
    /*
 
218
     * Sanity checks.
 
219
     */
 
220
    if (open_flags != O_RDONLY)
 
221
        msg_fatal("%s:%s map requires O_RDONLY access mode",
 
222
                  DICT_TYPE_CIDR, mapname);
 
223
 
 
224
    /*
 
225
     * XXX Eliminate unnecessary queries by setting a flag that says "this
 
226
     * map matches network addresses only".
 
227
     */
 
228
    dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
 
229
                                         sizeof(*dict_cidr));
 
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;
 
233
    dict_cidr->head = 0;
 
234
 
 
235
    if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
 
236
        msg_fatal("open %s: %m", mapname);
 
237
 
 
238
    while (readlline(line_buffer, map_fp, &lineno)) {
 
239
        rule = dict_cidr_parse_rule(mapname, lineno, vstring_str(line_buffer));
 
240
        if (rule == 0)
 
241
            continue;
 
242
        if (last_rule == 0)
 
243
            dict_cidr->head = rule;
 
244
        else
 
245
            last_rule->next = rule;
 
246
        last_rule = rule;
 
247
    }
 
248
 
 
249
    /*
 
250
     * Clean up.
 
251
     */
 
252
    if (vstream_fclose(map_fp))
 
253
        msg_fatal("cidr map %s: read error: %m", mapname);
 
254
    vstring_free(line_buffer);
 
255
 
 
256
    return (DICT_DEBUG (&dict_cidr->dict));
 
257
}