1122679Sume/*	$KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $	*/
278064Sume/*	$FreeBSD$	*/
378064Sume
478064Sume/*
578064Sume * Copyright (C) 2000 WIDE Project.
678064Sume * All rights reserved.
778064Sume *
878064Sume * Redistribution and use in source and binary forms, with or without
978064Sume * modification, are permitted provided that the following conditions
1078064Sume * are met:
1178064Sume * 1. Redistributions of source code must retain the above copyright
1278064Sume *    notice, this list of conditions and the following disclaimer.
1378064Sume * 2. Redistributions in binary form must reproduce the above copyright
1478064Sume *    notice, this list of conditions and the following disclaimer in the
1578064Sume *    documentation and/or other materials provided with the distribution.
1678064Sume * 3. Neither the name of the project nor the names of its contributors
1778064Sume *    may be used to endorse or promote products derived from this software
1878064Sume *    without specific prior written permission.
1978064Sume *
2078064Sume * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2178064Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2278064Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2378064Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2478064Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2578064Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2678064Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2778064Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2878064Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2978064Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3078064Sume * SUCH DAMAGE.
3178064Sume */
3278064Sume
3378064Sume#include <sys/types.h>
3478064Sume#include <sys/socket.h>
3578064Sume#include <netinet/in.h>
3678064Sume#include <stdio.h>
3778064Sume#include <netdb.h>
3878064Sume#include <string.h>
3978064Sume#include <stddef.h>
4078064Sume#include <stdlib.h>
4178064Sume#include <limits.h>
4278064Sume
4378064Sume#ifndef offsetof
4478064Sume#define	offsetof(type, member)	((size_t)(u_long)(&((type *)0)->member))
4578064Sume#endif
4678064Sume
4778064Sume#include "faithd.h"
4878064Sume#include "prefix.h"
4978064Sume
50173412Skevlostatic int prefix_set(const char *, struct prefix *, int);
51173412Skevlostatic struct config *config_load1(const char *);
5278064Sume#if 0
53173412Skevlostatic void config_show1(const struct config *);
54173412Skevlostatic void config_show(void);
5578064Sume#endif
5678064Sume
5778064Sumestruct config *config_list = NULL;
5878064Sumeconst int niflags = NI_NUMERICHOST;
5978064Sume
6078064Sumestatic int
61122679Sumeprefix_set(const char *s, struct prefix *prefix, int slash)
6278064Sume{
63122679Sume	char *p = NULL, *q, *r;
6478064Sume	struct addrinfo hints, *res = NULL;
6578064Sume	int max;
6678064Sume	char *a;
6778064Sume
6878064Sume	p = strdup(s);
69122679Sume	if (!p)
70122679Sume		goto fail;
7178064Sume	q = strchr(p, '/');
7278064Sume	if (q) {
7378064Sume		if (!slash)
7478064Sume			goto fail;
7578064Sume		*q++ = '\0';
7678064Sume	}
7778064Sume
7878064Sume	memset(&hints, 0, sizeof(hints));
7978064Sume	hints.ai_family = PF_UNSPEC;
8078064Sume	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
8178064Sume	hints.ai_flags = AI_NUMERICHOST;
8278064Sume	if (getaddrinfo(p, "0", &hints, &res))
8378064Sume		goto fail;
8478064Sume	if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
8578064Sume		goto fail;
8678064Sume	memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
8778064Sume
8878064Sume	switch (prefix->a.ss_family) {
8978064Sume	case AF_INET:
9078064Sume		max = 32;
9178064Sume		a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr;
9278064Sume		break;
9378064Sume	case AF_INET6:
9478064Sume		max = 128;
9578064Sume		a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr;
9678064Sume		break;
9778064Sume	default:
9878064Sume		a = NULL;
9978064Sume		max = -1;
10078064Sume		break;
10178064Sume	}
10278064Sume
10378064Sume	if (q) {
10478064Sume		r = NULL;
10578064Sume		prefix->l = (int)strtoul(q, &r, 10);
10678064Sume		if (!*q || *r)
10778064Sume			goto fail;
10878064Sume		if (prefix->l < 0 || prefix->l > max)
10978064Sume			goto fail;
11078064Sume	} else
11178064Sume		prefix->l = max;
11278064Sume
11378064Sume	if (p)
11478064Sume		free(p);
11578064Sume	if (res)
11678064Sume		freeaddrinfo(res);
11778064Sume	return 0;
11878064Sume
11978064Sumefail:
12078064Sume	if (p)
12178064Sume		free(p);
12278064Sume	if (res)
12378064Sume		freeaddrinfo(res);
12478064Sume	return -1;
12578064Sume}
12678064Sume
12778064Sumeconst char *
128122679Sumeprefix_string(const struct prefix *prefix)
12978064Sume{
13078064Sume	static char buf[NI_MAXHOST + 20];
13178064Sume	char hbuf[NI_MAXHOST];
13278064Sume
13395023Ssuz	if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len,
13495023Ssuz	    hbuf, sizeof(hbuf), NULL, 0, niflags))
13578064Sume		return NULL;
13678064Sume	snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
13778064Sume	return buf;
13878064Sume}
13978064Sume
14078064Sumeint
141122679Sumeprefix_match(const struct prefix *prefix, const struct sockaddr *sa)
14278064Sume{
14378064Sume	struct sockaddr_storage a, b;
14478064Sume	char *pa, *pb;
14578064Sume	int off, l;
14678064Sume
14778064Sume	if (prefix->a.ss_family != sa->sa_family ||
14878064Sume	    prefix->a.ss_len != sa->sa_len)
14978064Sume		return 0;
15078064Sume
15178064Sume	if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
15278064Sume		return 0;
15378064Sume
15478064Sume	switch (prefix->a.ss_family) {
15578064Sume	case AF_INET:
15678064Sume		off = offsetof(struct sockaddr_in, sin_addr);
15778064Sume		break;
15878064Sume	case AF_INET6:
15978064Sume		off = offsetof(struct sockaddr_in6, sin6_addr);
16078064Sume		break;
16178064Sume	default:
16278064Sume		if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
16378064Sume			return 0;
16478064Sume		else
16578064Sume			return 1;
16678064Sume	}
16778064Sume
16878064Sume	memcpy(&a, &prefix->a, prefix->a.ss_len);
16978064Sume	memcpy(&b, sa, sa->sa_len);
17078064Sume	l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
17178064Sume
17278064Sume	/* overrun check */
17378064Sume	if (off + l > a.ss_len)
17478064Sume		return 0;
17578064Sume
17678064Sume	pa = ((char *)&a) + off;
17778064Sume	pb = ((char *)&b) + off;
17878064Sume	if (prefix->l % 8) {
17978064Sume		pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
18078064Sume		pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
18178064Sume	}
18278064Sume	if (memcmp(pa, pb, l) != 0)
18378064Sume		return 0;
18478064Sume	else
18578064Sume		return 1;
18678064Sume}
18778064Sume
18878064Sume/*
18978064Sume * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
19078064Sume * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
19178064Sume */
19278064Sumestatic struct config *
193122679Sumeconfig_load1(const char *line)
19478064Sume{
19578064Sume	struct config *conf;
19678064Sume	char buf[BUFSIZ];
19778064Sume	char *p;
19878064Sume	char *token[4];
19978064Sume	int i;
20078064Sume
20178064Sume	if (strlen(line) + 1 > sizeof(buf))
20278064Sume		return NULL;
20378064Sume	strlcpy(buf, line, sizeof(buf));
20478064Sume
20578064Sume	p = strchr(buf, '\n');
20678064Sume	if (!p)
20778064Sume		return NULL;
20878064Sume	*p = '\0';
20978064Sume	p = strchr(buf, '#');
21078064Sume	if (p)
21178064Sume		*p = '\0';
21278064Sume	if (strlen(buf) == 0)
21378064Sume		return NULL;
21478064Sume
21578064Sume	p = buf;
21678064Sume	memset(token, 0, sizeof(token));
21778064Sume	for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
21878064Sume		token[i] = strtok(p, "\t ");
21978064Sume		p = NULL;
22078064Sume		if (token[i] == NULL)
22178064Sume			break;
22278064Sume	}
22378064Sume	/* extra tokens? */
22478064Sume	if (strtok(p, "\t ") != NULL)
22578064Sume		return NULL;
22678064Sume	/* insufficient tokens */
22778064Sume	switch (i) {
22878064Sume	case 3:
22978064Sume	case 4:
23078064Sume		break;
23178064Sume	default:
23278064Sume		return NULL;
23378064Sume	}
23478064Sume
23578064Sume	conf = (struct config *)malloc(sizeof(*conf));
23678064Sume	if (conf == NULL)
23778064Sume		return NULL;
23878064Sume	memset(conf, 0, sizeof(*conf));
23978064Sume
24078064Sume	if (strcasecmp(token[1], "permit") == 0)
24178064Sume		conf->permit = 1;
24278064Sume	else if (strcasecmp(token[1], "deny") == 0)
24378064Sume		conf->permit = 0;
24478064Sume	else {
24578064Sume		/* invalid keyword is considered as "deny" */
24678064Sume		conf->permit = 0;
24778064Sume	}
24878064Sume
24978064Sume	if (prefix_set(token[0], &conf->match, 1) < 0)
25078064Sume		goto fail;
25178064Sume	if (prefix_set(token[2], &conf->dest, 1) < 0)
25278064Sume		goto fail;
25378064Sume	if (token[3]) {
25478064Sume		if (prefix_set(token[3], &conf->src, 0) < 0)
25578064Sume			goto fail;
25678064Sume	}
25778064Sume
25878064Sume	return conf;
25978064Sume
26078064Sumefail:
26178064Sume	free(conf);
26278064Sume	return NULL;
26378064Sume}
26478064Sume
26578064Sumeint
266122679Sumeconfig_load(const char *configfile)
26778064Sume{
26878064Sume	FILE *fp;
26978064Sume	char buf[BUFSIZ];
27078064Sume	struct config *conf, *p;
27178064Sume	struct config sentinel;
27278064Sume
27378064Sume	config_list = NULL;
27478064Sume
27578064Sume	if (!configfile)
27678064Sume		configfile = _PATH_PREFIX_CONF;
27778064Sume	fp = fopen(configfile, "r");
27878064Sume	if (fp == NULL)
27978064Sume		return -1;
28078064Sume
28178064Sume	p = &sentinel;
282122679Sume	sentinel.next = NULL;
28378064Sume	while (fgets(buf, sizeof(buf), fp) != NULL) {
28478064Sume		conf = config_load1(buf);
28578064Sume		if (conf) {
28678064Sume			p->next = conf;
28778064Sume			p = p->next;
28878064Sume		}
28978064Sume	}
29078064Sume	config_list = sentinel.next;
29178064Sume
29278064Sume	fclose(fp);
29378064Sume	return 0;
29478064Sume}
29578064Sume
29678064Sume#if 0
29778064Sumestatic void
298122679Sumeconfig_show1(const struct config *conf)
29978064Sume{
30078064Sume	const char *p;
30178064Sume
30278064Sume	p = prefix_string(&conf->match);
30378064Sume	printf("%s", p ? p : "?");
30478064Sume
30578064Sume	if (conf->permit)
30678064Sume		printf(" permit");
30778064Sume	else
30878064Sume		printf(" deny");
30978064Sume
31078064Sume	p = prefix_string(&conf->dest);
31178064Sume	printf(" %s", p ? p : "?");
31278064Sume
31378064Sume	printf("\n");
31478064Sume}
31578064Sume
31678064Sumestatic void
31778064Sumeconfig_show()
31878064Sume{
31978064Sume	struct config *conf;
32078064Sume
32178064Sume	for (conf = config_list; conf; conf = conf->next)
32278064Sume		config_show1(conf);
32378064Sume}
32478064Sume#endif
32578064Sume
32678064Sumeconst struct config *
327122679Sumeconfig_match(struct sockaddr *sa1, struct sockaddr *sa2)
32878064Sume{
32978064Sume	static struct config conf;
33078064Sume	const struct config *p;
33178064Sume
33278064Sume	if (sa1->sa_len > sizeof(conf.match.a) ||
33378064Sume	    sa2->sa_len > sizeof(conf.dest.a))
33478064Sume		return NULL;
33578064Sume
33678064Sume	memset(&conf, 0, sizeof(conf));
33778064Sume	if (!config_list) {
33878064Sume		conf.permit = 1;
33978064Sume		memcpy(&conf.match.a, sa1, sa1->sa_len);
34078064Sume		memcpy(&conf.dest.a, sa2, sa2->sa_len);
34178064Sume		return &conf;
34278064Sume	}
34378064Sume
34478064Sume	for (p = config_list; p; p = p->next)
34578064Sume		if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
34678064Sume			return p;
34778064Sume
34878064Sume	return NULL;
34978064Sume}
350