1 /* $NetBSD: dict_cidr.c,v 1.1.1.1 2009/06/23 10:08:59 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_cidr 3 6 /* SUMMARY 7 /* Dictionary interface for CIDR data 8 /* SYNOPSIS 9 /* #include <dict_cidr.h> 10 /* 11 /* DICT *dict_cidr_open(name, open_flags, dict_flags) 12 /* const char *name; 13 /* int open_flags; 14 /* int dict_flags; 15 /* DESCRIPTION 16 /* dict_cidr_open() opens the named file and stores 17 /* the key/value pairs where the key must be either a 18 /* "naked" IP address or a netblock in CIDR notation. 19 /* SEE ALSO 20 /* dict(3) generic dictionary manager 21 /* AUTHOR(S) 22 /* Jozsef Kadlecsik 23 /* kadlec@blackhole.kfki.hu 24 /* KFKI Research Institute for Particle and Nuclear Physics 25 /* POB. 49 26 /* 1525 Budapest, Hungary 27 /* 28 /* Wietse Venema 29 /* IBM T.J. Watson Research 30 /* P.O. Box 704 31 /* Yorktown Heights, NY 10598, USA 32 /*--*/ 33 34 /* System library. */ 35 36 #include <sys_defs.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <string.h> 40 #include <ctype.h> 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 45 /* Utility library. */ 46 47 #include <mymalloc.h> 48 #include <msg.h> 49 #include <vstream.h> 50 #include <vstring.h> 51 #include <stringops.h> 52 #include <readlline.h> 53 #include <dict.h> 54 #include <myaddrinfo.h> 55 #include <cidr_match.h> 56 #include <dict_cidr.h> 57 58 /* Application-specific. */ 59 60 /* 61 * Each rule in a CIDR table is parsed and stored in a linked list. 62 */ 63 typedef struct DICT_CIDR_ENTRY { 64 CIDR_MATCH cidr_info; /* must be first */ 65 char *value; /* lookup result */ 66 } DICT_CIDR_ENTRY; 67 68 typedef struct { 69 DICT dict; /* generic members */ 70 DICT_CIDR_ENTRY *head; /* first entry */ 71 } DICT_CIDR; 72 73 /* dict_cidr_lookup - CIDR table lookup */ 74 75 static const char *dict_cidr_lookup(DICT *dict, const char *key) 76 { 77 DICT_CIDR *dict_cidr = (DICT_CIDR *) dict; 78 DICT_CIDR_ENTRY *entry; 79 80 if (msg_verbose) 81 msg_info("dict_cidr_lookup: %s: %s", dict->name, key); 82 83 dict_errno = 0; 84 85 if ((entry = (DICT_CIDR_ENTRY *) 86 cidr_match_execute(&(dict_cidr->head->cidr_info), key)) != 0) 87 return (entry->value); 88 return (0); 89 } 90 91 /* dict_cidr_close - close the CIDR table */ 92 93 static void dict_cidr_close(DICT *dict) 94 { 95 DICT_CIDR *dict_cidr = (DICT_CIDR *) dict; 96 DICT_CIDR_ENTRY *entry; 97 DICT_CIDR_ENTRY *next; 98 99 for (entry = dict_cidr->head; entry; entry = next) { 100 next = (DICT_CIDR_ENTRY *) entry->cidr_info.next; 101 myfree(entry->value); 102 myfree((char *) entry); 103 } 104 dict_free(dict); 105 } 106 107 /* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */ 108 109 static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why) 110 { 111 DICT_CIDR_ENTRY *rule; 112 char *pattern; 113 char *value; 114 CIDR_MATCH cidr_info; 115 MAI_HOSTADDR_STR hostaddr; 116 117 /* 118 * Split the rule into key and value. We already eliminated leading 119 * whitespace, comments, empty lines or lines with whitespace only. This 120 * means a null key can't happen but we will handle this anyway. 121 */ 122 pattern = p; 123 while (*p && !ISSPACE(*p)) /* Skip over key */ 124 p++; 125 if (*p) /* Terminate key */ 126 *p++ = 0; 127 while (*p && ISSPACE(*p)) /* Skip whitespace */ 128 p++; 129 value = p; 130 trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ 131 if (*pattern == 0) { 132 vstring_sprintf(why, "no address pattern"); 133 return (0); 134 } 135 if (*value == 0) { 136 vstring_sprintf(why, "no lookup result"); 137 return (0); 138 } 139 140 /* 141 * Parse the pattern, destroying it in the process. 142 */ 143 if (cidr_match_parse(&cidr_info, pattern, why) != 0) 144 return (0); 145 146 /* 147 * Bundle up the result. 148 */ 149 rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); 150 rule->cidr_info = cidr_info; 151 rule->value = mystrdup(value); 152 153 if (msg_verbose) { 154 if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, 155 hostaddr.buf, sizeof(hostaddr.buf)) == 0) 156 msg_fatal("inet_ntop: %m"); 157 msg_info("dict_cidr_open: add %s/%d %s", 158 hostaddr.buf, cidr_info.mask_shift, rule->value); 159 } 160 return (rule); 161 } 162 163 /* dict_cidr_open - parse CIDR table */ 164 165 DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags) 166 { 167 DICT_CIDR *dict_cidr; 168 VSTREAM *map_fp; 169 VSTRING *line_buffer = vstring_alloc(100); 170 VSTRING *why = vstring_alloc(100); 171 DICT_CIDR_ENTRY *rule; 172 DICT_CIDR_ENTRY *last_rule = 0; 173 int lineno = 0; 174 175 /* 176 * Sanity checks. 177 */ 178 if (open_flags != O_RDONLY) 179 msg_fatal("%s:%s map requires O_RDONLY access mode", 180 DICT_TYPE_CIDR, mapname); 181 182 /* 183 * XXX Eliminate unnecessary queries by setting a flag that says "this 184 * map matches network addresses only". 185 */ 186 dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname, 187 sizeof(*dict_cidr)); 188 dict_cidr->dict.lookup = dict_cidr_lookup; 189 dict_cidr->dict.close = dict_cidr_close; 190 dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN; 191 dict_cidr->head = 0; 192 193 if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0) 194 msg_fatal("open %s: %m", mapname); 195 196 while (readlline(line_buffer, map_fp, &lineno)) { 197 rule = dict_cidr_parse_rule(vstring_str(line_buffer), why); 198 if (rule == 0) { 199 msg_warn("cidr map %s, line %d: %s: skipping this rule", 200 mapname, lineno, vstring_str(why)); 201 continue; 202 } 203 if (last_rule == 0) 204 dict_cidr->head = rule; 205 else 206 last_rule->cidr_info.next = &(rule->cidr_info); 207 last_rule = rule; 208 } 209 210 /* 211 * Clean up. 212 */ 213 if (vstream_fclose(map_fp)) 214 msg_fatal("cidr map %s: read error: %m", mapname); 215 vstring_free(line_buffer); 216 vstring_free(why); 217 218 return (DICT_DEBUG (&dict_cidr->dict)); 219 } 220