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