Postfix3.3.1
dict_cidr.c
[詳解]
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 /* Wietse Venema
32 /* Google, Inc.
33 /* 111 8th Avenue
34 /* New York, NY 10011, USA
35 /*--*/
36 
37 /* System library. */
38 
39 #include <sys_defs.h>
40 #include <sys/stat.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 
49 /* Utility library. */
50 
51 #include <mymalloc.h>
52 #include <msg.h>
53 #include <vstream.h>
54 #include <vstring.h>
55 #include <stringops.h>
56 #include <readlline.h>
57 #include <dict.h>
58 #include <myaddrinfo.h>
59 #include <cidr_match.h>
60 #include <dict_cidr.h>
61 #include <warn_stat.h>
62 #include <mvect.h>
63 
64 /* Application-specific. */
65 
66  /*
67  * Each rule in a CIDR table is parsed and stored in a linked list.
68  */
69 typedef struct DICT_CIDR_ENTRY {
70  CIDR_MATCH cidr_info; /* must be first */
71  char *value; /* lookup result */
72  int lineno;
74 
75 typedef struct {
76  DICT dict; /* generic members */
77  DICT_CIDR_ENTRY *head; /* first entry */
78 } DICT_CIDR;
79 
80 /* dict_cidr_lookup - CIDR table lookup */
81 
82 static const char *dict_cidr_lookup(DICT *dict, const char *key)
83 {
84  DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
85  DICT_CIDR_ENTRY *entry;
86 
87  if (msg_verbose)
88  msg_info("dict_cidr_lookup: %s: %s", dict->name, key);
89 
90  dict->error = 0;
91 
92  if ((entry = (DICT_CIDR_ENTRY *)
93  cidr_match_execute(&(dict_cidr->head->cidr_info), key)) != 0)
94  return (entry->value);
95  return (0);
96 }
97 
98 /* dict_cidr_close - close the CIDR table */
99 
100 static void dict_cidr_close(DICT *dict)
101 {
102  DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
103  DICT_CIDR_ENTRY *entry;
104  DICT_CIDR_ENTRY *next;
105 
106  for (entry = dict_cidr->head; entry; entry = next) {
107  next = (DICT_CIDR_ENTRY *) entry->cidr_info.next;
108  myfree(entry->value);
109  myfree((void *) entry);
110  }
111  dict_free(dict);
112 }
113 
114 /* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
115 
116 static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, int lineno, int nesting,
117  VSTRING *why)
118 {
119  DICT_CIDR_ENTRY *rule;
120  char *pattern;
121  char *value;
122  CIDR_MATCH cidr_info;
123  MAI_HOSTADDR_STR hostaddr;
124  int match = 1;
125 
126  /*
127  * IF must be followed by a pattern.
128  */
129  if (strncasecmp(p, "IF", 2) == 0 && !ISALNUM(p[2])) {
130  p += 2;
131  for (;;) {
132  if (*p == '!')
133  match = !match;
134  else if (!ISSPACE(*p))
135  break;
136  p++;
137  }
138  if (*p == 0) {
139  vstring_sprintf(why, "no address pattern");
140  return (0);
141  }
142  trimblanks(p, 0)[0] = 0; /* Trim trailing blanks */
143  if (cidr_match_parse_if(&cidr_info, p, match, why) != 0)
144  return (0);
145  value = "";
146  }
147 
148  /*
149  * ENDIF must not be followed by other text.
150  */
151  else if (strncasecmp(p, "ENDIF", 5) == 0 && !ISALNUM(p[5])) {
152  p += 5;
153  while (*p && ISSPACE(*p)) /* Skip whitespace */
154  p++;
155  if (*p != 0) {
156  vstring_sprintf(why, "garbage after ENDIF");
157  return (0);
158  }
159  if (nesting == 0) {
160  vstring_sprintf(why, "ENDIF without IF");
161  return (0);
162  }
163  cidr_match_endif(&cidr_info);
164  value = "";
165  }
166 
167  /*
168  * An address pattern.
169  */
170  else {
171 
172  /*
173  * Process negation operators.
174  */
175  for (;;) {
176  if (*p == '!')
177  match = !match;
178  else if (!ISSPACE(*p))
179  break;
180  p++;
181  }
182 
183  /*
184  * Split the rule into key and value. We already eliminated leading
185  * whitespace, comments, empty lines or lines with whitespace only.
186  * This means a null key can't happen but we will handle this anyway.
187  */
188  pattern = p;
189  while (*p && !ISSPACE(*p)) /* Skip over key */
190  p++;
191  if (*p) /* Terminate key */
192  *p++ = 0;
193  while (*p && ISSPACE(*p)) /* Skip whitespace */
194  p++;
195  value = p;
196  trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */
197  if (*pattern == 0) {
198  vstring_sprintf(why, "no address pattern");
199  return (0);
200  }
201 
202  /*
203  * Parse the pattern, destroying it in the process.
204  */
205  if (cidr_match_parse(&cidr_info, pattern, match, why) != 0)
206  return (0);
207 
208  if (*value == 0) {
209  vstring_sprintf(why, "no lookup result");
210  return (0);
211  }
212  }
213 
214  /*
215  * Bundle up the result.
216  */
217  rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
218  rule->cidr_info = cidr_info;
219  rule->value = mystrdup(value);
220  rule->lineno = lineno;
221 
222  if (msg_verbose) {
223  if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes,
224  hostaddr.buf, sizeof(hostaddr.buf)) == 0)
225  msg_fatal("inet_ntop: %m");
226  msg_info("dict_cidr_open: add %s/%d %s",
227  hostaddr.buf, cidr_info.mask_shift, rule->value);
228  }
229  return (rule);
230 }
231 
232 /* dict_cidr_open - parse CIDR table */
233 
234 DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
235 {
236  const char myname[] = "dict_cidr_open";
237  DICT_CIDR *dict_cidr;
238  VSTREAM *map_fp = 0;
239  struct stat st;
240  VSTRING *line_buffer = 0;
241  VSTRING *why = 0;
242  DICT_CIDR_ENTRY *rule;
243  DICT_CIDR_ENTRY *last_rule = 0;
244  int last_line = 0;
245  int lineno;
246  int nesting = 0;
247  DICT_CIDR_ENTRY **rule_stack = 0;
248  MVECT mvect;
249 
250  /*
251  * Let the optimizer worry about eliminating redundant code.
252  */
253 #define DICT_CIDR_OPEN_RETURN(d) do { \
254  DICT *__d = (d); \
255  if (map_fp != 0 && vstream_fclose(map_fp)) \
256  msg_fatal("cidr map %s: read error: %m", mapname); \
257  if (line_buffer != 0) \
258  vstring_free(line_buffer); \
259  if (why != 0) \
260  vstring_free(why); \
261  return (__d); \
262  } while (0)
263 
264  /*
265  * Sanity checks.
266  */
267  if (open_flags != O_RDONLY)
269  open_flags, dict_flags,
270  "%s:%s map requires O_RDONLY access mode",
271  DICT_TYPE_CIDR, mapname));
272 
273  /*
274  * Open the configuration file.
275  */
276  if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
278  open_flags, dict_flags,
279  "open %s: %m", mapname));
280  if (fstat(vstream_fileno(map_fp), &st) < 0)
281  msg_fatal("fstat %s: %m", mapname);
282 
283  line_buffer = vstring_alloc(100);
284  why = vstring_alloc(100);
285 
286  /*
287  * XXX Eliminate unnecessary queries by setting a flag that says "this
288  * map matches network addresses only".
289  */
290  dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
291  sizeof(*dict_cidr));
292  dict_cidr->dict.lookup = dict_cidr_lookup;
293  dict_cidr->dict.close = dict_cidr_close;
294  dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
295  dict_cidr->head = 0;
296 
297  dict_cidr->dict.owner.uid = st.st_uid;
298  dict_cidr->dict.owner.status = (st.st_uid != 0);
299 
300  while (readllines(line_buffer, map_fp, &last_line, &lineno)) {
301  rule = dict_cidr_parse_rule(vstring_str(line_buffer), lineno,
302  nesting, why);
303  if (rule == 0) {
304  msg_warn("cidr map %s, line %d: %s: skipping this rule",
305  mapname, lineno, vstring_str(why));
306  continue;
307  }
308  if (rule->cidr_info.op == CIDR_MATCH_OP_IF) {
309  if (rule_stack == 0)
310  rule_stack = (DICT_CIDR_ENTRY **) mvect_alloc(&mvect,
311  sizeof(*rule_stack), nesting + 1,
312  (MVECT_FN) 0, (MVECT_FN) 0);
313  else
314  rule_stack =
315  (DICT_CIDR_ENTRY **) mvect_realloc(&mvect, nesting + 1);
316  rule_stack[nesting] = rule;
317  nesting++;
318  } else if (rule->cidr_info.op == CIDR_MATCH_OP_ENDIF) {
319  DICT_CIDR_ENTRY *if_rule;
320 
321  if (nesting-- <= 0)
322  /* Already handled in dict_cidr_parse_rule(). */
323  msg_panic("%s: ENDIF without IF", myname);
324  if_rule = rule_stack[nesting];
325  if (if_rule->cidr_info.op != CIDR_MATCH_OP_IF)
326  msg_panic("%s: unexpected rule stack element type %d",
327  myname, if_rule->cidr_info.op);
328  if_rule->cidr_info.block_end = &(rule->cidr_info);
329  }
330  if (last_rule == 0)
331  dict_cidr->head = rule;
332  else
333  last_rule->cidr_info.next = &(rule->cidr_info);
334  last_rule = rule;
335  }
336 
337  while (nesting-- > 0)
338  msg_warn("cidr map %s, line %d: IF has no matching ENDIF",
339  mapname, rule_stack[nesting]->lineno);
340 
341  if (rule_stack)
342  (void) mvect_free(&mvect);
343 
344  DICT_CIDR_OPEN_RETURN(DICT_DEBUG (&dict_cidr->dict));
345 }
void cidr_match_endif(CIDR_MATCH *ip)
Definition: cidr_match.c:301
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
uid_t uid
Definition: dict.h:39
char * mystrdup(const char *str)
Definition: mymalloc.c:225
Definition: mvect.h:19
unsigned char mask_shift
Definition: cidr_match.h:48
#define CIDR_MATCH_OP_IF
Definition: cidr_match.h:54
void(* close)(struct DICT *)
Definition: dict.h:87
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
struct CIDR_MATCH * block_end
Definition: cidr_match.h:50
char * name
Definition: dict.h:80
unsigned char net_bytes[CIDR_MATCH_ABYTES]
Definition: cidr_match.h:43
#define stat(p, s)
Definition: warn_stat.h:18
int flags
Definition: dict.h:81
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition: strcasecmp.c:52
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
#define ISALNUM(c)
Definition: sys_defs.h:1745
VSTRING * cidr_match_parse_if(CIDR_MATCH *ip, char *pattern, int match, VSTRING *why)
Definition: cidr_match.c:289
CIDR_MATCH cidr_info
Definition: dict_cidr.c:70
Definition: dict.h:78
DICT_CIDR_ENTRY * head
Definition: dict_cidr.c:77
char buf[MAI_HOSTADDR_STRSIZE]
Definition: myaddrinfo.h:146
CIDR_MATCH * cidr_match_execute(CIDR_MATCH *list, const char *addr)
Definition: cidr_match.c:148
const char * inet_ntop(int af, const void *src, char *dst, SOCKADDR_SIZE size)
Definition: sys_compat.c:325
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
struct DICT_CIDR_ENTRY DICT_CIDR_ENTRY
int error
Definition: dict.h:94
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
char * value
Definition: dict_cidr.c:71
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
char * mvect_free(MVECT *vect)
Definition: mvect.c:111
char * trimblanks(char *, ssize_t)
Definition: trimblanks.c:37
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
char * mvect_alloc(MVECT *vect, ssize_t elsize, ssize_t nelm, void(*init_fn)(char *, ssize_t), void(*wipe_fn)(char *, ssize_t))
Definition: mvect.c:75
int status
Definition: dict.h:38
VSTRING * readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
Definition: readlline.c:82
#define DICT_FLAG_PATTERN
Definition: dict.h:115
#define CIDR_MATCH_OP_ENDIF
Definition: cidr_match.h:55
void(* MVECT_FN)(char *, ssize_t)
Definition: mvect.h:17
void dict_free(DICT *)
Definition: dict_alloc.c:163
struct CIDR_MATCH * next
Definition: cidr_match.h:49
char * mvect_realloc(MVECT *vect, ssize_t nelm)
Definition: mvect.c:91
VSTRING * cidr_match_parse(CIDR_MATCH *ip, char *pattern, int match, VSTRING *why)
Definition: cidr_match.c:186
#define DICT_CIDR_OPEN_RETURN(d)
DICT dict
Definition: dict_cidr.c:76
DICT * dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
Definition: dict_cidr.c:234
unsigned char addr_family
Definition: cidr_match.h:45
#define vstream_fileno(vp)
Definition: vstream.h:115
#define ISSPACE(c)
Definition: sys_defs.h:1753
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
DICT_OWNER owner
Definition: dict.h:93
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
#define fstat(f, s)
Definition: warn_stat.h:20
#define DICT_TYPE_CIDR
Definition: dict_cidr.h:24
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199