1 /* $NetBSD: dict_cidr.c,v 1.1.1.3 2014/07/06 19:27:57 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 <sys/stat.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <ctype.h> 42 #include <sys/socket.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 46 /* Utility library. */ 47 48 #include <mymalloc.h> 49 #include <msg.h> 50 #include <vstream.h> 51 #include <vstring.h> 52 #include <stringops.h> 53 #include <readlline.h> 54 #include <dict.h> 55 #include <myaddrinfo.h> 56 #include <cidr_match.h> 57 #include <dict_cidr.h> 58 #include <warn_stat.h> 59 60 /* Application-specific. */ 61 62 /* 63 * Each rule in a CIDR table is parsed and stored in a linked list. 64 */ 65 typedef struct DICT_CIDR_ENTRY { 66 CIDR_MATCH cidr_info; /* must be first */ 67 char *value; /* lookup result */ 68 } DICT_CIDR_ENTRY; 69 70 typedef struct { 71 DICT dict; /* generic members */ 72 DICT_CIDR_ENTRY *head; /* first entry */ 73 } DICT_CIDR; 74 75 /* dict_cidr_lookup - CIDR table lookup */ 76 77 static const char *dict_cidr_lookup(DICT *dict, const char *key) 78 { 79 DICT_CIDR *dict_cidr = (DICT_CIDR *) dict; 80 DICT_CIDR_ENTRY *entry; 81 82 if (msg_verbose) 83 msg_info("dict_cidr_lookup: %s: %s", dict->name, key); 84 85 dict->error = 0; 86 87 if ((entry = (DICT_CIDR_ENTRY *) 88 cidr_match_execute(&(dict_cidr->head->cidr_info), key)) != 0) 89 return (entry->value); 90 return (0); 91 } 92 93 /* dict_cidr_close - close the CIDR table */ 94 95 static void dict_cidr_close(DICT *dict) 96 { 97 DICT_CIDR *dict_cidr = (DICT_CIDR *) dict; 98 DICT_CIDR_ENTRY *entry; 99 DICT_CIDR_ENTRY *next; 100 101 for (entry = dict_cidr->head; entry; entry = next) { 102 next = (DICT_CIDR_ENTRY *) entry->cidr_info.next; 103 myfree(entry->value); 104 myfree((char *) entry); 105 } 106 dict_free(dict); 107 } 108 109 /* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */ 110 111 static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why) 112 { 113 DICT_CIDR_ENTRY *rule; 114 char *pattern; 115 char *value; 116 CIDR_MATCH cidr_info; 117 MAI_HOSTADDR_STR hostaddr; 118 119 /* 120 * Split the rule into key and value. We already eliminated leading 121 * whitespace, comments, empty lines or lines with whitespace only. This 122 * means a null key can't happen but we will handle this anyway. 123 */ 124 pattern = p; 125 while (*p && !ISSPACE(*p)) /* Skip over key */ 126 p++; 127 if (*p) /* Terminate key */ 128 *p++ = 0; 129 while (*p && ISSPACE(*p)) /* Skip whitespace */ 130 p++; 131 value = p; 132 trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ 133 if (*pattern == 0) { 134 vstring_sprintf(why, "no address pattern"); 135 return (0); 136 } 137 if (*value == 0) { 138 vstring_sprintf(why, "no lookup result"); 139 return (0); 140 } 141 142 /* 143 * Parse the pattern, destroying it in the process. 144 */ 145 if (cidr_match_parse(&cidr_info, pattern, why) != 0) 146 return (0); 147 148 /* 149 * Bundle up the result. 150 */ 151 rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); 152 rule->cidr_info = cidr_info; 153 rule->value = mystrdup(value); 154 155 if (msg_verbose) { 156 if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, 157 hostaddr.buf, sizeof(hostaddr.buf)) == 0) 158 msg_fatal("inet_ntop: %m"); 159 msg_info("dict_cidr_open: add %s/%d %s", 160 hostaddr.buf, cidr_info.mask_shift, rule->value); 161 } 162 return (rule); 163 } 164 165 /* dict_cidr_open - parse CIDR table */ 166 167 DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags) 168 { 169 DICT_CIDR *dict_cidr; 170 VSTREAM *map_fp = 0; 171 struct stat st; 172 VSTRING *line_buffer = 0; 173 VSTRING *why = 0; 174 DICT_CIDR_ENTRY *rule; 175 DICT_CIDR_ENTRY *last_rule = 0; 176 int lineno = 0; 177 178 /* 179 * Let the optimizer worry about eliminating redundant code. 180 */ 181 #define DICT_CIDR_OPEN_RETURN(d) do { \ 182 DICT *__d = (d); \ 183 if (map_fp != 0 && vstream_fclose(map_fp)) \ 184 msg_fatal("cidr map %s: read error: %m", mapname); \ 185 if (line_buffer != 0) \ 186 vstring_free(line_buffer); \ 187 if (why != 0) \ 188 vstring_free(why); \ 189 return (__d); \ 190 } while (0) 191 192 /* 193 * Sanity checks. 194 */ 195 if (open_flags != O_RDONLY) 196 DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname, 197 open_flags, dict_flags, 198 "%s:%s map requires O_RDONLY access mode", 199 DICT_TYPE_CIDR, mapname)); 200 201 /* 202 * Open the configuration file. 203 */ 204 if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0) 205 DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname, 206 open_flags, dict_flags, 207 "open %s: %m", mapname)); 208 if (fstat(vstream_fileno(map_fp), &st) < 0) 209 msg_fatal("fstat %s: %m", mapname); 210 211 line_buffer = vstring_alloc(100); 212 why = vstring_alloc(100); 213 214 /* 215 * XXX Eliminate unnecessary queries by setting a flag that says "this 216 * map matches network addresses only". 217 */ 218 dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname, 219 sizeof(*dict_cidr)); 220 dict_cidr->dict.lookup = dict_cidr_lookup; 221 dict_cidr->dict.close = dict_cidr_close; 222 dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN; 223 dict_cidr->head = 0; 224 225 dict_cidr->dict.owner.uid = st.st_uid; 226 dict_cidr->dict.owner.status = (st.st_uid != 0); 227 228 while (readlline(line_buffer, map_fp, &lineno)) { 229 rule = dict_cidr_parse_rule(vstring_str(line_buffer), why); 230 if (rule == 0) { 231 msg_warn("cidr map %s, line %d: %s: skipping this rule", 232 mapname, lineno, vstring_str(why)); 233 continue; 234 } 235 if (last_rule == 0) 236 dict_cidr->head = rule; 237 else 238 last_rule->cidr_info.next = &(rule->cidr_info); 239 last_rule = rule; 240 } 241 242 DICT_CIDR_OPEN_RETURN(DICT_DEBUG (&dict_cidr->dict)); 243 } 244