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