xref: /netbsd/usr.sbin/faithd/prefix.c (revision 6550d01e)
1 /*	$NetBSD: prefix.c,v 1.8 2010/11/26 18:58:43 christos Exp $	*/
2 /*	$KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $	*/
3 
4 /*
5  * Copyright (C) 2000 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <stdio.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <limits.h>
42 
43 #ifndef offsetof
44 #define	offsetof(type, member)	((size_t)(u_long)(&((type *)0)->member))
45 #endif
46 
47 #include "faithd.h"
48 #include "prefix.h"
49 
50 static int prefix_set __P((const char *, struct prefix *, int));
51 static struct config *config_load1 __P((const char *));
52 #if 0
53 static void config_show1 __P((const struct config *));
54 static void config_show __P((void));
55 #endif
56 
57 struct config *config_list = NULL;
58 const int niflags = NI_NUMERICHOST;
59 
60 static int
61 prefix_set(const char *s, struct prefix *prefix, int slash)
62 {
63 	char *p = NULL, *q, *r;
64 	struct addrinfo hints, *res = NULL;
65 	int max;
66 
67 	p = strdup(s);
68 	if (!p)
69 		goto fail;
70 	q = strchr(p, '/');
71 	if (q) {
72 		if (!slash)
73 			goto fail;
74 		*q++ = '\0';
75 	}
76 
77 	memset(&hints, 0, sizeof(hints));
78 	hints.ai_family = PF_UNSPEC;
79 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
80 	hints.ai_flags = AI_NUMERICHOST;
81 	if (getaddrinfo(p, "0", &hints, &res))
82 		goto fail;
83 	if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
84 		goto fail;
85 	memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
86 
87 	switch (prefix->a.ss_family) {
88 	case AF_INET:
89 		max = 32;
90 		break;
91 	case AF_INET6:
92 		max = 128;
93 		break;
94 	default:
95 		max = -1;
96 		break;
97 	}
98 
99 	if (q) {
100 		r = NULL;
101 		prefix->l = (int)strtoul(q, &r, 10);
102 		if (!*q || *r)
103 			goto fail;
104 		if (prefix->l < 0 || prefix->l > max)
105 			goto fail;
106 	} else
107 		prefix->l = max;
108 
109 	if (p)
110 		free(p);
111 	if (res)
112 		freeaddrinfo(res);
113 	return 0;
114 
115 fail:
116 	if (p)
117 		free(p);
118 	if (res)
119 		freeaddrinfo(res);
120 	return -1;
121 }
122 
123 const char *
124 prefix_string(const struct prefix *prefix)
125 {
126 	static char buf[NI_MAXHOST + 20];
127 	char hbuf[NI_MAXHOST];
128 
129 	if (getnameinfo((const void *)&prefix->a, (socklen_t)prefix->a.ss_len,
130 	    hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags))
131 		return NULL;
132 	(void)snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
133 	return buf;
134 }
135 
136 int
137 prefix_match(const struct prefix *prefix, const struct sockaddr *sa)
138 {
139 	struct sockaddr_storage a, b;
140 	char *pa, *pb;
141 	int off, l;
142 
143 	if (prefix->a.ss_family != sa->sa_family ||
144 	    prefix->a.ss_len != sa->sa_len)
145 		return 0;
146 
147 	if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
148 		return 0;
149 
150 	switch (prefix->a.ss_family) {
151 	case AF_INET:
152 		off = offsetof(struct sockaddr_in, sin_addr);
153 		break;
154 	case AF_INET6:
155 		off = offsetof(struct sockaddr_in6, sin6_addr);
156 		break;
157 	default:
158 		if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
159 			return 0;
160 		else
161 			return 1;
162 	}
163 
164 	memcpy(&a, &prefix->a, prefix->a.ss_len);
165 	memcpy(&b, sa, sa->sa_len);
166 	l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
167 
168 	/* overrun check */
169 	if (off + l > a.ss_len)
170 		return 0;
171 
172 	pa = ((char *)(void *)&a) + off;
173 	pb = ((char *)(void *)&b) + off;
174 	if (prefix->l % 8) {
175 		pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
176 		pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
177 	}
178 	if (memcmp(pa, pb, l) != 0)
179 		return 0;
180 	else
181 		return 1;
182 }
183 
184 /*
185  * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
186  * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
187  */
188 static struct config *
189 config_load1(const char *line)
190 {
191 	struct config *conf;
192 	char buf[BUFSIZ];
193 	char *p;
194 	char *token[4];
195 	size_t i;
196 
197 	if (strlen(line) + 1 > sizeof(buf))
198 		return NULL;
199 	(void)strlcpy(buf, line, sizeof(buf));
200 
201 	p = strchr(buf, '\n');
202 	if (!p)
203 		return NULL;
204 	*p = '\0';
205 	p = strchr(buf, '#');
206 	if (p)
207 		*p = '\0';
208 	if (strlen(buf) == 0)
209 		return NULL;
210 
211 	p = buf;
212 	memset(token, 0, sizeof(token));
213 	for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
214 		token[i] = strtok(p, "\t ");
215 		p = NULL;
216 		if (token[i] == NULL)
217 			break;
218 	}
219 	/* extra tokens? */
220 	if (strtok(p, "\t ") != NULL)
221 		return NULL;
222 	/* insufficient tokens */
223 	switch (i) {
224 	case 3:
225 	case 4:
226 		break;
227 	default:
228 		return NULL;
229 	}
230 
231 	conf = (struct config *)malloc(sizeof(*conf));
232 	if (conf == NULL)
233 		return NULL;
234 	memset(conf, 0, sizeof(*conf));
235 
236 	if (strcasecmp(token[1], "permit") == 0)
237 		conf->permit = 1;
238 	else if (strcasecmp(token[1], "deny") == 0)
239 		conf->permit = 0;
240 	else {
241 		/* invalid keyword is considered as "deny" */
242 		conf->permit = 0;
243 	}
244 
245 	if (prefix_set(token[0], &conf->match, 1) < 0)
246 		goto fail;
247 	if (prefix_set(token[2], &conf->dest, 1) < 0)
248 		goto fail;
249 	if (token[3]) {
250 		if (prefix_set(token[3], &conf->src, 0) < 0)
251 			goto fail;
252 	}
253 
254 	return conf;
255 
256 fail:
257 	free(conf);
258 	return NULL;
259 }
260 
261 int
262 config_load(const char *configfile)
263 {
264 	FILE *fp;
265 	char buf[BUFSIZ];
266 	struct config *conf, *p;
267 	struct config sentinel;
268 
269 	config_list = NULL;
270 
271 	if (!configfile)
272 		configfile = _PATH_PREFIX_CONF;
273 	fp = fopen(configfile, "r");
274 	if (fp == NULL)
275 		return -1;
276 
277 	p = &sentinel;
278 	sentinel.next = NULL;
279 	while (fgets(buf, (int)sizeof(buf), fp) != NULL) {
280 		conf = config_load1(buf);
281 		if (conf) {
282 			p->next = conf;
283 			p = p->next;
284 		}
285 	}
286 	config_list = sentinel.next;
287 
288 	(void)fclose(fp);
289 	return 0;
290 }
291 
292 #if 0
293 static void
294 config_show1(const struct config *conf)
295 {
296 	const char *p;
297 
298 	p = prefix_string(&conf->match);
299 	printf("%s", p ? p : "?");
300 
301 	if (conf->permit)
302 		printf(" permit");
303 	else
304 		printf(" deny");
305 
306 	p = prefix_string(&conf->dest);
307 	printf(" %s", p ? p : "?");
308 
309 	printf("\n");
310 }
311 
312 static void
313 config_show()
314 {
315 	struct config *conf;
316 
317 	for (conf = config_list; conf; conf = conf->next)
318 		config_show1(conf);
319 }
320 #endif
321 
322 const struct config *
323 config_match(struct sockaddr *sa1, struct sockaddr *sa2)
324 {
325 	static struct config conf;
326 	const struct config *p;
327 
328 	if (sa1->sa_len > sizeof(conf.match.a) ||
329 	    sa2->sa_len > sizeof(conf.dest.a))
330 		return NULL;
331 
332 	memset(&conf, 0, sizeof(conf));
333 	if (!config_list) {
334 		conf.permit = 1;
335 		memcpy(&conf.match.a, sa1, sa1->sa_len);
336 		memcpy(&conf.dest.a, sa2, sa2->sa_len);
337 		return &conf;
338 	}
339 
340 	for (p = config_list; p; p = p->next)
341 		if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
342 			return p;
343 
344 	return NULL;
345 }
346