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