1 /* $OpenBSD: getnetnamadr_async.c,v 1.19 2014/11/02 13:59:16 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 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <netinet/in.h> 21 #include <arpa/inet.h> 22 #include <arpa/nameser.h> 23 #include <netdb.h> 24 25 #include <asr.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <resolv.h> /* for res_hnok */ 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "asr_private.h" 34 35 #define MAXALIASES 16 36 37 struct netent_ext { 38 struct netent n; 39 char *aliases[MAXALIASES + 1]; 40 char *end; 41 char *pos; 42 }; 43 44 static int getnetnamadr_async_run(struct asr_query *, struct asr_result *); 45 static struct netent_ext *netent_alloc(int); 46 static int netent_set_cname(struct netent_ext *, const char *, int); 47 static int netent_add_alias(struct netent_ext *, const char *, int); 48 static struct netent_ext *netent_file_match(FILE *, int, const char *); 49 static struct netent_ext *netent_from_packet(int, char *, size_t); 50 51 struct asr_query * 52 getnetbyname_async(const char *name, void *asr) 53 { 54 struct asr_ctx *ac; 55 struct asr_query *as; 56 57 /* The current resolver segfaults. */ 58 if (name == NULL) { 59 errno = EINVAL; 60 return (NULL); 61 } 62 63 ac = asr_use_resolver(asr); 64 if ((as = asr_async_new(ac, ASR_GETNETBYNAME)) == NULL) 65 goto abort; /* errno set */ 66 as->as_run = getnetnamadr_async_run; 67 68 as->as.netnamadr.family = AF_INET; 69 as->as.netnamadr.name = strdup(name); 70 if (as->as.netnamadr.name == NULL) 71 goto abort; /* errno set */ 72 73 asr_ctx_unref(ac); 74 return (as); 75 76 abort: 77 if (as) 78 asr_async_free(as); 79 asr_ctx_unref(ac); 80 return (NULL); 81 } 82 83 struct asr_query * 84 getnetbyaddr_async(in_addr_t net, int family, void *asr) 85 { 86 struct asr_ctx *ac; 87 struct asr_query *as; 88 89 ac = asr_use_resolver(asr); 90 if ((as = asr_async_new(ac, ASR_GETNETBYADDR)) == NULL) 91 goto abort; /* errno set */ 92 as->as_run = getnetnamadr_async_run; 93 94 as->as.netnamadr.family = family; 95 as->as.netnamadr.addr = net; 96 97 asr_ctx_unref(ac); 98 return (as); 99 100 abort: 101 if (as) 102 asr_async_free(as); 103 asr_ctx_unref(ac); 104 return (NULL); 105 } 106 107 static int 108 getnetnamadr_async_run(struct asr_query *as, struct asr_result *ar) 109 { 110 struct netent_ext *n; 111 int r, type, saved_errno; 112 FILE *f; 113 char dname[MAXDNAME], *name, *data; 114 in_addr_t in; 115 116 next: 117 switch (as->as_state) { 118 119 case ASR_STATE_INIT: 120 121 if (as->as.netnamadr.family != AF_INET) { 122 ar->ar_h_errno = NETDB_INTERNAL; 123 ar->ar_errno = EAFNOSUPPORT; 124 async_set_state(as, ASR_STATE_HALT); 125 break; 126 } 127 128 if (as->as_type == ASR_GETNETBYNAME && 129 as->as.netnamadr.name[0] == '\0') { 130 ar->ar_h_errno = NO_DATA; 131 async_set_state(as, ASR_STATE_HALT); 132 break; 133 } 134 135 async_set_state(as, ASR_STATE_NEXT_DB); 136 break; 137 138 case ASR_STATE_NEXT_DB: 139 140 if (asr_iter_db(as) == -1) { 141 async_set_state(as, ASR_STATE_NOT_FOUND); 142 break; 143 } 144 145 switch (AS_DB(as)) { 146 case ASR_DB_DNS: 147 148 if (as->as_type == ASR_GETNETBYNAME) { 149 type = T_A; 150 /* 151 * I think we want to do the former, but our 152 * resolver is doing the following, so let's 153 * preserve bugward-compatibility there. 154 */ 155 type = T_PTR; 156 name = as->as.netnamadr.name; 157 as->as.netnamadr.subq = res_search_async_ctx( 158 name, C_IN, type, as->as_ctx); 159 } else { 160 type = T_PTR; 161 name = dname; 162 163 in = htonl(as->as.netnamadr.addr); 164 asr_addr_as_fqdn((char *)&in, 165 as->as.netnamadr.family, 166 dname, sizeof(dname)); 167 as->as.netnamadr.subq = res_query_async_ctx( 168 name, C_IN, type, as->as_ctx); 169 } 170 171 if (as->as.netnamadr.subq == NULL) { 172 ar->ar_errno = errno; 173 ar->ar_h_errno = NETDB_INTERNAL; 174 async_set_state(as, ASR_STATE_HALT); 175 } 176 async_set_state(as, ASR_STATE_SUBQUERY); 177 break; 178 179 case ASR_DB_FILE: 180 181 if ((f = fopen(_PATH_NETWORKS, "re")) == NULL) 182 break; 183 184 if (as->as_type == ASR_GETNETBYNAME) 185 data = as->as.netnamadr.name; 186 else 187 data = (void *)&as->as.netnamadr.addr; 188 189 n = netent_file_match(f, as->as_type, data); 190 saved_errno = errno; 191 fclose(f); 192 errno = saved_errno; 193 if (n == NULL) { 194 if (errno) { 195 ar->ar_errno = errno; 196 ar->ar_h_errno = NETDB_INTERNAL; 197 async_set_state(as, ASR_STATE_HALT); 198 } 199 /* otherwise not found */ 200 break; 201 } 202 203 ar->ar_netent = &n->n; 204 ar->ar_h_errno = NETDB_SUCCESS; 205 async_set_state(as, ASR_STATE_HALT); 206 break; 207 } 208 break; 209 210 case ASR_STATE_SUBQUERY: 211 212 if ((r = asr_run(as->as.netnamadr.subq, ar)) == ASYNC_COND) 213 return (ASYNC_COND); 214 as->as.netnamadr.subq = NULL; 215 216 if (ar->ar_datalen == -1) { 217 async_set_state(as, ASR_STATE_NEXT_DB); 218 break; 219 } 220 221 /* Got packet, but no answer */ 222 if (ar->ar_count == 0) { 223 free(ar->ar_data); 224 async_set_state(as, ASR_STATE_NEXT_DB); 225 break; 226 } 227 228 n = netent_from_packet(as->as_type, ar->ar_data, 229 ar->ar_datalen); 230 free(ar->ar_data); 231 if (n == NULL) { 232 ar->ar_errno = errno; 233 ar->ar_h_errno = NETDB_INTERNAL; 234 async_set_state(as, ASR_STATE_HALT); 235 break; 236 } 237 238 if (as->as_type == ASR_GETNETBYADDR) 239 n->n.n_net = as->as.netnamadr.addr; 240 241 /* 242 * No valid hostname or address found in the dns packet. 243 * Ignore it. 244 */ 245 if ((as->as_type == ASR_GETNETBYNAME && n->n.n_net == 0) || 246 n->n.n_name == NULL) { 247 free(n); 248 async_set_state(as, ASR_STATE_NEXT_DB); 249 break; 250 } 251 252 ar->ar_netent = &n->n; 253 ar->ar_h_errno = NETDB_SUCCESS; 254 async_set_state(as, ASR_STATE_HALT); 255 break; 256 257 case ASR_STATE_NOT_FOUND: 258 259 ar->ar_errno = 0; 260 ar->ar_h_errno = HOST_NOT_FOUND; 261 async_set_state(as, ASR_STATE_HALT); 262 break; 263 264 case ASR_STATE_HALT: 265 266 if (ar->ar_h_errno) 267 ar->ar_netent = NULL; 268 else 269 ar->ar_errno = 0; 270 return (ASYNC_DONE); 271 272 default: 273 ar->ar_errno = EOPNOTSUPP; 274 ar->ar_h_errno = NETDB_INTERNAL; 275 ar->ar_gai_errno = EAI_SYSTEM; 276 async_set_state(as, ASR_STATE_HALT); 277 break; 278 } 279 goto next; 280 } 281 282 static struct netent_ext * 283 netent_file_match(FILE *f, int reqtype, const char *data) 284 { 285 struct netent_ext *e; 286 char *tokens[MAXTOKEN]; 287 int n, i; 288 in_addr_t net; 289 290 for (;;) { 291 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 292 if (n == -1) { 293 errno = 0; /* ignore errors reading the file */ 294 return (NULL); 295 } 296 297 /* there must be an address and at least one name */ 298 if (n < 2) 299 continue; 300 301 if (reqtype == ASR_GETNETBYADDR) { 302 net = inet_network(tokens[1]); 303 if (memcmp(&net, data, sizeof net) == 0) 304 goto found; 305 } else { 306 for (i = 0; i < n; i++) { 307 if (i == 1) 308 continue; 309 if (strcasecmp(data, tokens[i])) 310 continue; 311 goto found; 312 } 313 } 314 } 315 316 found: 317 if ((e = netent_alloc(AF_INET)) == NULL) 318 return (NULL); 319 if (netent_set_cname(e, tokens[0], 0) == -1) 320 goto fail; 321 for (i = 2; i < n; i ++) 322 if (netent_add_alias(e, tokens[i], 0) == -1) 323 goto fail; 324 e->n.n_net = inet_network(tokens[1]); 325 return (e); 326 fail: 327 free(e); 328 return (NULL); 329 } 330 331 static struct netent_ext * 332 netent_from_packet(int reqtype, char *pkt, size_t pktlen) 333 { 334 struct netent_ext *n; 335 struct asr_unpack p; 336 struct asr_dns_header hdr; 337 struct asr_dns_query q; 338 struct asr_dns_rr rr; 339 340 if ((n = netent_alloc(AF_INET)) == NULL) 341 return (NULL); 342 343 asr_unpack_init(&p, pkt, pktlen); 344 asr_unpack_header(&p, &hdr); 345 for (; hdr.qdcount; hdr.qdcount--) 346 asr_unpack_query(&p, &q); 347 for (; hdr.ancount; hdr.ancount--) { 348 asr_unpack_rr(&p, &rr); 349 if (rr.rr_class != C_IN) 350 continue; 351 switch (rr.rr_type) { 352 case T_CNAME: 353 if (reqtype == ASR_GETNETBYNAME) { 354 if (netent_add_alias(n, rr.rr_dname, 1) == -1) 355 goto fail; 356 } else { 357 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 358 goto fail; 359 } 360 break; 361 362 case T_PTR: 363 if (reqtype != ASR_GETNETBYADDR) 364 continue; 365 if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1) 366 goto fail; 367 /* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */ 368 break; 369 370 case T_A: 371 if (n->n.n_addrtype != AF_INET) 372 break; 373 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 374 goto fail; 375 n->n.n_net = ntohl(rr.rr.in_a.addr.s_addr); 376 break; 377 } 378 } 379 380 return (n); 381 fail: 382 free(n); 383 return (NULL); 384 } 385 386 static struct netent_ext * 387 netent_alloc(int family) 388 { 389 struct netent_ext *n; 390 size_t alloc; 391 392 alloc = sizeof(*n) + 1024; 393 if ((n = calloc(1, alloc)) == NULL) 394 return (NULL); 395 396 n->n.n_addrtype = family; 397 n->n.n_aliases = n->aliases; 398 n->pos = (char *)(n) + sizeof(*n); 399 n->end = n->pos + 1024; 400 401 return (n); 402 } 403 404 static int 405 netent_set_cname(struct netent_ext *n, const char *name, int isdname) 406 { 407 char buf[MAXDNAME]; 408 size_t l; 409 410 if (n->n.n_name) 411 return (-1); 412 413 if (isdname) { 414 asr_strdname(name, buf, sizeof buf); 415 buf[strlen(buf) - 1] = '\0'; 416 if (!res_hnok(buf)) 417 return (-1); 418 name = buf; 419 } 420 421 l = strlen(name) + 1; 422 if (n->pos + l >= n->end) 423 return (-1); 424 425 n->n.n_name = n->pos; 426 memmove(n->pos, name, l); 427 n->pos += l; 428 429 return (0); 430 } 431 432 static int 433 netent_add_alias(struct netent_ext *n, const char *name, int isdname) 434 { 435 char buf[MAXDNAME]; 436 size_t i, l; 437 438 for (i = 0; i < MAXALIASES; i++) 439 if (n->aliases[i] == NULL) 440 break; 441 if (i == MAXALIASES) 442 return (-1); 443 444 if (isdname) { 445 asr_strdname(name, buf, sizeof buf); 446 buf[strlen(buf)-1] = '\0'; 447 if (!res_hnok(buf)) 448 return (-1); 449 name = buf; 450 } 451 452 l = strlen(name) + 1; 453 if (n->pos + l >= n->end) 454 return (-1); 455 456 n->aliases[i] = n->pos; 457 memmove(n->pos, name, l); 458 n->pos += l; 459 return (0); 460 } 461