1 /* $OpenBSD: getnetnamadr_async.c,v 1.23 2015/09/20 14:19:21 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.netnamadr.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.netnamadr.subq = _res_query_async_ctx( 169 name, C_IN, type, as->as_ctx); 170 } 171 172 if (as->as.netnamadr.subq == NULL) { 173 ar->ar_errno = errno; 174 ar->ar_h_errno = NETDB_INTERNAL; 175 async_set_state(as, ASR_STATE_HALT); 176 } 177 async_set_state(as, ASR_STATE_SUBQUERY); 178 break; 179 180 case ASR_DB_FILE: 181 182 if ((f = fopen(_PATH_NETWORKS, "re")) == NULL) 183 break; 184 185 if (as->as_type == ASR_GETNETBYNAME) 186 data = as->as.netnamadr.name; 187 else 188 data = (void *)&as->as.netnamadr.addr; 189 190 n = netent_file_match(f, as->as_type, data); 191 saved_errno = errno; 192 fclose(f); 193 errno = saved_errno; 194 if (n == NULL) { 195 if (errno) { 196 ar->ar_errno = errno; 197 ar->ar_h_errno = NETDB_INTERNAL; 198 async_set_state(as, ASR_STATE_HALT); 199 } 200 /* otherwise not found */ 201 break; 202 } 203 204 ar->ar_netent = &n->n; 205 ar->ar_h_errno = NETDB_SUCCESS; 206 async_set_state(as, ASR_STATE_HALT); 207 break; 208 } 209 break; 210 211 case ASR_STATE_SUBQUERY: 212 213 if ((r = asr_run(as->as.netnamadr.subq, ar)) == ASYNC_COND) 214 return (ASYNC_COND); 215 as->as.netnamadr.subq = NULL; 216 217 if (ar->ar_datalen == -1) { 218 async_set_state(as, ASR_STATE_NEXT_DB); 219 break; 220 } 221 222 /* Got packet, but no answer */ 223 if (ar->ar_count == 0) { 224 free(ar->ar_data); 225 async_set_state(as, ASR_STATE_NEXT_DB); 226 break; 227 } 228 229 n = netent_from_packet(as->as_type, ar->ar_data, 230 ar->ar_datalen); 231 free(ar->ar_data); 232 if (n == NULL) { 233 ar->ar_errno = errno; 234 ar->ar_h_errno = NETDB_INTERNAL; 235 async_set_state(as, ASR_STATE_HALT); 236 break; 237 } 238 239 if (as->as_type == ASR_GETNETBYADDR) 240 n->n.n_net = as->as.netnamadr.addr; 241 242 /* 243 * No valid hostname or address found in the dns packet. 244 * Ignore it. 245 */ 246 if ((as->as_type == ASR_GETNETBYNAME && n->n.n_net == 0) || 247 n->n.n_name == NULL) { 248 free(n); 249 async_set_state(as, ASR_STATE_NEXT_DB); 250 break; 251 } 252 253 ar->ar_netent = &n->n; 254 ar->ar_h_errno = NETDB_SUCCESS; 255 async_set_state(as, ASR_STATE_HALT); 256 break; 257 258 case ASR_STATE_NOT_FOUND: 259 260 ar->ar_errno = 0; 261 ar->ar_h_errno = HOST_NOT_FOUND; 262 async_set_state(as, ASR_STATE_HALT); 263 break; 264 265 case ASR_STATE_HALT: 266 267 if (ar->ar_h_errno) 268 ar->ar_netent = NULL; 269 else 270 ar->ar_errno = 0; 271 return (ASYNC_DONE); 272 273 default: 274 ar->ar_errno = EOPNOTSUPP; 275 ar->ar_h_errno = NETDB_INTERNAL; 276 ar->ar_gai_errno = EAI_SYSTEM; 277 async_set_state(as, ASR_STATE_HALT); 278 break; 279 } 280 goto next; 281 } 282 283 static struct netent_ext * 284 netent_file_match(FILE *f, int reqtype, const char *data) 285 { 286 struct netent_ext *e; 287 char *tokens[MAXTOKEN], buf[BUFSIZ + 1]; 288 int n, i; 289 in_addr_t net; 290 291 for (;;) { 292 n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); 293 if (n == -1) { 294 errno = 0; /* ignore errors reading the file */ 295 return (NULL); 296 } 297 298 /* there must be an address and at least one name */ 299 if (n < 2) 300 continue; 301 302 if (reqtype == ASR_GETNETBYADDR) { 303 net = inet_network(tokens[1]); 304 if (memcmp(&net, data, sizeof net) == 0) 305 goto found; 306 } else { 307 for (i = 0; i < n; i++) { 308 if (i == 1) 309 continue; 310 if (strcasecmp(data, tokens[i])) 311 continue; 312 goto found; 313 } 314 } 315 } 316 317 found: 318 if ((e = netent_alloc(AF_INET)) == NULL) 319 return (NULL); 320 if (netent_set_cname(e, tokens[0], 0) == -1) 321 goto fail; 322 for (i = 2; i < n; i ++) 323 if (netent_add_alias(e, tokens[i], 0) == -1) 324 goto fail; 325 e->n.n_net = inet_network(tokens[1]); 326 return (e); 327 fail: 328 free(e); 329 return (NULL); 330 } 331 332 static struct netent_ext * 333 netent_from_packet(int reqtype, char *pkt, size_t pktlen) 334 { 335 struct netent_ext *n; 336 struct asr_unpack p; 337 struct asr_dns_header hdr; 338 struct asr_dns_query q; 339 struct asr_dns_rr rr; 340 341 if ((n = netent_alloc(AF_INET)) == NULL) 342 return (NULL); 343 344 _asr_unpack_init(&p, pkt, pktlen); 345 _asr_unpack_header(&p, &hdr); 346 for (; hdr.qdcount; hdr.qdcount--) 347 _asr_unpack_query(&p, &q); 348 for (; hdr.ancount; hdr.ancount--) { 349 _asr_unpack_rr(&p, &rr); 350 if (rr.rr_class != C_IN) 351 continue; 352 switch (rr.rr_type) { 353 case T_CNAME: 354 if (reqtype == ASR_GETNETBYNAME) { 355 if (netent_add_alias(n, rr.rr_dname, 1) == -1) 356 goto fail; 357 } else { 358 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 359 goto fail; 360 } 361 break; 362 363 case T_PTR: 364 if (reqtype != ASR_GETNETBYADDR) 365 continue; 366 if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1) 367 goto fail; 368 /* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */ 369 break; 370 371 case T_A: 372 if (n->n.n_addrtype != AF_INET) 373 break; 374 if (netent_set_cname(n, rr.rr_dname, 1) == -1) 375 goto fail; 376 n->n.n_net = ntohl(rr.rr.in_a.addr.s_addr); 377 break; 378 } 379 } 380 381 return (n); 382 fail: 383 free(n); 384 return (NULL); 385 } 386 387 static struct netent_ext * 388 netent_alloc(int family) 389 { 390 struct netent_ext *n; 391 size_t alloc; 392 393 alloc = sizeof(*n) + 1024; 394 if ((n = calloc(1, alloc)) == NULL) 395 return (NULL); 396 397 n->n.n_addrtype = family; 398 n->n.n_aliases = n->aliases; 399 n->pos = (char *)(n) + sizeof(*n); 400 n->end = n->pos + 1024; 401 402 return (n); 403 } 404 405 static int 406 netent_set_cname(struct netent_ext *n, const char *name, int isdname) 407 { 408 char buf[MAXDNAME]; 409 size_t l; 410 411 if (n->n.n_name) 412 return (-1); 413 414 if (isdname) { 415 _asr_strdname(name, buf, sizeof buf); 416 buf[strlen(buf) - 1] = '\0'; 417 if (!res_hnok(buf)) 418 return (-1); 419 name = buf; 420 } 421 422 l = strlen(name) + 1; 423 if (n->pos + l >= n->end) 424 return (-1); 425 426 n->n.n_name = n->pos; 427 memmove(n->pos, name, l); 428 n->pos += l; 429 430 return (0); 431 } 432 433 static int 434 netent_add_alias(struct netent_ext *n, const char *name, int isdname) 435 { 436 char buf[MAXDNAME]; 437 size_t i, l; 438 439 for (i = 0; i < MAXALIASES; i++) 440 if (n->aliases[i] == NULL) 441 break; 442 if (i == MAXALIASES) 443 return (-1); 444 445 if (isdname) { 446 _asr_strdname(name, buf, sizeof buf); 447 buf[strlen(buf)-1] = '\0'; 448 if (!res_hnok(buf)) 449 return (-1); 450 name = buf; 451 } 452 453 l = strlen(name) + 1; 454 if (n->pos + l >= n->end) 455 return (-1); 456 457 n->aliases[i] = n->pos; 458 memmove(n->pos, name, l); 459 n->pos += l; 460 return (0); 461 } 462