xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_cidr.c (revision 5dd36a3bc8bf2a9dec29ceb6349550414570c447)
1 /*	$NetBSD: dict_cidr.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_cidr 3
6 /* SUMMARY
7 /*	Dictionary interface for CIDR data
8 /* SYNOPSIS
9 /*	#include <dict_cidr.h>
10 /*
11 /*	DICT	*dict_cidr_open(name, open_flags, dict_flags)
12 /*	const char *name;
13 /*	int	open_flags;
14 /*	int	dict_flags;
15 /* DESCRIPTION
16 /*	dict_cidr_open() opens the named file and stores
17 /*	the key/value pairs where the key must be either a
18 /*	"naked" IP address or a netblock in CIDR notation.
19 /* SEE ALSO
20 /*	dict(3) generic dictionary manager
21 /* AUTHOR(S)
22 /*	Jozsef Kadlecsik
23 /*	kadlec@blackhole.kfki.hu
24 /*	KFKI Research Institute for Particle and Nuclear Physics
25 /*	POB. 49
26 /*	1525 Budapest, Hungary
27 /*
28 /*	Wietse Venema
29 /*	IBM T.J. Watson Research
30 /*	P.O. Box 704
31 /*	Yorktown Heights, NY 10598, USA
32 /*--*/
33 
34 /* System library. */
35 
36 #include <sys_defs.h>
37 #include <sys/stat.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
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 <myaddrinfo.h>
56 #include <cidr_match.h>
57 #include <dict_cidr.h>
58 #include <warn_stat.h>
59 
60 /* Application-specific. */
61 
62  /*
63   * Each rule in a CIDR table is parsed and stored in a linked list.
64   */
65 typedef struct DICT_CIDR_ENTRY {
66     CIDR_MATCH cidr_info;		/* must be first */
67     char   *value;			/* lookup result */
68 } DICT_CIDR_ENTRY;
69 
70 typedef struct {
71     DICT    dict;			/* generic members */
72     DICT_CIDR_ENTRY *head;		/* first entry */
73 } DICT_CIDR;
74 
75 /* dict_cidr_lookup - CIDR table lookup */
76 
77 static const char *dict_cidr_lookup(DICT *dict, const char *key)
78 {
79     DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
80     DICT_CIDR_ENTRY *entry;
81 
82     if (msg_verbose)
83 	msg_info("dict_cidr_lookup: %s: %s", dict->name, key);
84 
85     dict->error = 0;
86 
87     if ((entry = (DICT_CIDR_ENTRY *)
88 	 cidr_match_execute(&(dict_cidr->head->cidr_info), key)) != 0)
89 	return (entry->value);
90     return (0);
91 }
92 
93 /* dict_cidr_close - close the CIDR table */
94 
95 static void dict_cidr_close(DICT *dict)
96 {
97     DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
98     DICT_CIDR_ENTRY *entry;
99     DICT_CIDR_ENTRY *next;
100 
101     for (entry = dict_cidr->head; entry; entry = next) {
102 	next = (DICT_CIDR_ENTRY *) entry->cidr_info.next;
103 	myfree(entry->value);
104 	myfree((void *) entry);
105     }
106     dict_free(dict);
107 }
108 
109 /* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
110 
111 static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why)
112 {
113     DICT_CIDR_ENTRY *rule;
114     char   *pattern;
115     char   *value;
116     CIDR_MATCH cidr_info;
117     MAI_HOSTADDR_STR hostaddr;
118 
119     /*
120      * Split the rule into key and value. We already eliminated leading
121      * whitespace, comments, empty lines or lines with whitespace only. This
122      * means a null key can't happen but we will handle this anyway.
123      */
124     pattern = p;
125     while (*p && !ISSPACE(*p))			/* Skip over key */
126 	p++;
127     if (*p)					/* Terminate key */
128 	*p++ = 0;
129     while (*p && ISSPACE(*p))			/* Skip whitespace */
130 	p++;
131     value = p;
132     trimblanks(value, 0)[0] = 0;		/* Trim trailing blanks */
133     if (*pattern == 0) {
134 	vstring_sprintf(why, "no address pattern");
135 	return (0);
136     }
137     if (*value == 0) {
138 	vstring_sprintf(why, "no lookup result");
139 	return (0);
140     }
141 
142     /*
143      * Parse the pattern, destroying it in the process.
144      */
145     if (cidr_match_parse(&cidr_info, pattern, why) != 0)
146 	return (0);
147 
148     /*
149      * Bundle up the result.
150      */
151     rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
152     rule->cidr_info = cidr_info;
153     rule->value = mystrdup(value);
154 
155     if (msg_verbose) {
156 	if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes,
157 		      hostaddr.buf, sizeof(hostaddr.buf)) == 0)
158 	    msg_fatal("inet_ntop: %m");
159 	msg_info("dict_cidr_open: add %s/%d %s",
160 		 hostaddr.buf, cidr_info.mask_shift, rule->value);
161     }
162     return (rule);
163 }
164 
165 /* dict_cidr_open - parse CIDR table */
166 
167 DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
168 {
169     DICT_CIDR *dict_cidr;
170     VSTREAM *map_fp = 0;
171     struct stat st;
172     VSTRING *line_buffer = 0;
173     VSTRING *why = 0;
174     DICT_CIDR_ENTRY *rule;
175     DICT_CIDR_ENTRY *last_rule = 0;
176     int     last_line = 0;
177     int     lineno;
178 
179     /*
180      * Let the optimizer worry about eliminating redundant code.
181      */
182 #define DICT_CIDR_OPEN_RETURN(d) do { \
183 	DICT *__d = (d); \
184 	if (map_fp != 0 && vstream_fclose(map_fp)) \
185 	    msg_fatal("cidr map %s: read error: %m", mapname); \
186 	if (line_buffer != 0) \
187 	    vstring_free(line_buffer); \
188 	if (why != 0) \
189 	    vstring_free(why); \
190 	return (__d); \
191     } while (0)
192 
193     /*
194      * Sanity checks.
195      */
196     if (open_flags != O_RDONLY)
197 	DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname,
198 					     open_flags, dict_flags,
199 				  "%s:%s map requires O_RDONLY access mode",
200 					     DICT_TYPE_CIDR, mapname));
201 
202     /*
203      * Open the configuration file.
204      */
205     if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
206 	DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname,
207 					     open_flags, dict_flags,
208 					     "open %s: %m", mapname));
209     if (fstat(vstream_fileno(map_fp), &st) < 0)
210 	msg_fatal("fstat %s: %m", mapname);
211 
212     line_buffer = vstring_alloc(100);
213     why = vstring_alloc(100);
214 
215     /*
216      * XXX Eliminate unnecessary queries by setting a flag that says "this
217      * map matches network addresses only".
218      */
219     dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
220 					 sizeof(*dict_cidr));
221     dict_cidr->dict.lookup = dict_cidr_lookup;
222     dict_cidr->dict.close = dict_cidr_close;
223     dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
224     dict_cidr->head = 0;
225 
226     dict_cidr->dict.owner.uid = st.st_uid;
227     dict_cidr->dict.owner.status = (st.st_uid != 0);
228 
229     while (readllines(line_buffer, map_fp, &last_line, &lineno)) {
230 	rule = dict_cidr_parse_rule(vstring_str(line_buffer), why);
231 	if (rule == 0) {
232 	    msg_warn("cidr map %s, line %d: %s: skipping this rule",
233 		     mapname, lineno, vstring_str(why));
234 	    continue;
235 	}
236 	if (last_rule == 0)
237 	    dict_cidr->head = rule;
238 	else
239 	    last_rule->cidr_info.next = &(rule->cidr_info);
240 	last_rule = rule;
241     }
242 
243     DICT_CIDR_OPEN_RETURN(DICT_DEBUG (&dict_cidr->dict));
244 }
245