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