1/* $NetBSD: prefix.c,v 1.7 2009/04/19 06:09:42 lukem 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 50static int prefix_set __P((const char *, struct prefix *, int)); 51static struct config *config_load1 __P((const char *)); 52#if 0 53static void config_show1 __P((const struct config *)); 54static void config_show __P((void)); 55#endif 56 57struct config *config_list = NULL; 58const int niflags = NI_NUMERICHOST; 59 60static int 61prefix_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 115fail: 116 if (p) 117 free(p); 118 if (res) 119 freeaddrinfo(res); 120 return -1; 121} 122 123const char * 124prefix_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 136int 137prefix_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 */ 188static struct config * 189config_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 256fail: 257 free(conf); 258 return NULL; 259} 260 261int 262config_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 293static void 294config_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 312static void 313config_show() 314{ 315 struct config *conf; 316 317 for (conf = config_list; conf; conf = conf->next) 318 config_show1(conf); 319} 320#endif 321 322const struct config * 323config_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