1*b44da627Seric /* $OpenBSD: asr.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */ 2*b44da627Seric /* 3*b44da627Seric * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org> 4*b44da627Seric * 5*b44da627Seric * Permission to use, copy, modify, and distribute this software for any 6*b44da627Seric * purpose with or without fee is hereby granted, provided that the above 7*b44da627Seric * copyright notice and this permission notice appear in all copies. 8*b44da627Seric * 9*b44da627Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*b44da627Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*b44da627Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*b44da627Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*b44da627Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*b44da627Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*b44da627Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*b44da627Seric */ 17*b44da627Seric #include <sys/types.h> 18*b44da627Seric #include <sys/stat.h> 19*b44da627Seric 20*b44da627Seric #include <netinet/in.h> 21*b44da627Seric #include <arpa/inet.h> 22*b44da627Seric #include <arpa/nameser.h> 23*b44da627Seric 24*b44da627Seric #include <err.h> 25*b44da627Seric #include <errno.h> 26*b44da627Seric #include <fcntl.h> 27*b44da627Seric #include <netdb.h> 28*b44da627Seric #include <resolv.h> 29*b44da627Seric #include <poll.h> 30*b44da627Seric #include <stdio.h> 31*b44da627Seric #include <stdlib.h> 32*b44da627Seric #include <string.h> 33*b44da627Seric #include <unistd.h> 34*b44da627Seric 35*b44da627Seric #include "asr.h" 36*b44da627Seric #include "asr_private.h" 37*b44da627Seric 38*b44da627Seric #define DEFAULT_CONFFILE "/etc/resolv.conf" 39*b44da627Seric #define DEFAULT_HOSTFILE "/etc/hosts" 40*b44da627Seric #define DEFAULT_CONF "lookup bind file\nnameserver 127.0.0.1\n" 41*b44da627Seric #define DEFAULT_LOOKUP "lookup bind file" 42*b44da627Seric 43*b44da627Seric #define RELOAD_DELAY 15 /* seconds */ 44*b44da627Seric 45*b44da627Seric static void asr_check_reload(struct asr *); 46*b44da627Seric static struct asr_ctx *asr_ctx_create(void); 47*b44da627Seric static void asr_ctx_ref(struct asr_ctx *); 48*b44da627Seric static void asr_ctx_free(struct asr_ctx *); 49*b44da627Seric static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); 50*b44da627Seric static int asr_ctx_from_file(struct asr_ctx *, const char *); 51*b44da627Seric static int asr_ctx_from_string(struct asr_ctx *, const char *); 52*b44da627Seric static int asr_ctx_parse(const char*, int(*)(char**, int, struct asr_ctx*), 53*b44da627Seric struct asr_ctx *); 54*b44da627Seric static int asr_parse_nameserver(struct sockaddr *, const char *); 55*b44da627Seric static char *asr_hostalias(const char *, char *, size_t); 56*b44da627Seric static int asr_ndots(const char *); 57*b44da627Seric static void asr_ctx_envopts(struct asr_ctx *); 58*b44da627Seric static int pass0(char **, int, struct asr_ctx *); 59*b44da627Seric 60*b44da627Seric static struct asr * _default_resolver = NULL; 61*b44da627Seric 62*b44da627Seric /* Allocate and configure an async "resolver". */ 63*b44da627Seric struct asr * 64*b44da627Seric async_resolver(const char *conf) 65*b44da627Seric { 66*b44da627Seric static int init = 0; 67*b44da627Seric struct asr *asr; 68*b44da627Seric 69*b44da627Seric #ifdef DEBUG 70*b44da627Seric if (init == 0) { 71*b44da627Seric if (getenv("ASR_DEBUG")) 72*b44da627Seric asr_debug = 1; 73*b44da627Seric init = 1; 74*b44da627Seric } 75*b44da627Seric #endif 76*b44da627Seric if ((asr = calloc(1, sizeof(*asr))) == NULL) 77*b44da627Seric goto fail; 78*b44da627Seric 79*b44da627Seric /* If not setuid/setgid, allow to use an alternate config. */ 80*b44da627Seric if (conf == NULL && !issetugid()) 81*b44da627Seric conf = getenv("ASR_CONFIG"); 82*b44da627Seric 83*b44da627Seric if (conf == NULL) 84*b44da627Seric conf = DEFAULT_CONFFILE; 85*b44da627Seric 86*b44da627Seric if (conf[0] == '!') { 87*b44da627Seric /* Use the rest of the string as config file */ 88*b44da627Seric if ((asr->a_ctx = asr_ctx_create()) == NULL) 89*b44da627Seric goto fail; 90*b44da627Seric if (asr_ctx_from_string(asr->a_ctx, conf + 1) == -1) 91*b44da627Seric goto fail; 92*b44da627Seric } else { 93*b44da627Seric /* Use the given config file */ 94*b44da627Seric asr->a_path = strdup(conf); 95*b44da627Seric asr_check_reload(asr); 96*b44da627Seric if (asr->a_ctx == NULL) { 97*b44da627Seric if ((asr->a_ctx = asr_ctx_create()) == NULL) 98*b44da627Seric goto fail; 99*b44da627Seric if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) 100*b44da627Seric goto fail; 101*b44da627Seric asr_ctx_envopts(asr->a_ctx); 102*b44da627Seric } 103*b44da627Seric } 104*b44da627Seric 105*b44da627Seric #ifdef DEBUG 106*b44da627Seric asr_dump(asr); 107*b44da627Seric #endif 108*b44da627Seric return (asr); 109*b44da627Seric 110*b44da627Seric fail: 111*b44da627Seric if (asr) { 112*b44da627Seric if (asr->a_ctx) 113*b44da627Seric asr_ctx_free(asr->a_ctx); 114*b44da627Seric free(asr); 115*b44da627Seric } 116*b44da627Seric 117*b44da627Seric return (NULL); 118*b44da627Seric } 119*b44da627Seric 120*b44da627Seric /* 121*b44da627Seric * Free the "asr" async resolver (or the thread-local resolver if NULL). 122*b44da627Seric * Drop the reference to the current context. 123*b44da627Seric */ 124*b44da627Seric void 125*b44da627Seric async_resolver_done(struct asr *asr) 126*b44da627Seric { 127*b44da627Seric if (asr == NULL) { 128*b44da627Seric if (_default_resolver == NULL) 129*b44da627Seric return; 130*b44da627Seric asr = _default_resolver; 131*b44da627Seric _default_resolver = NULL; 132*b44da627Seric } 133*b44da627Seric 134*b44da627Seric asr_ctx_unref(asr->a_ctx); 135*b44da627Seric if (asr->a_path) 136*b44da627Seric free(asr->a_path); 137*b44da627Seric free(asr); 138*b44da627Seric } 139*b44da627Seric 140*b44da627Seric /* 141*b44da627Seric * Cancel an async query. 142*b44da627Seric */ 143*b44da627Seric void 144*b44da627Seric async_abort(struct async *as) 145*b44da627Seric { 146*b44da627Seric async_free(as); 147*b44da627Seric } 148*b44da627Seric 149*b44da627Seric /* 150*b44da627Seric * Resume the "as" async query resolution. Return one of ASYNC_COND, 151*b44da627Seric * ASYNC_YIELD or ASYNC_DONE and put query-specific return values in 152*b44da627Seric * the user-allocated memory at "ar". 153*b44da627Seric */ 154*b44da627Seric int 155*b44da627Seric async_run(struct async *as, struct async_res *ar) 156*b44da627Seric { 157*b44da627Seric int r; 158*b44da627Seric 159*b44da627Seric #ifdef DEBUG 160*b44da627Seric asr_printf("asr: async_run(%p, %p) %s ctx=[%p]\n", 161*b44da627Seric as, ar, asr_querystr(as->as_type), as->as_ctx); 162*b44da627Seric #endif 163*b44da627Seric r = as->as_run(as, ar); 164*b44da627Seric 165*b44da627Seric #ifdef DEBUG 166*b44da627Seric if (asr_debug) { 167*b44da627Seric asr_printf("asr: async_run(%p, %p) -> %s", as, ar, 168*b44da627Seric asr_transitionstr(r)); 169*b44da627Seric if (r == ASYNC_COND) 170*b44da627Seric asr_printf(" fd=%i timeout=%i\n", 171*b44da627Seric ar->ar_fd, ar->ar_timeout); 172*b44da627Seric else 173*b44da627Seric asr_printf("\n"); 174*b44da627Seric fflush(stderr); 175*b44da627Seric } 176*b44da627Seric #endif 177*b44da627Seric if (r == ASYNC_DONE) 178*b44da627Seric async_free(as); 179*b44da627Seric 180*b44da627Seric return (r); 181*b44da627Seric } 182*b44da627Seric 183*b44da627Seric /* 184*b44da627Seric * Same as above, but run in a loop that handles the fd conditions result. 185*b44da627Seric */ 186*b44da627Seric int 187*b44da627Seric async_run_sync(struct async *as, struct async_res *ar) 188*b44da627Seric { 189*b44da627Seric struct pollfd fds[1]; 190*b44da627Seric int r; 191*b44da627Seric 192*b44da627Seric while((r = async_run(as, ar)) == ASYNC_COND) { 193*b44da627Seric fds[0].fd = ar->ar_fd; 194*b44da627Seric fds[0].events = (ar->ar_cond == ASYNC_READ) ? POLLIN : POLLOUT; 195*b44da627Seric again: 196*b44da627Seric r = poll(fds, 1, ar->ar_timeout); 197*b44da627Seric if (r == -1 && errno == EINTR) 198*b44da627Seric goto again; 199*b44da627Seric if (r == -1) /* XXX Is it possible? and what to do if so? */ 200*b44da627Seric err(1, "poll"); 201*b44da627Seric } 202*b44da627Seric 203*b44da627Seric return (r); 204*b44da627Seric } 205*b44da627Seric 206*b44da627Seric /* 207*b44da627Seric * Create a new async request of the given "type" on the async context "ac". 208*b44da627Seric * Take a reference on it so it does not gets deleted while the async query 209*b44da627Seric * is running. 210*b44da627Seric */ 211*b44da627Seric struct async * 212*b44da627Seric async_new(struct asr_ctx *ac, int type) 213*b44da627Seric { 214*b44da627Seric struct async *as; 215*b44da627Seric #ifdef DEBUG 216*b44da627Seric asr_printf("asr: async_new(ctx=%p) type=%i refcount=%i\n", 217*b44da627Seric ac, type, ac->ac_refcount); 218*b44da627Seric #endif 219*b44da627Seric if ((as = calloc(1, sizeof(*as))) == NULL) 220*b44da627Seric return (NULL); 221*b44da627Seric 222*b44da627Seric ac->ac_refcount += 1; 223*b44da627Seric as->as_ctx = ac; 224*b44da627Seric as->as_fd = -1; 225*b44da627Seric as->as_type = type; 226*b44da627Seric as->as_state = ASR_STATE_INIT; 227*b44da627Seric 228*b44da627Seric return (as); 229*b44da627Seric } 230*b44da627Seric 231*b44da627Seric /* 232*b44da627Seric * Free an async query and unref the associated context. 233*b44da627Seric */ 234*b44da627Seric void 235*b44da627Seric async_free(struct async *as) 236*b44da627Seric { 237*b44da627Seric #ifdef DEBUG 238*b44da627Seric asr_printf("asr: async_free(%p)\n", as); 239*b44da627Seric #endif 240*b44da627Seric switch(as->as_type) { 241*b44da627Seric case ASR_SEND: 242*b44da627Seric if (as->as_fd != -1) 243*b44da627Seric close(as->as_fd); 244*b44da627Seric if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF)) 245*b44da627Seric free (as->as.dns.obuf); 246*b44da627Seric if (as->as.dns.ibuf && !(as->as.dns.flags & ASYNC_EXTIBUF)) 247*b44da627Seric free (as->as.dns.ibuf); 248*b44da627Seric if (as->as.dns.dname) 249*b44da627Seric free(as->as.dns.dname); 250*b44da627Seric break; 251*b44da627Seric 252*b44da627Seric case ASR_SEARCH: 253*b44da627Seric if (as->as.search.subq) 254*b44da627Seric async_free(as->as.search.subq); 255*b44da627Seric if (as->as.search.name) 256*b44da627Seric free(as->as.search.name); 257*b44da627Seric break; 258*b44da627Seric 259*b44da627Seric case ASR_GETRRSETBYNAME: 260*b44da627Seric if (as->as.rrset.subq) 261*b44da627Seric async_free(as->as.rrset.subq); 262*b44da627Seric if (as->as.rrset.name) 263*b44da627Seric free(as->as.rrset.name); 264*b44da627Seric break; 265*b44da627Seric 266*b44da627Seric case ASR_GETHOSTBYNAME: 267*b44da627Seric case ASR_GETHOSTBYADDR: 268*b44da627Seric if (as->as.hostnamadr.subq) 269*b44da627Seric async_free(as->as.hostnamadr.subq); 270*b44da627Seric if (as->as.hostnamadr.name) 271*b44da627Seric free(as->as.hostnamadr.name); 272*b44da627Seric if (as->as.hostnamadr.dname) 273*b44da627Seric free(as->as.hostnamadr.dname); 274*b44da627Seric break; 275*b44da627Seric 276*b44da627Seric case ASR_GETNETBYNAME: 277*b44da627Seric case ASR_GETNETBYADDR: 278*b44da627Seric if (as->as.netnamadr.subq) 279*b44da627Seric async_free(as->as.netnamadr.subq); 280*b44da627Seric if (as->as.netnamadr.name) 281*b44da627Seric free(as->as.netnamadr.name); 282*b44da627Seric break; 283*b44da627Seric 284*b44da627Seric case ASR_GETADDRINFO: 285*b44da627Seric if (as->as.ai.subq) 286*b44da627Seric async_free(as->as.ai.subq); 287*b44da627Seric if (as->as.ai.aifirst) 288*b44da627Seric freeaddrinfo(as->as.ai.aifirst); 289*b44da627Seric if (as->as.ai.hostname) 290*b44da627Seric free(as->as.ai.hostname); 291*b44da627Seric if (as->as.ai.servname) 292*b44da627Seric free(as->as.ai.servname); 293*b44da627Seric break; 294*b44da627Seric 295*b44da627Seric case ASR_GETNAMEINFO: 296*b44da627Seric if (as->as.ni.subq) 297*b44da627Seric async_free(as->as.ni.subq); 298*b44da627Seric break; 299*b44da627Seric 300*b44da627Seric case ASR_HOSTADDR: 301*b44da627Seric if (as->as.host.name) 302*b44da627Seric free(as->as.host.name); 303*b44da627Seric if (as->as.host.subq) 304*b44da627Seric async_free(as->as.host.subq); 305*b44da627Seric if (as->as.host.pkt) 306*b44da627Seric free(as->as.host.pkt); 307*b44da627Seric if (as->as.host.file) 308*b44da627Seric fclose(as->as.host.file); 309*b44da627Seric break; 310*b44da627Seric } 311*b44da627Seric 312*b44da627Seric asr_ctx_unref(as->as_ctx); 313*b44da627Seric free(as); 314*b44da627Seric } 315*b44da627Seric 316*b44da627Seric /* 317*b44da627Seric * Get a context from the given resolver. This takes a new reference to 318*b44da627Seric * the returned context, which *must* be explicitely dropped when done 319*b44da627Seric * using this context. 320*b44da627Seric */ 321*b44da627Seric struct asr_ctx * 322*b44da627Seric asr_use_resolver(struct asr *asr) 323*b44da627Seric { 324*b44da627Seric if (asr == NULL) { 325*b44da627Seric /* We want the use the global resolver. */ 326*b44da627Seric 327*b44da627Seric /* _THREAD_PRIVATE_MUTEX_LOCK(_asr_mutex); */ 328*b44da627Seric if (_default_resolver != NULL) 329*b44da627Seric asr_check_reload(asr); 330*b44da627Seric else 331*b44da627Seric _default_resolver = async_resolver(NULL); 332*b44da627Seric asr = _default_resolver; 333*b44da627Seric /* _THREAD_PRIVATE_MUTEX_UNLOCK(_asr_mutex); */ 334*b44da627Seric } 335*b44da627Seric 336*b44da627Seric asr_check_reload(asr); 337*b44da627Seric asr_ctx_ref(asr->a_ctx); 338*b44da627Seric return (asr->a_ctx); 339*b44da627Seric } 340*b44da627Seric 341*b44da627Seric static void 342*b44da627Seric asr_ctx_ref(struct asr_ctx *ac) 343*b44da627Seric { 344*b44da627Seric #ifdef DEBUG 345*b44da627Seric asr_printf("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", 346*b44da627Seric ac, ac->ac_refcount); 347*b44da627Seric #endif 348*b44da627Seric ac->ac_refcount += 1; 349*b44da627Seric } 350*b44da627Seric 351*b44da627Seric /* 352*b44da627Seric * Drop a reference to an async context, freeing it if the reference 353*b44da627Seric * count drops to 0. 354*b44da627Seric */ 355*b44da627Seric void 356*b44da627Seric asr_ctx_unref(struct asr_ctx *ac) 357*b44da627Seric { 358*b44da627Seric #ifdef DEBUG 359*b44da627Seric asr_printf("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", 360*b44da627Seric ac, ac->ac_refcount); 361*b44da627Seric #endif 362*b44da627Seric if (--ac->ac_refcount) 363*b44da627Seric return; 364*b44da627Seric 365*b44da627Seric asr_ctx_free(ac); 366*b44da627Seric } 367*b44da627Seric 368*b44da627Seric static void 369*b44da627Seric asr_ctx_free(struct asr_ctx *ac) 370*b44da627Seric { 371*b44da627Seric int i; 372*b44da627Seric 373*b44da627Seric if (ac->ac_domain) 374*b44da627Seric free(ac->ac_domain); 375*b44da627Seric for(i = 0; i < ac->ac_nscount; i++) 376*b44da627Seric free(ac->ac_ns[i]); 377*b44da627Seric for(i = 0; i < ac->ac_domcount; i++) 378*b44da627Seric free(ac->ac_dom[i]); 379*b44da627Seric 380*b44da627Seric free(ac); 381*b44da627Seric } 382*b44da627Seric 383*b44da627Seric /* 384*b44da627Seric * Reload the configuration file if it has changed on disk. 385*b44da627Seric */ 386*b44da627Seric static void 387*b44da627Seric asr_check_reload(struct asr *asr) 388*b44da627Seric { 389*b44da627Seric struct stat st; 390*b44da627Seric struct asr_ctx *ac; 391*b44da627Seric struct timespec tp; 392*b44da627Seric 393*b44da627Seric if (asr->a_path == NULL) 394*b44da627Seric return; 395*b44da627Seric 396*b44da627Seric if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) 397*b44da627Seric return; 398*b44da627Seric 399*b44da627Seric if ((tp.tv_sec - asr->a_rtime) < RELOAD_DELAY) 400*b44da627Seric return; 401*b44da627Seric asr->a_rtime = tp.tv_sec; 402*b44da627Seric 403*b44da627Seric #ifdef DEBUG 404*b44da627Seric asr_printf("asr: checking for update of \"%s\"\n", asr->a_path); 405*b44da627Seric #endif 406*b44da627Seric 407*b44da627Seric if (stat(asr->a_path, &st) == -1 || 408*b44da627Seric asr->a_mtime == st.st_mtime || 409*b44da627Seric (ac = asr_ctx_create()) == NULL) 410*b44da627Seric return; 411*b44da627Seric asr->a_mtime = st.st_mtime; 412*b44da627Seric 413*b44da627Seric #ifdef DEBUG 414*b44da627Seric asr_printf("asr: reloading config file\n"); 415*b44da627Seric #endif 416*b44da627Seric 417*b44da627Seric if (asr_ctx_from_file(ac, asr->a_path) == -1) { 418*b44da627Seric asr_ctx_free(ac); 419*b44da627Seric return; 420*b44da627Seric } 421*b44da627Seric 422*b44da627Seric asr_ctx_envopts(ac); 423*b44da627Seric if (asr->a_ctx) 424*b44da627Seric asr_ctx_unref(asr->a_ctx); 425*b44da627Seric asr->a_ctx = ac; 426*b44da627Seric } 427*b44da627Seric 428*b44da627Seric /* 429*b44da627Seric * Construct a fully-qualified domain name for the given name and domain. 430*b44da627Seric * If "name" ends with a '.' it is considered as a FQDN by itself. 431*b44da627Seric * Otherwise, the domain, which must be a FQDN, is appended to "name" (it 432*b44da627Seric * may have a leading dot which would be ignored). If the domain is null, 433*b44da627Seric * then "." is used. Return the length of the constructed FQDN or (0) on 434*b44da627Seric * error. 435*b44da627Seric */ 436*b44da627Seric size_t 437*b44da627Seric asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) 438*b44da627Seric { 439*b44da627Seric size_t len; 440*b44da627Seric 441*b44da627Seric if (domain == NULL) 442*b44da627Seric domain = "."; 443*b44da627Seric else if ((len = strlen(domain)) == 0) 444*b44da627Seric return (0); 445*b44da627Seric else if (domain[len -1] != '.') 446*b44da627Seric return (0); 447*b44da627Seric 448*b44da627Seric len = strlen(name); 449*b44da627Seric if (len == 0) { 450*b44da627Seric strlcpy(buf, domain, buflen); 451*b44da627Seric } else if (name[len - 1] != '.') { 452*b44da627Seric if (domain[0] == '.') 453*b44da627Seric domain += 1; 454*b44da627Seric strlcpy(buf, name, buflen); 455*b44da627Seric strlcat(buf, ".", buflen); 456*b44da627Seric strlcat(buf, domain, buflen); 457*b44da627Seric } else { 458*b44da627Seric strlcpy(buf, name, buflen); 459*b44da627Seric } 460*b44da627Seric 461*b44da627Seric return (strlen(buf)); 462*b44da627Seric } 463*b44da627Seric 464*b44da627Seric /* 465*b44da627Seric * Concatenate a name and a domain name. The result has no trailing dot. 466*b44da627Seric */ 467*b44da627Seric size_t 468*b44da627Seric asr_domcat(const char *name, const char *domain, char *buf, size_t buflen) 469*b44da627Seric { 470*b44da627Seric size_t r; 471*b44da627Seric 472*b44da627Seric r = asr_make_fqdn(name, domain, buf, buflen); 473*b44da627Seric if (r == 0) 474*b44da627Seric return (0); 475*b44da627Seric buf[r - 1] = '\0'; 476*b44da627Seric 477*b44da627Seric return (r - 1); 478*b44da627Seric } 479*b44da627Seric 480*b44da627Seric /* 481*b44da627Seric * Count the dots in a string. 482*b44da627Seric */ 483*b44da627Seric static int 484*b44da627Seric asr_ndots(const char *s) 485*b44da627Seric { 486*b44da627Seric int n; 487*b44da627Seric 488*b44da627Seric for(n = 0; *s; s++) 489*b44da627Seric if (*s == '.') 490*b44da627Seric n += 1; 491*b44da627Seric 492*b44da627Seric return (n); 493*b44da627Seric } 494*b44da627Seric 495*b44da627Seric /* 496*b44da627Seric * Allocate a new empty context. 497*b44da627Seric */ 498*b44da627Seric static struct asr_ctx * 499*b44da627Seric asr_ctx_create(void) 500*b44da627Seric { 501*b44da627Seric struct asr_ctx *ac; 502*b44da627Seric 503*b44da627Seric if ((ac = calloc(1, sizeof(*ac))) == NULL) 504*b44da627Seric return (NULL); 505*b44da627Seric 506*b44da627Seric ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 507*b44da627Seric ac->ac_refcount = 1; 508*b44da627Seric ac->ac_ndots = 1; 509*b44da627Seric ac->ac_family[0] = AF_INET; 510*b44da627Seric ac->ac_family[1] = AF_INET6; 511*b44da627Seric ac->ac_family[2] = -1; 512*b44da627Seric 513*b44da627Seric ac->ac_hostfile = DEFAULT_HOSTFILE; 514*b44da627Seric 515*b44da627Seric ac->ac_nscount = 0; 516*b44da627Seric ac->ac_nstimeout = 1000; 517*b44da627Seric ac->ac_nsretries = 3; 518*b44da627Seric 519*b44da627Seric return (ac); 520*b44da627Seric } 521*b44da627Seric 522*b44da627Seric /* 523*b44da627Seric * Add a search domain to the async context. 524*b44da627Seric */ 525*b44da627Seric static int 526*b44da627Seric asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) 527*b44da627Seric { 528*b44da627Seric char buf[MAXDNAME]; 529*b44da627Seric 530*b44da627Seric if (ac->ac_domcount == ASR_MAXDOM) 531*b44da627Seric return (-1); 532*b44da627Seric 533*b44da627Seric if (asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) 534*b44da627Seric return (-1); 535*b44da627Seric 536*b44da627Seric if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) 537*b44da627Seric return (0); 538*b44da627Seric 539*b44da627Seric ac->ac_domcount += 1; 540*b44da627Seric 541*b44da627Seric return (1); 542*b44da627Seric } 543*b44da627Seric 544*b44da627Seric /* 545*b44da627Seric * Pass on a split config line. 546*b44da627Seric */ 547*b44da627Seric static int 548*b44da627Seric pass0(char **tok, int n, struct asr_ctx *ac) 549*b44da627Seric { 550*b44da627Seric int i, j, d; 551*b44da627Seric const char *e; 552*b44da627Seric struct sockaddr_storage ss; 553*b44da627Seric 554*b44da627Seric if (!strcmp(tok[0], "nameserver")) { 555*b44da627Seric if (ac->ac_nscount == ASR_MAXNS) 556*b44da627Seric return (0); 557*b44da627Seric if (n != 2) 558*b44da627Seric return (0); 559*b44da627Seric if (asr_parse_nameserver((struct sockaddr*)&ss, tok[1])) 560*b44da627Seric return (0); 561*b44da627Seric if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL) 562*b44da627Seric return (0); 563*b44da627Seric memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len); 564*b44da627Seric ac->ac_nscount += 1; 565*b44da627Seric 566*b44da627Seric } else if (!strcmp(tok[0], "domain")) { 567*b44da627Seric if (n != 2) 568*b44da627Seric return (0); 569*b44da627Seric if (ac->ac_domain) 570*b44da627Seric return (0); 571*b44da627Seric ac->ac_domain = strdup(tok[1]); 572*b44da627Seric 573*b44da627Seric } else if (!strcmp(tok[0], "lookup")) { 574*b44da627Seric /* ignore the line if we already set lookup */ 575*b44da627Seric if (ac->ac_dbcount != 0) 576*b44da627Seric return (0); 577*b44da627Seric if (n - 1 > ASR_MAXDB) 578*b44da627Seric return (0); 579*b44da627Seric /* ensure that each lookup is only given once */ 580*b44da627Seric for(i = 1; i < n; i++) 581*b44da627Seric for(j = i + 1; j < n; j++) 582*b44da627Seric if (!strcmp(tok[i], tok[j])) 583*b44da627Seric return (0); 584*b44da627Seric for(i = 1; i < n; i++, ac->ac_dbcount++) { 585*b44da627Seric if (!strcmp(tok[i], "yp")) { 586*b44da627Seric ac->ac_db[i-1] = ASR_DB_YP; 587*b44da627Seric } else if (!strcmp(tok[i], "bind")) { 588*b44da627Seric ac->ac_db[i-1] = ASR_DB_DNS; 589*b44da627Seric } else if (!strcmp(tok[i], "file")) { 590*b44da627Seric ac->ac_db[i-1] = ASR_DB_FILE; 591*b44da627Seric } else { 592*b44da627Seric /* ignore the line */ 593*b44da627Seric ac->ac_dbcount = 0; 594*b44da627Seric return (0); 595*b44da627Seric } 596*b44da627Seric } 597*b44da627Seric } else if (!strcmp(tok[0], "search")) { 598*b44da627Seric /* resolv.conf says the last line wins */ 599*b44da627Seric for(i = 0; i < ac->ac_domcount; i++) 600*b44da627Seric free(ac->ac_dom[i]); 601*b44da627Seric ac->ac_domcount = 0; 602*b44da627Seric for(i = 1; i < n; i++) 603*b44da627Seric asr_ctx_add_searchdomain(ac, tok[i]); 604*b44da627Seric 605*b44da627Seric } else if (!strcmp(tok[0], "family")) { 606*b44da627Seric if (n == 1 || n > 3) 607*b44da627Seric return (0); 608*b44da627Seric for (i = 1; i < n; i++) 609*b44da627Seric if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) 610*b44da627Seric return (0); 611*b44da627Seric for (i = 1; i < n; i++) 612*b44da627Seric ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ 613*b44da627Seric AF_INET6 : AF_INET; 614*b44da627Seric ac->ac_family[i - 1] = -1; 615*b44da627Seric 616*b44da627Seric } else if (!strcmp(tok[0], "options")) { 617*b44da627Seric for(i = 1; i < n; i++) { 618*b44da627Seric if (!strcmp(tok[i], "tcp")) 619*b44da627Seric ac->ac_options |= RES_USEVC; 620*b44da627Seric else if ((!strncmp(tok[i], "ndots:", 6))) { 621*b44da627Seric e = NULL; 622*b44da627Seric d = strtonum(tok[i] + 6, 1, 16, &e); 623*b44da627Seric if (e == NULL) 624*b44da627Seric ac->ac_ndots = d; 625*b44da627Seric } 626*b44da627Seric } 627*b44da627Seric } 628*b44da627Seric 629*b44da627Seric return (0); 630*b44da627Seric } 631*b44da627Seric 632*b44da627Seric /* 633*b44da627Seric * Setup an async context with the config specified in the string "str". 634*b44da627Seric */ 635*b44da627Seric static int 636*b44da627Seric asr_ctx_from_string(struct asr_ctx *ac, const char *str) 637*b44da627Seric { 638*b44da627Seric char buf[512], *ch; 639*b44da627Seric 640*b44da627Seric asr_ctx_parse(str, pass0, ac); 641*b44da627Seric 642*b44da627Seric if (ac->ac_dbcount == 0) { 643*b44da627Seric /* No lookup directive */ 644*b44da627Seric asr_ctx_parse(DEFAULT_LOOKUP, pass0, ac); 645*b44da627Seric } 646*b44da627Seric 647*b44da627Seric if (ac->ac_nscount == 0) 648*b44da627Seric asr_ctx_parse("nameserver 127.0.0.1", pass0, ac); 649*b44da627Seric 650*b44da627Seric if (ac->ac_domain == NULL) 651*b44da627Seric if (gethostname(buf, sizeof buf) == 0) { 652*b44da627Seric ch = strchr(buf, '.'); 653*b44da627Seric if (ch) 654*b44da627Seric ac->ac_domain = strdup(ch + 1); 655*b44da627Seric else /* Assume root. see resolv.conf(5) */ 656*b44da627Seric ac->ac_domain = strdup(""); 657*b44da627Seric } 658*b44da627Seric 659*b44da627Seric /* If no search domain was specified, use the local subdomains */ 660*b44da627Seric if (ac->ac_domcount == 0) 661*b44da627Seric for(ch = ac->ac_domain; ch; ) { 662*b44da627Seric asr_ctx_add_searchdomain(ac, ch); 663*b44da627Seric ch = strchr(ch, '.'); 664*b44da627Seric if (ch && asr_ndots(++ch) == 0) 665*b44da627Seric break; 666*b44da627Seric } 667*b44da627Seric 668*b44da627Seric return (0); 669*b44da627Seric } 670*b44da627Seric 671*b44da627Seric /* 672*b44da627Seric * Setup the "ac" async context from the file at location "path". 673*b44da627Seric */ 674*b44da627Seric static int 675*b44da627Seric asr_ctx_from_file(struct asr_ctx *ac, const char *path) 676*b44da627Seric { 677*b44da627Seric FILE *cf; 678*b44da627Seric char buf[4096]; 679*b44da627Seric ssize_t r; 680*b44da627Seric 681*b44da627Seric cf = fopen(path, "r"); 682*b44da627Seric if (cf == NULL) 683*b44da627Seric return (-1); 684*b44da627Seric 685*b44da627Seric r = fread(buf, 1, sizeof buf - 1, cf); 686*b44da627Seric if (feof(cf) == 0) { 687*b44da627Seric #ifdef DEBUG 688*b44da627Seric asr_printf("asr: config file too long: \"%s\"\n", path); 689*b44da627Seric #endif 690*b44da627Seric r = -1; 691*b44da627Seric } 692*b44da627Seric fclose(cf); 693*b44da627Seric if (r == -1) 694*b44da627Seric return (-1); 695*b44da627Seric buf[r] = '\0'; 696*b44da627Seric 697*b44da627Seric return asr_ctx_from_string(ac, buf); 698*b44da627Seric } 699*b44da627Seric 700*b44da627Seric /* 701*b44da627Seric * Parse a configuration string. Lines are read one by one, comments are 702*b44da627Seric * stripped and the remaining line is split into tokens which are passed 703*b44da627Seric * to the "cb" callback function. Parsing stops if the callback returns 704*b44da627Seric * non-zero. 705*b44da627Seric */ 706*b44da627Seric static int 707*b44da627Seric asr_ctx_parse(const char *str, int (*cb)(char**, int, struct asr_ctx*), 708*b44da627Seric struct asr_ctx *ac) 709*b44da627Seric { 710*b44da627Seric size_t len; 711*b44da627Seric const char *line; 712*b44da627Seric char buf[1024]; 713*b44da627Seric char *tok[10], **tp, *cp; 714*b44da627Seric int ntok; 715*b44da627Seric 716*b44da627Seric line = str; 717*b44da627Seric while (*line) { 718*b44da627Seric len = strcspn(line, "\n\0"); 719*b44da627Seric if (len < sizeof buf) { 720*b44da627Seric memmove(buf, line, len); 721*b44da627Seric buf[len] = '\0'; 722*b44da627Seric } else 723*b44da627Seric buf[0] = '\0'; 724*b44da627Seric line += len; 725*b44da627Seric if (*line == '\n') 726*b44da627Seric line++; 727*b44da627Seric buf[strcspn(buf, ";#")] = '\0'; 728*b44da627Seric for(cp = buf, tp = tok, ntok = 0; 729*b44da627Seric tp < &tok[10] && (*tp = strsep(&cp, " \t")) != NULL; ) 730*b44da627Seric if (**tp != '\0') { 731*b44da627Seric tp++; 732*b44da627Seric ntok++; 733*b44da627Seric } 734*b44da627Seric *tp = NULL; 735*b44da627Seric 736*b44da627Seric if (tok[0] == NULL) 737*b44da627Seric continue; 738*b44da627Seric 739*b44da627Seric if (cb(tok, ntok, ac)) 740*b44da627Seric break; 741*b44da627Seric } 742*b44da627Seric 743*b44da627Seric return (0); 744*b44da627Seric } 745*b44da627Seric 746*b44da627Seric /* 747*b44da627Seric * Check for environment variables altering the configuration as described 748*b44da627Seric * in resolv.conf(5). Altough not documented there, this feature is disabled 749*b44da627Seric * for setuid/setgid programs. 750*b44da627Seric */ 751*b44da627Seric static void 752*b44da627Seric asr_ctx_envopts(struct asr_ctx *ac) 753*b44da627Seric { 754*b44da627Seric char buf[4096], *e; 755*b44da627Seric size_t s; 756*b44da627Seric 757*b44da627Seric if (issetugid()) { 758*b44da627Seric ac->ac_options |= RES_NOALIASES; 759*b44da627Seric return; 760*b44da627Seric } 761*b44da627Seric 762*b44da627Seric if ((e = getenv("RES_OPTIONS")) != NULL) { 763*b44da627Seric strlcpy(buf, "options ", sizeof buf); 764*b44da627Seric strlcat(buf, e, sizeof buf); 765*b44da627Seric s = strlcat(buf, "\n", sizeof buf); 766*b44da627Seric s = strlcat(buf, "\n", sizeof buf); 767*b44da627Seric if (s < sizeof buf) 768*b44da627Seric asr_ctx_parse(buf, pass0, ac); 769*b44da627Seric } 770*b44da627Seric 771*b44da627Seric if ((e = getenv("LOCALDOMAIN")) != NULL) { 772*b44da627Seric strlcpy(buf, "search ", sizeof buf); 773*b44da627Seric strlcat(buf, e, sizeof buf); 774*b44da627Seric s = strlcat(buf, "\n", sizeof buf); 775*b44da627Seric if (s < sizeof buf) 776*b44da627Seric asr_ctx_parse(buf, pass0, ac); 777*b44da627Seric } 778*b44da627Seric } 779*b44da627Seric 780*b44da627Seric /* 781*b44da627Seric * Parse a resolv.conf(5) nameserver string into a sockaddr. 782*b44da627Seric */ 783*b44da627Seric static int 784*b44da627Seric asr_parse_nameserver(struct sockaddr *sa, const char *s) 785*b44da627Seric { 786*b44da627Seric const char *estr; 787*b44da627Seric char buf[256]; 788*b44da627Seric char *port = NULL; 789*b44da627Seric in_port_t portno = 53; 790*b44da627Seric 791*b44da627Seric if (*s == '[') { 792*b44da627Seric strlcpy(buf, s + 1, sizeof buf); 793*b44da627Seric s = buf; 794*b44da627Seric port = strchr(buf, ']'); 795*b44da627Seric if (port == NULL) 796*b44da627Seric return (-1); 797*b44da627Seric *port++ = '\0'; 798*b44da627Seric if (*port != ':') 799*b44da627Seric return (-1); 800*b44da627Seric port++; 801*b44da627Seric } 802*b44da627Seric 803*b44da627Seric if (port) { 804*b44da627Seric portno = strtonum(port, 1, USHRT_MAX, &estr); 805*b44da627Seric if (estr) 806*b44da627Seric return (-1); 807*b44da627Seric } 808*b44da627Seric 809*b44da627Seric if (sockaddr_from_str(sa, PF_UNSPEC, s) == -1) 810*b44da627Seric return (-1); 811*b44da627Seric 812*b44da627Seric if (sa->sa_family == PF_INET) 813*b44da627Seric ((struct sockaddr_in *)sa)->sin_port = htons(portno); 814*b44da627Seric else if (sa->sa_family == PF_INET6) 815*b44da627Seric ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); 816*b44da627Seric 817*b44da627Seric return (0); 818*b44da627Seric } 819*b44da627Seric 820*b44da627Seric /* 821*b44da627Seric * Turn a (uncompressed) DNS domain name into a regular nul-terminated string 822*b44da627Seric * where labels are separated by dots. The result is put into the "buf" buffer, 823*b44da627Seric * truncated if it exceeds "max" chars. The function returns "buf". 824*b44da627Seric */ 825*b44da627Seric char* 826*b44da627Seric asr_strdname(const char *_dname, char *buf, size_t max) 827*b44da627Seric { 828*b44da627Seric const unsigned char *dname = _dname; 829*b44da627Seric char *res; 830*b44da627Seric size_t left, n, count; 831*b44da627Seric 832*b44da627Seric if (_dname[0] == 0) { 833*b44da627Seric strlcpy(buf, ".", max); 834*b44da627Seric return buf; 835*b44da627Seric } 836*b44da627Seric 837*b44da627Seric res = buf; 838*b44da627Seric left = max - 1; 839*b44da627Seric for (n = 0; dname[0] && left; n += dname[0]) { 840*b44da627Seric count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 841*b44da627Seric memmove(buf, dname + 1, count); 842*b44da627Seric dname += dname[0] + 1; 843*b44da627Seric left -= count; 844*b44da627Seric buf += count; 845*b44da627Seric if (left) { 846*b44da627Seric left -= 1; 847*b44da627Seric *buf++ = '.'; 848*b44da627Seric } 849*b44da627Seric } 850*b44da627Seric buf[0] = 0; 851*b44da627Seric 852*b44da627Seric return (res); 853*b44da627Seric } 854*b44da627Seric 855*b44da627Seric /* 856*b44da627Seric * Read and split the next line from the given namedb file. 857*b44da627Seric * Return -1 on error, or put the result in the "tokens" array of 858*b44da627Seric * size "ntoken" and returns the number of token on the line. 859*b44da627Seric */ 860*b44da627Seric int 861*b44da627Seric asr_parse_namedb_line(FILE *file, char **tokens, int ntoken) 862*b44da627Seric { 863*b44da627Seric size_t len; 864*b44da627Seric char *buf, *cp, **tp; 865*b44da627Seric int ntok; 866*b44da627Seric 867*b44da627Seric again: 868*b44da627Seric if ((buf = fgetln(file, &len)) == NULL) 869*b44da627Seric return (-1); 870*b44da627Seric 871*b44da627Seric if (buf[len - 1] == '\n') 872*b44da627Seric len--; 873*b44da627Seric 874*b44da627Seric buf[len] = '\0'; 875*b44da627Seric buf[strcspn(buf, "#")] = '\0'; 876*b44da627Seric for(cp = buf, tp = tokens, ntok = 0; 877*b44da627Seric ntok < ntoken && (*tp = strsep(&cp, " \t")) != NULL;) 878*b44da627Seric if (**tp != '\0') { 879*b44da627Seric tp++; 880*b44da627Seric ntok++; 881*b44da627Seric } 882*b44da627Seric *tp = NULL; 883*b44da627Seric if (tokens[0] == NULL) 884*b44da627Seric goto again; 885*b44da627Seric 886*b44da627Seric return (ntok); 887*b44da627Seric } 888*b44da627Seric 889*b44da627Seric /* 890*b44da627Seric * Update the async context so that it uses the next configured DB. 891*b44da627Seric * Return 0 on success, or -1 if no more DBs is available. 892*b44da627Seric */ 893*b44da627Seric int 894*b44da627Seric asr_iter_db(struct async *as) 895*b44da627Seric { 896*b44da627Seric if (as->as_db_idx >= as->as_ctx->ac_dbcount) { 897*b44da627Seric #if DEBUG 898*b44da627Seric asr_printf("asr_iter_db: done\n"); 899*b44da627Seric #endif 900*b44da627Seric return (-1); 901*b44da627Seric } 902*b44da627Seric 903*b44da627Seric as->as_db_idx += 1; 904*b44da627Seric as->as_ns_idx = 0; 905*b44da627Seric #if DEBUG 906*b44da627Seric asr_printf("asr_iter_db: %i\n", as->as_db_idx); 907*b44da627Seric #endif 908*b44da627Seric return (0); 909*b44da627Seric } 910*b44da627Seric 911*b44da627Seric /* 912*b44da627Seric * Set the async context nameserver index to the next nameserver of the 913*b44da627Seric * currently used DB (assuming it is DNS), cycling over the list until the 914*b44da627Seric * maximum retry counter is reached. Return 0 on success, or -1 if all 915*b44da627Seric * nameservers were used. 916*b44da627Seric */ 917*b44da627Seric int 918*b44da627Seric asr_iter_ns(struct async *as) 919*b44da627Seric { 920*b44da627Seric for (;;) { 921*b44da627Seric if (as->as_ns_cycles >= as->as_ctx->ac_nsretries) 922*b44da627Seric return (-1); 923*b44da627Seric 924*b44da627Seric as->as_ns_idx += 1; 925*b44da627Seric if (as->as_ns_idx <= as->as_ctx->ac_nscount) 926*b44da627Seric break; 927*b44da627Seric as->as_ns_idx = 0; 928*b44da627Seric as->as_ns_cycles++; 929*b44da627Seric #if DEBUG 930*b44da627Seric asr_printf("asr: asr_iter_ns(): cycle %i\n", as->as_ns_cycles); 931*b44da627Seric #endif 932*b44da627Seric } 933*b44da627Seric 934*b44da627Seric return (0); 935*b44da627Seric } 936*b44da627Seric 937*b44da627Seric enum { 938*b44da627Seric DOM_INIT, 939*b44da627Seric DOM_DOMAIN, 940*b44da627Seric DOM_DONE 941*b44da627Seric }; 942*b44da627Seric 943*b44da627Seric /* 944*b44da627Seric * Implement the search domain strategy. 945*b44da627Seric * 946*b44da627Seric * This function works as a generator that constructs complete domains in 947*b44da627Seric * buffer "buf" of size "len" for the given host name "name", according to the 948*b44da627Seric * search rules defined by the resolving context. It is supposed to be called 949*b44da627Seric * multiple times (with the same name) to generate the next possible domain 950*b44da627Seric * name, if any. 951*b44da627Seric * 952*b44da627Seric * It returns 0 if it could generate a new domain name, or -1 when all 953*b44da627Seric * possibilites have been exhausted. 954*b44da627Seric */ 955*b44da627Seric int 956*b44da627Seric asr_iter_domain(struct async *as, const char *name, char * buf, size_t len) 957*b44da627Seric { 958*b44da627Seric char *alias; 959*b44da627Seric 960*b44da627Seric switch(as->as_dom_step) { 961*b44da627Seric 962*b44da627Seric case DOM_INIT: 963*b44da627Seric /* First call */ 964*b44da627Seric 965*b44da627Seric /* 966*b44da627Seric * If "name" is an FQDN, that's the only result and we 967*b44da627Seric * don't try anything else. 968*b44da627Seric */ 969*b44da627Seric if (strlen(name) && name[strlen(name) - 1] == '.') { 970*b44da627Seric #if DEBUG 971*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") fqdn\n", name); 972*b44da627Seric #endif 973*b44da627Seric as->as_dom_flags |= ASYNC_DOM_FQDN; 974*b44da627Seric as->as_dom_step = DOM_DONE; 975*b44da627Seric return (asr_domcat(name, NULL, buf, len)); 976*b44da627Seric } 977*b44da627Seric 978*b44da627Seric /* 979*b44da627Seric * If "name" has no dots, it might be an alias. If so, 980*b44da627Seric * That's also the only result. 981*b44da627Seric */ 982*b44da627Seric if ((as->as_ctx->ac_options & RES_NOALIASES) == 0 && 983*b44da627Seric asr_ndots(name) == 0 && 984*b44da627Seric (alias = asr_hostalias(name, buf, len)) != NULL) { 985*b44da627Seric #if DEBUG 986*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") is alias " 987*b44da627Seric "\"%s\"\n", name, alias); 988*b44da627Seric #endif 989*b44da627Seric as->as_dom_flags |= ASYNC_DOM_HOSTALIAS; 990*b44da627Seric as->as_dom_step = DOM_DONE; 991*b44da627Seric return (asr_domcat(alias, NULL, buf, len)); 992*b44da627Seric } 993*b44da627Seric 994*b44da627Seric /* 995*b44da627Seric * Otherwise, we iterate through the specified search domains. 996*b44da627Seric */ 997*b44da627Seric as->as_dom_step = DOM_DOMAIN; 998*b44da627Seric as->as_dom_idx = 0; 999*b44da627Seric 1000*b44da627Seric /* 1001*b44da627Seric * If "name" as enough dots, use it as-is first, as indicated 1002*b44da627Seric * in resolv.conf(5). 1003*b44da627Seric */ 1004*b44da627Seric if ((asr_ndots(name)) >= as->as_ctx->ac_ndots) { 1005*b44da627Seric #ifdef DEBUG 1006*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") ndots\n", 1007*b44da627Seric name); 1008*b44da627Seric #endif 1009*b44da627Seric as->as_dom_flags |= ASYNC_DOM_NDOTS; 1010*b44da627Seric strlcpy(buf, name, len); 1011*b44da627Seric return (0); 1012*b44da627Seric } 1013*b44da627Seric /* Otherwise, starts using the search domains */ 1014*b44da627Seric /* FALLTHROUGH */ 1015*b44da627Seric 1016*b44da627Seric case DOM_DOMAIN: 1017*b44da627Seric if (as->as_dom_idx < as->as_ctx->ac_domcount) { 1018*b44da627Seric #ifdef DEBUG 1019*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") " 1020*b44da627Seric "domain \"%s\"\n", name, 1021*b44da627Seric as->as_ctx->ac_dom[as->as_dom_idx]); 1022*b44da627Seric #endif 1023*b44da627Seric as->as_dom_flags |= ASYNC_DOM_DOMAIN; 1024*b44da627Seric return (asr_domcat(name, 1025*b44da627Seric as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); 1026*b44da627Seric } 1027*b44da627Seric 1028*b44da627Seric /* No more domain to try. */ 1029*b44da627Seric 1030*b44da627Seric as->as_dom_step = DOM_DONE; 1031*b44da627Seric 1032*b44da627Seric /* 1033*b44da627Seric * If the name was not tried as an absolute name before, 1034*b44da627Seric * do it now. 1035*b44da627Seric */ 1036*b44da627Seric if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) { 1037*b44da627Seric #ifdef DEBUG 1038*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") as is\n", 1039*b44da627Seric name); 1040*b44da627Seric #endif 1041*b44da627Seric as->as_dom_flags |= ASYNC_DOM_ASIS; 1042*b44da627Seric strlcpy(buf, name, len); 1043*b44da627Seric return (0); 1044*b44da627Seric } 1045*b44da627Seric /* Otherwise, we are done. */ 1046*b44da627Seric 1047*b44da627Seric case DOM_DONE: 1048*b44da627Seric default: 1049*b44da627Seric #ifdef DEBUG 1050*b44da627Seric asr_printf("asr: asr_iter_domain(\"%s\") done\n", name); 1051*b44da627Seric #endif 1052*b44da627Seric return (-1); 1053*b44da627Seric } 1054*b44da627Seric } 1055*b44da627Seric 1056*b44da627Seric /* 1057*b44da627Seric * Check if the hostname "name" is a user-defined alias as per hostname(7). 1058*b44da627Seric * If so, copies the result in the buffer "abuf" of size "abufsz" and 1059*b44da627Seric * return "abuf". Otherwise return NULL. 1060*b44da627Seric */ 1061*b44da627Seric static char * 1062*b44da627Seric asr_hostalias(const char *name, char *abuf, size_t abufsz) 1063*b44da627Seric { 1064*b44da627Seric FILE *fp; 1065*b44da627Seric size_t len; 1066*b44da627Seric char *file, *buf, *cp, **tp, *tokens[2]; 1067*b44da627Seric int ntok; 1068*b44da627Seric 1069*b44da627Seric file = getenv("HOSTALIASES"); 1070*b44da627Seric if (file == NULL || issetugid() != 0 || (fp = fopen(file, "r")) == NULL) 1071*b44da627Seric return (NULL); 1072*b44da627Seric 1073*b44da627Seric #ifdef DEBUG 1074*b44da627Seric asr_printf("asr: looking up aliases in \"%s\"\n", file); 1075*b44da627Seric #endif 1076*b44da627Seric 1077*b44da627Seric while ((buf = fgetln(fp, &len)) != NULL) { 1078*b44da627Seric if (buf[len - 1] == '\n') 1079*b44da627Seric len--; 1080*b44da627Seric buf[len] = '\0'; 1081*b44da627Seric for(cp = buf, tp = tokens, ntok = 0; 1082*b44da627Seric ntok < 2 && (*tp = strsep(&cp, " \t")) != NULL; ) 1083*b44da627Seric if (**tp != '\0') { 1084*b44da627Seric tp++; 1085*b44da627Seric ntok++; 1086*b44da627Seric } 1087*b44da627Seric if (ntok != 2) 1088*b44da627Seric continue; 1089*b44da627Seric if (!strcasecmp(tokens[0], name)) { 1090*b44da627Seric if (strlcpy(abuf, tokens[1], abufsz) > abufsz) 1091*b44da627Seric continue; 1092*b44da627Seric #ifdef DEBUG 1093*b44da627Seric asr_printf("asr: found alias \"%s\"\n", abuf); 1094*b44da627Seric #endif 1095*b44da627Seric fclose(fp); 1096*b44da627Seric return (abuf); 1097*b44da627Seric } 1098*b44da627Seric } 1099*b44da627Seric 1100*b44da627Seric fclose(fp); 1101*b44da627Seric return (NULL); 1102*b44da627Seric } 1103