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
6778064Sume	p = strdup(s);
68122679Sume	if (!p)
69122679Sume		goto fail;
7078064Sume	q = strchr(p, '/');
7178064Sume	if (q) {
7278064Sume		if (!slash)
7378064Sume			goto fail;
7478064Sume		*q++ = '\0';
7578064Sume	}
7678064Sume
7778064Sume	memset(&hints, 0, sizeof(hints));
7878064Sume	hints.ai_family = PF_UNSPEC;
7978064Sume	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
8078064Sume	hints.ai_flags = AI_NUMERICHOST;
8178064Sume	if (getaddrinfo(p, "0", &hints, &res))
8278064Sume		goto fail;
8378064Sume	if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
8478064Sume		goto fail;
8578064Sume	memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
8678064Sume
8778064Sume	switch (prefix->a.ss_family) {
8878064Sume	case AF_INET:
8978064Sume		max = 32;
9078064Sume		break;
9178064Sume	case AF_INET6:
9278064Sume		max = 128;
9378064Sume		break;
9478064Sume	default:
9578064Sume		max = -1;
9678064Sume		break;
9778064Sume	}
9878064Sume
9978064Sume	if (q) {
10078064Sume		r = NULL;
10178064Sume		prefix->l = (int)strtoul(q, &r, 10);
10278064Sume		if (!*q || *r)
10378064Sume			goto fail;
10478064Sume		if (prefix->l < 0 || prefix->l > max)
10578064Sume			goto fail;
10678064Sume	} else
10778064Sume		prefix->l = max;
10878064Sume
10978064Sume	if (p)
11078064Sume		free(p);
11178064Sume	if (res)
11278064Sume		freeaddrinfo(res);
11378064Sume	return 0;
11478064Sume
11578064Sumefail:
11678064Sume	if (p)
11778064Sume		free(p);
11878064Sume	if (res)
11978064Sume		freeaddrinfo(res);
12078064Sume	return -1;
12178064Sume}
12278064Sume
12378064Sumeconst char *
124122679Sumeprefix_string(const struct prefix *prefix)
12578064Sume{
12678064Sume	static char buf[NI_MAXHOST + 20];
12778064Sume	char hbuf[NI_MAXHOST];
12878064Sume
12995023Ssuz	if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len,
13095023Ssuz	    hbuf, sizeof(hbuf), NULL, 0, niflags))
13178064Sume		return NULL;
13278064Sume	snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
13378064Sume	return buf;
13478064Sume}
13578064Sume
13678064Sumeint
137122679Sumeprefix_match(const struct prefix *prefix, const struct sockaddr *sa)
13878064Sume{
13978064Sume	struct sockaddr_storage a, b;
14078064Sume	char *pa, *pb;
14178064Sume	int off, l;
14278064Sume
14378064Sume	if (prefix->a.ss_family != sa->sa_family ||
14478064Sume	    prefix->a.ss_len != sa->sa_len)
14578064Sume		return 0;
14678064Sume
14778064Sume	if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
14878064Sume		return 0;
14978064Sume
15078064Sume	switch (prefix->a.ss_family) {
15178064Sume	case AF_INET:
15278064Sume		off = offsetof(struct sockaddr_in, sin_addr);
15378064Sume		break;
15478064Sume	case AF_INET6:
15578064Sume		off = offsetof(struct sockaddr_in6, sin6_addr);
15678064Sume		break;
15778064Sume	default:
15878064Sume		if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
15978064Sume			return 0;
16078064Sume		else
16178064Sume			return 1;
16278064Sume	}
16378064Sume
16478064Sume	memcpy(&a, &prefix->a, prefix->a.ss_len);
16578064Sume	memcpy(&b, sa, sa->sa_len);
16678064Sume	l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
16778064Sume
16878064Sume	/* overrun check */
16978064Sume	if (off + l > a.ss_len)
17078064Sume		return 0;
17178064Sume
17278064Sume	pa = ((char *)&a) + off;
17378064Sume	pb = ((char *)&b) + off;
17478064Sume	if (prefix->l % 8) {
17578064Sume		pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
17678064Sume		pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
17778064Sume	}
17878064Sume	if (memcmp(pa, pb, l) != 0)
17978064Sume		return 0;
18078064Sume	else
18178064Sume		return 1;
18278064Sume}
18378064Sume
18478064Sume/*
18578064Sume * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
18678064Sume * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
18778064Sume */
18878064Sumestatic struct config *
189122679Sumeconfig_load1(const char *line)
19078064Sume{
19178064Sume	struct config *conf;
19278064Sume	char buf[BUFSIZ];
19378064Sume	char *p;
19478064Sume	char *token[4];
19578064Sume	int i;
19678064Sume
19778064Sume	if (strlen(line) + 1 > sizeof(buf))
19878064Sume		return NULL;
19978064Sume	strlcpy(buf, line, sizeof(buf));
20078064Sume
20178064Sume	p = strchr(buf, '\n');
20278064Sume	if (!p)
20378064Sume		return NULL;
20478064Sume	*p = '\0';
20578064Sume	p = strchr(buf, '#');
20678064Sume	if (p)
20778064Sume		*p = '\0';
20878064Sume	if (strlen(buf) == 0)
20978064Sume		return NULL;
21078064Sume
21178064Sume	p = buf;
21278064Sume	memset(token, 0, sizeof(token));
21378064Sume	for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
21478064Sume		token[i] = strtok(p, "\t ");
21578064Sume		p = NULL;
21678064Sume		if (token[i] == NULL)
21778064Sume			break;
21878064Sume	}
21978064Sume	/* extra tokens? */
22078064Sume	if (strtok(p, "\t ") != NULL)
22178064Sume		return NULL;
22278064Sume	/* insufficient tokens */
22378064Sume	switch (i) {
22478064Sume	case 3:
22578064Sume	case 4:
22678064Sume		break;
22778064Sume	default:
22878064Sume		return NULL;
22978064Sume	}
23078064Sume
23178064Sume	conf = (struct config *)malloc(sizeof(*conf));
23278064Sume	if (conf == NULL)
23378064Sume		return NULL;
23478064Sume	memset(conf, 0, sizeof(*conf));
23578064Sume
23678064Sume	if (strcasecmp(token[1], "permit") == 0)
23778064Sume		conf->permit = 1;
23878064Sume	else if (strcasecmp(token[1], "deny") == 0)
23978064Sume		conf->permit = 0;
24078064Sume	else {
24178064Sume		/* invalid keyword is considered as "deny" */
24278064Sume		conf->permit = 0;
24378064Sume	}
24478064Sume
24578064Sume	if (prefix_set(token[0], &conf->match, 1) < 0)
24678064Sume		goto fail;
24778064Sume	if (prefix_set(token[2], &conf->dest, 1) < 0)
24878064Sume		goto fail;
24978064Sume	if (token[3]) {
25078064Sume		if (prefix_set(token[3], &conf->src, 0) < 0)
25178064Sume			goto fail;
25278064Sume	}
25378064Sume
25478064Sume	return conf;
25578064Sume
25678064Sumefail:
25778064Sume	free(conf);
25878064Sume	return NULL;
25978064Sume}
26078064Sume
26178064Sumeint
262122679Sumeconfig_load(const char *configfile)
26378064Sume{
26478064Sume	FILE *fp;
26578064Sume	char buf[BUFSIZ];
26678064Sume	struct config *conf, *p;
26778064Sume	struct config sentinel;
26878064Sume
26978064Sume	config_list = NULL;
27078064Sume
27178064Sume	if (!configfile)
27278064Sume		configfile = _PATH_PREFIX_CONF;
27378064Sume	fp = fopen(configfile, "r");
27478064Sume	if (fp == NULL)
27578064Sume		return -1;
27678064Sume
27778064Sume	p = &sentinel;
278122679Sume	sentinel.next = NULL;
27978064Sume	while (fgets(buf, sizeof(buf), fp) != NULL) {
28078064Sume		conf = config_load1(buf);
28178064Sume		if (conf) {
28278064Sume			p->next = conf;
28378064Sume			p = p->next;
28478064Sume		}
28578064Sume	}
28678064Sume	config_list = sentinel.next;
28778064Sume
28878064Sume	fclose(fp);
28978064Sume	return 0;
29078064Sume}
29178064Sume
29278064Sume#if 0
29378064Sumestatic void
294122679Sumeconfig_show1(const struct config *conf)
29578064Sume{
29678064Sume	const char *p;
29778064Sume
29878064Sume	p = prefix_string(&conf->match);
29978064Sume	printf("%s", p ? p : "?");
30078064Sume
30178064Sume	if (conf->permit)
30278064Sume		printf(" permit");
30378064Sume	else
30478064Sume		printf(" deny");
30578064Sume
30678064Sume	p = prefix_string(&conf->dest);
30778064Sume	printf(" %s", p ? p : "?");
30878064Sume
30978064Sume	printf("\n");
31078064Sume}
31178064Sume
31278064Sumestatic void
31378064Sumeconfig_show()
31478064Sume{
31578064Sume	struct config *conf;
31678064Sume
31778064Sume	for (conf = config_list; conf; conf = conf->next)
31878064Sume		config_show1(conf);
31978064Sume}
32078064Sume#endif
32178064Sume
32278064Sumeconst struct config *
323122679Sumeconfig_match(struct sockaddr *sa1, struct sockaddr *sa2)
32478064Sume{
32578064Sume	static struct config conf;
32678064Sume	const struct config *p;
32778064Sume
32878064Sume	if (sa1->sa_len > sizeof(conf.match.a) ||
32978064Sume	    sa2->sa_len > sizeof(conf.dest.a))
33078064Sume		return NULL;
33178064Sume
33278064Sume	memset(&conf, 0, sizeof(conf));
33378064Sume	if (!config_list) {
33478064Sume		conf.permit = 1;
33578064Sume		memcpy(&conf.match.a, sa1, sa1->sa_len);
33678064Sume		memcpy(&conf.dest.a, sa2, sa2->sa_len);
33778064Sume		return &conf;
33878064Sume	}
33978064Sume
34078064Sume	for (p = config_list; p; p = p->next)
34178064Sume		if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
34278064Sume			return p;
34378064Sume
34478064Sume	return NULL;
34578064Sume}
346