1 /* $OpenBSD: getnetnamadr_async.c,v 1.2 2012/07/10 17:30:38 eric Exp $ */ 2 /* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 20 #include <netinet/in.h> 21 #include <arpa/inet.h> 22 #include <arpa/nameser.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "asr.h" 31 #include "asr_private.h" 32 33 #define MAXALIASES 16 34 35 ssize_t addr_as_fqdn(const char *, int, char *, size_t); 36 37 static int getnetnamadr_async_run(struct async *, struct async_res *); 38 static struct netent *netent_alloc(int); 39 static int netent_set_cname(struct netent *, const char *, int); 40 static int netent_add_alias(struct netent *, const char *, int); 41 static struct netent *netent_file_match(FILE *, int, const char *); 42 static struct netent *netent_from_packet(int, char *, size_t); 43 44 struct async * 45 getnetbyname_async(const char *name, struct asr *asr) 46 { 47 struct asr_ctx *ac; 48 struct async *as; 49 50 /* The current resolver segfaults. */ 51 if (name == NULL) { 52 errno = EINVAL; 53 return (NULL); 54 } 55 56 ac = asr_use_resolver(asr); 57 if ((as = async_new(ac, ASR_GETNETBYNAME)) == NULL) 58 goto abort; /* errno set */ 59 as->as_run = getnetnamadr_async_run; 60 61 as->as.netnamadr.family = AF_INET; 62 as->as.netnamadr.name = strdup(name); 63 if (as->as.netnamadr.name == NULL) 64 goto abort; /* errno set */ 65 66 asr_ctx_unref(ac); 67 return (as); 68 69 abort: 70 if (as) 71 async_free(as); 72 asr_ctx_unref(ac); 73 return (NULL); 74 } 75 76 struct async * 77 getnetbyaddr_async(in_addr_t net, int family, struct asr *asr) 78 { 79 struct asr_ctx *ac; 80 struct async *as; 81 82 ac = asr_use_resolver(asr); 83 if ((as = async_new(ac, ASR_GETNETBYADDR)) == NULL) 84 goto abort; /* errno set */ 85 as->as_run = getnetnamadr_async_run; 86 87 as->as.netnamadr.family = family; 88 as->as.netnamadr.addr = net; 89 90 asr_ctx_unref(ac); 91 return (as); 92 93 abort: 94 if (as) 95 async_free(as); 96 asr_ctx_unref(ac); 97 return (NULL); 98 } 99 100 static int 101 getnetnamadr_async_run(struct async *as, struct async_res *ar) 102 { 103 int i, n, r, type; 104 FILE *f; 105 char dname[MAXDNAME], *name, *data; 106 in_addr_t in; 107 108 next: 109 switch(as->as_state) { 110 111 case ASR_STATE_INIT: 112 113 if (as->as.netnamadr.family != AF_INET) { 114 ar->ar_h_errno = NETDB_INTERNAL; 115 ar->ar_errno = EAFNOSUPPORT; 116 async_set_state(as, ASR_STATE_HALT); 117 break; 118 } 119 120 async_set_state(as, ASR_STATE_NEXT_DB); 121 break; 122 123 case ASR_STATE_NEXT_DB: 124 125 if (asr_iter_db(as) == -1) { 126 async_set_state(as, ASR_STATE_NOT_FOUND); 127 break; 128 } 129 130 switch(AS_DB(as)) { 131 case ASR_DB_DNS: 132 133 if (as->as_type == ASR_GETNETBYNAME) { 134 type = T_A; 135 /* 136 * I think we want to do the former, but our 137 * resolver is doing the following, so let's 138 * preserve bugward-compatibility there. 139 */ 140 type = T_PTR; 141 name = as->as.netnamadr.name; 142 as->as.netnamadr.subq = res_search_async_ctx( 143 name, C_IN, type, NULL, 0, as->as_ctx); 144 } else { 145 type = T_PTR; 146 name = dname; 147 148 in = htonl(as->as.netnamadr.addr); 149 addr_as_fqdn((char*)&in, 150 as->as.netnamadr.family, 151 dname, sizeof(dname)); 152 as->as.netnamadr.subq = res_query_async_ctx( 153 name, C_IN, type, NULL, 0, as->as_ctx); 154 } 155 156 if (as->as.netnamadr.subq == NULL) { 157 ar->ar_errno = errno; 158 ar->ar_h_errno = NETDB_INTERNAL; 159 async_set_state(as, ASR_STATE_HALT); 160 } 161 async_set_state(as, ASR_STATE_SUBQUERY); 162 break; 163 164 case ASR_DB_FILE: 165 166 if ((f = fopen("/etc/networks", "r")) == NULL) 167 break; 168 169 if (as->as_type == ASR_GETNETBYNAME) 170 data = as->as.netnamadr.name; 171 else 172 data = (void*)&as->as.netnamadr.addr; 173 174 ar->ar_netent = netent_file_match(f, as->as_type, data); 175 fclose(f); 176 177 if (ar->ar_netent == NULL) { 178 if (errno) { 179 ar->ar_errno = errno; 180 ar->ar_h_errno = NETDB_INTERNAL; 181 async_set_state(as, ASR_STATE_HALT); 182 } 183 /* otherwise not found */ 184 break; 185 } 186 187 ar->ar_h_errno = NETDB_SUCCESS; 188 async_set_state(as, ASR_STATE_HALT); 189 break; 190 } 191 break; 192 193 case ASR_STATE_SUBQUERY: 194 195 if ((r = async_run(as->as.netnamadr.subq, ar)) == ASYNC_COND) 196 return (ASYNC_COND); 197 as->as.netnamadr.subq = NULL; 198 199 if (ar->ar_datalen == -1) { 200 async_set_state(as, ASR_STATE_NEXT_DB); 201 break; 202 } 203 204 /* Got packet, but no answer */ 205 if (ar->ar_count == 0) { 206 free(ar->ar_data); 207 async_set_state(as, ASR_STATE_NEXT_DB); 208 break; 209 } 210 211 ar->ar_netent = netent_from_packet(as->as_type, ar->ar_data, 212 ar->ar_datalen); 213 free(ar->ar_data); 214 215 if (ar->ar_netent == NULL) { 216 ar->ar_errno = errno; 217 ar->ar_h_errno = NETDB_INTERNAL; 218 async_set_state(as, ASR_STATE_HALT); 219 break; 220 } 221 222 if (as->as_type == ASR_GETNETBYADDR) 223 ar->ar_netent->n_net = as->as.netnamadr.addr; 224 225 /* 226 * No address found in the dns packet. The blocking version 227 * reports this as an error. 228 */ 229 if (as->as_type == ASR_GETNETBYNAME && 230 ar->ar_netent->n_net == 0) { 231 /* XXX wrong */ 232 freenetent(ar->ar_netent); 233 async_set_state(as, ASR_STATE_NEXT_DB); 234 } else { 235 ar->ar_h_errno = NETDB_SUCCESS; 236 async_set_state(as, ASR_STATE_HALT); 237 } 238 break; 239 240 case ASR_STATE_NOT_FOUND: 241 242 ar->ar_errno = 0; 243 ar->ar_h_errno = HOST_NOT_FOUND; 244 async_set_state(as, ASR_STATE_HALT); 245 break; 246 247 case ASR_STATE_HALT: 248 249 if (ar->ar_h_errno) 250 ar->ar_netent = NULL; 251 else 252 ar->ar_errno = 0; 253 return (ASYNC_DONE); 254 255 default: 256 ar->ar_errno = EOPNOTSUPP; 257 ar->ar_h_errno = NETDB_INTERNAL; 258 ar->ar_gai_errno = EAI_SYSTEM; 259 async_set_state(as, ASR_STATE_HALT); 260 break; 261 } 262 goto next; 263 } 264 265 static struct netent * 266 netent_file_match(FILE *f, int reqtype, const char *data) 267 { 268 struct netent *e; 269 char *tokens[MAXTOKEN]; 270 int n, i; 271 in_addr_t net; 272 273 for(;;) { 274 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 275 if (n == -1) { 276 errno = 0; /* ignore errors reading the file */ 277 return (NULL); 278 } 279 280 if (reqtype == ASR_GETNETBYADDR) { 281 net = inet_network(tokens[1]); 282 if (memcmp(&net, data, sizeof net) == 0) 283 goto found; 284 } else { 285 for (i = 0; i < n; i++) { 286 if (i == 1) 287 continue; 288 if (strcasecmp(data, tokens[i])) 289 continue; 290 goto found; 291 } 292 } 293 } 294 295 found: 296 if ((e = netent_alloc(AF_INET)) == NULL) 297 return (NULL); 298 if (netent_set_cname(e, tokens[0], 0) == -1) 299 goto fail; 300 for (i = 2; i < n; i ++) 301 if (netent_add_alias(e, tokens[i], 0) == -1) 302 goto fail; 303 e->n_net = inet_network(tokens[1]); 304 return (e); 305 fail: 306 freenetent(e); 307 return (NULL); 308 } 309 310 static struct netent * 311 netent_from_packet(int reqtype, char *pkt, size_t pktlen) 312 { 313 struct netent *n; 314 struct packed p; 315 struct header hdr; 316 struct query q; 317 struct rr rr; 318 319 if ((n = netent_alloc(AF_INET)) == NULL) 320 return (NULL); 321 322 packed_init(&p, pkt, pktlen); 323 unpack_header(&p, &hdr); 324 for(; hdr.qdcount; hdr.qdcount--) 325 unpack_query(&p, &q); 326 for(; hdr.ancount; hdr.ancount--) { 327 unpack_rr(&p, &rr); 328 if (rr.rr_class != C_IN) 329 continue; 330 switch (rr.rr_type) { 331 case T_CNAME: 332 if (reqtype == ASR_GETNETBYNAME) { 333 if (netent_add_alias(n, rr.rr_dname, 1) == -1) 334 goto fail; 335 } else { 336 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 337 goto fail; 338 } 339 break; 340 341 case T_PTR: 342 if (reqtype != ASR_GETNETBYADDR) 343 continue; 344 if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1) 345 goto fail; 346 /* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */ 347 break; 348 349 case T_A: 350 if (n->n_addrtype != AF_INET) 351 break; 352 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 353 goto fail; 354 n->n_net = ntohl(rr.rr.in_a.addr.s_addr); 355 break; 356 } 357 } 358 359 return (n); 360 fail: 361 freenetent(n); 362 return (NULL); 363 } 364 365 static struct netent * 366 netent_alloc(int family) 367 { 368 struct netent *n; 369 370 n = calloc(1, sizeof *n); 371 if (n == NULL) 372 return (NULL); 373 374 n->n_aliases = calloc(MAXALIASES, sizeof *n->n_aliases); 375 if (n->n_aliases == NULL) { 376 freenetent(n); 377 return (NULL); 378 } 379 n->n_addrtype = family; 380 381 return (n); 382 } 383 384 static int 385 netent_set_cname(struct netent *n, const char *name, int isdname) 386 { 387 char buf[MAXDNAME]; 388 389 if (n->n_name) 390 return (0); 391 392 if (isdname) { 393 asr_strdname(name, buf, sizeof buf); 394 buf[strlen(buf) - 1] = '\0'; 395 n->n_name = strdup(buf); 396 } else { 397 n->n_name = strdup(name); 398 } 399 if (n->n_name == NULL) 400 return (-1); 401 402 return (0); 403 } 404 405 static int 406 netent_add_alias(struct netent *n, const char *name, int isdname) 407 { 408 char buf[MAXDNAME]; 409 size_t i; 410 411 for (i = 0; i < MAXALIASES; i++) 412 if (n->n_aliases[i] == NULL) 413 break; 414 if (i == MAXALIASES) 415 return (0); 416 417 if (isdname) { 418 asr_strdname(name, buf, sizeof buf); 419 buf[strlen(buf)-1] = '\0'; 420 n->n_aliases[i] = strdup(buf); 421 } else { 422 n->n_aliases[i] = strdup(name); 423 } 424 if (n->n_aliases[i] == NULL) 425 return (-1); 426 427 return (0); 428 } 429 430 void 431 freenetent(struct netent *n) 432 { 433 char **c; 434 435 free(n->n_name); 436 for (c = n->n_aliases; *c; c++) 437 free(*c); 438 free(n->n_aliases); 439 free(n); 440 } 441