/* $OpenBSD: prefix.c,v 1.2 2001/09/05 01:31:33 itojun Exp $ */ /* $KAME: prefix.c,v 1.9 2001/07/02 14:36:49 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #ifndef offsetof #define offsetof(type, member) ((size_t)(u_long)(&((type *)0)->member)) #endif #include "faithd.h" #include "prefix.h" static int prefix_set __P((const char *, struct prefix *, int)); static struct config *config_load1 __P((const char *)); #if 0 static void config_show1 __P((const struct config *)); static void config_show __P((void)); #endif struct config *config_list = NULL; #ifdef NI_WITHSCOPEID const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; #else const int niflags = NI_NUMERICHOST; #endif static int prefix_set(s, prefix, slash) const char *s; struct prefix *prefix; int slash; { char *p, *q, *r; struct addrinfo hints, *res = NULL; int max; char *a; p = strdup(s); q = strchr(p, '/'); if (q) { if (!slash) goto fail; *q++ = '\0'; } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(p, "0", &hints, &res)) goto fail; if (res->ai_next || res->ai_addrlen > sizeof(prefix->a)) goto fail; memcpy(&prefix->a, res->ai_addr, res->ai_addrlen); switch (prefix->a.ss_family) { case AF_INET: max = 32; a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr; break; case AF_INET6: max = 128; a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr; break; default: a = NULL; max = -1; break; } if (q) { r = NULL; prefix->l = (int)strtoul(q, &r, 10); if (!*q || *r) goto fail; if (prefix->l < 0 || prefix->l > max) goto fail; } else prefix->l = max; if (p) free(p); if (res) freeaddrinfo(res); return 0; fail: if (p) free(p); if (res) freeaddrinfo(res); return -1; } const char * prefix_string(prefix) const struct prefix *prefix; { static char buf[NI_MAXHOST + 20]; char hbuf[NI_MAXHOST]; if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len, hbuf, sizeof(hbuf), NULL, 0, niflags)) return NULL; snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l); return buf; } int prefix_match(prefix, sa) const struct prefix *prefix; const struct sockaddr *sa; { struct sockaddr_storage a, b; char *pa, *pb; int off, l; if (prefix->a.ss_family != sa->sa_family || prefix->a.ss_len != sa->sa_len) return 0; if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b)) return 0; switch (prefix->a.ss_family) { case AF_INET: off = offsetof(struct sockaddr_in, sin_addr); break; case AF_INET6: off = offsetof(struct sockaddr_in6, sin6_addr); break; default: if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0) return 0; else return 1; } memcpy(&a, &prefix->a, prefix->a.ss_len); memcpy(&b, sa, sa->sa_len); l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0); /* overrun check */ if (off + l > a.ss_len) return 0; pa = ((char *)&a) + off; pb = ((char *)&b) + off; if (prefix->l % 8) { pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); } if (memcmp(pa, pb, l) != 0) return 0; else return 1; } /* * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr] * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1 */ static struct config * config_load1(line) const char *line; { struct config *conf; char buf[BUFSIZ]; char *p; char *token[4]; int i; if (strlen(line) + 1 > sizeof(buf)) return NULL; strlcpy(buf, line, sizeof(buf)); p = strchr(buf, '\n'); if (!p) return NULL; *p = '\0'; p = strchr(buf, '#'); if (p) *p = '\0'; if (strlen(buf) == 0) return NULL; p = buf; memset(token, 0, sizeof(token)); for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) { token[i] = strtok(p, "\t "); p = NULL; if (token[i] == NULL) break; } /* extra tokens? */ if (strtok(p, "\t ") != NULL) return NULL; /* insufficient tokens */ switch (i) { case 3: case 4: break; default: return NULL; } conf = (struct config *)malloc(sizeof(*conf)); if (conf == NULL) return NULL; memset(conf, 0, sizeof(*conf)); if (strcasecmp(token[1], "permit") == 0) conf->permit = 1; else if (strcasecmp(token[1], "deny") == 0) conf->permit = 0; else { /* invalid keyword is considered as "deny" */ conf->permit = 0; } if (prefix_set(token[0], &conf->match, 1) < 0) goto fail; if (prefix_set(token[2], &conf->dest, 1) < 0) goto fail; if (token[3]) { if (prefix_set(token[3], &conf->src, 0) < 0) goto fail; } return conf; fail: free(conf); return NULL; } int config_load(configfile) const char *configfile; { FILE *fp; char buf[BUFSIZ]; struct config *conf, *p; struct config sentinel; config_list = NULL; if (!configfile) configfile = _PATH_PREFIX_CONF; fp = fopen(configfile, "r"); if (fp == NULL) return -1; p = &sentinel; while (fgets(buf, sizeof(buf), fp) != NULL) { conf = config_load1(buf); if (conf) { p->next = conf; p = p->next; } } config_list = sentinel.next; fclose(fp); return 0; } #if 0 static void config_show1(conf) const struct config *conf; { const char *p; p = prefix_string(&conf->match); printf("%s", p ? p : "?"); if (conf->permit) printf(" permit"); else printf(" deny"); p = prefix_string(&conf->dest); printf(" %s", p ? p : "?"); printf("\n"); } static void config_show() { struct config *conf; for (conf = config_list; conf; conf = conf->next) config_show1(conf); } #endif const struct config * config_match(sa1, sa2) struct sockaddr *sa1, *sa2; { static struct config conf; const struct config *p; if (sa1->sa_len > sizeof(conf.match.a) || sa2->sa_len > sizeof(conf.dest.a)) return NULL; memset(&conf, 0, sizeof(conf)); if (!config_list) { conf.permit = 1; memcpy(&conf.match.a, sa1, sa1->sa_len); memcpy(&conf.dest.a, sa2, sa2->sa_len); return &conf; } for (p = config_list; p; p = p->next) if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2)) return p; return NULL; }