1c2aa98e2SPeter Wemm /* 206f25ae9SGregory Neil Shapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 306f25ae9SGregory Neil Shapiro * All rights reserved. 4c2aa98e2SPeter Wemm * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 5c2aa98e2SPeter Wemm * Copyright (c) 1988, 1993 6c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 7c2aa98e2SPeter Wemm * 8c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set 9c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of 10c2aa98e2SPeter Wemm * the sendmail distribution. 11c2aa98e2SPeter Wemm * 12c2aa98e2SPeter Wemm */ 13c2aa98e2SPeter Wemm 1406f25ae9SGregory Neil Shapiro #include <sendmail.h> 15c2aa98e2SPeter Wemm 16c2aa98e2SPeter Wemm #ifndef lint 17c2aa98e2SPeter Wemm # if NAMED_BIND 1806f25ae9SGregory Neil Shapiro static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.3 2000/06/13 18:00:08 gshapiro Exp $ (with name server)"; 1906f25ae9SGregory Neil Shapiro # else /* NAMED_BIND */ 2006f25ae9SGregory Neil Shapiro static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.3 2000/06/13 18:00:08 gshapiro Exp $ (without name server)"; 2106f25ae9SGregory Neil Shapiro # endif /* NAMED_BIND */ 2206f25ae9SGregory Neil Shapiro #endif /* ! lint */ 2306f25ae9SGregory Neil Shapiro 24c2aa98e2SPeter Wemm 25c2aa98e2SPeter Wemm #if NAMED_BIND 26c2aa98e2SPeter Wemm 27c2aa98e2SPeter Wemm # include <arpa/inet.h> 28c2aa98e2SPeter Wemm 29c2aa98e2SPeter Wemm /* 30c2aa98e2SPeter Wemm ** The standard udp packet size PACKETSZ (512) is not sufficient for some 31c2aa98e2SPeter Wemm ** nameserver answers containing very many resource records. The resolver 32c2aa98e2SPeter Wemm ** may switch to tcp and retry if it detects udp packet overflow. 33c2aa98e2SPeter Wemm ** Also note that the resolver routines res_query and res_search return 34c2aa98e2SPeter Wemm ** the size of the *un*truncated answer in case the supplied answer buffer 35c2aa98e2SPeter Wemm ** it not big enough to accommodate the entire answer. 36c2aa98e2SPeter Wemm */ 37c2aa98e2SPeter Wemm 38c2aa98e2SPeter Wemm # ifndef MAXPACKET 39c2aa98e2SPeter Wemm # define MAXPACKET 8192 /* max packet size used internally by BIND */ 4006f25ae9SGregory Neil Shapiro # endif /* ! MAXPACKET */ 41c2aa98e2SPeter Wemm 42c2aa98e2SPeter Wemm typedef union 43c2aa98e2SPeter Wemm { 44c2aa98e2SPeter Wemm HEADER qb1; 45c2aa98e2SPeter Wemm u_char qb2[MAXPACKET]; 46c2aa98e2SPeter Wemm } querybuf; 47c2aa98e2SPeter Wemm 48c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE 49c2aa98e2SPeter Wemm # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 5006f25ae9SGregory Neil Shapiro # endif /* ! MXHOSTBUFSIZE */ 51c2aa98e2SPeter Wemm 52c2aa98e2SPeter Wemm static char MXHostBuf[MXHOSTBUFSIZE]; 53c2aa98e2SPeter Wemm 54c2aa98e2SPeter Wemm # ifndef MAXDNSRCH 55c2aa98e2SPeter Wemm # define MAXDNSRCH 6 /* number of possible domains to search */ 5606f25ae9SGregory Neil Shapiro # endif /* ! MAXDNSRCH */ 5706f25ae9SGregory Neil Shapiro 5806f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE 5906f25ae9SGregory Neil Shapiro # define RES_DNSRCH_VARIABLE _res.dnsrch 6006f25ae9SGregory Neil Shapiro # endif /* ! RES_DNSRCH_VARIABLE */ 61c2aa98e2SPeter Wemm 62c2aa98e2SPeter Wemm # ifndef MAX 63c2aa98e2SPeter Wemm # define MAX(a, b) ((a) > (b) ? (a) : (b)) 6406f25ae9SGregory Neil Shapiro # endif /* ! MAX */ 65c2aa98e2SPeter Wemm 66c2aa98e2SPeter Wemm # ifndef NO_DATA 67c2aa98e2SPeter Wemm # define NO_DATA NO_ADDRESS 6806f25ae9SGregory Neil Shapiro # endif /* ! NO_DATA */ 69c2aa98e2SPeter Wemm 70c2aa98e2SPeter Wemm # ifndef HFIXEDSZ 71c2aa98e2SPeter Wemm # define HFIXEDSZ 12 /* sizeof(HEADER) */ 7206f25ae9SGregory Neil Shapiro # endif /* ! HFIXEDSZ */ 73c2aa98e2SPeter Wemm 74c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 75c2aa98e2SPeter Wemm 76c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415) 77c2aa98e2SPeter Wemm # define RES_UNC_T char * 7806f25ae9SGregory Neil Shapiro # else /* defined(__RES) && (__RES >= 19940415) */ 79c2aa98e2SPeter Wemm # define RES_UNC_T u_char * 8006f25ae9SGregory Neil Shapiro # endif /* defined(__RES) && (__RES >= 19940415) */ 8106f25ae9SGregory Neil Shapiro 8206f25ae9SGregory Neil Shapiro static char *gethostalias __P((char *)); 8306f25ae9SGregory Neil Shapiro static int mxrand __P((char *)); 8406f25ae9SGregory Neil Shapiro 85c2aa98e2SPeter Wemm /* 86c2aa98e2SPeter Wemm ** GETMXRR -- get MX resource records for a domain 87c2aa98e2SPeter Wemm ** 88c2aa98e2SPeter Wemm ** Parameters: 89c2aa98e2SPeter Wemm ** host -- the name of the host to MX. 90c2aa98e2SPeter Wemm ** mxhosts -- a pointer to a return buffer of MX records. 9106f25ae9SGregory Neil Shapiro ** mxprefs -- a pointer to a return buffer of MX preferences. 9206f25ae9SGregory Neil Shapiro ** If NULL, don't try to populate. 93c2aa98e2SPeter Wemm ** droplocalhost -- If TRUE, all MX records less preferred 94c2aa98e2SPeter Wemm ** than the local host (as determined by $=w) will 95c2aa98e2SPeter Wemm ** be discarded. 96c2aa98e2SPeter Wemm ** rcode -- a pointer to an EX_ status code. 97c2aa98e2SPeter Wemm ** 98c2aa98e2SPeter Wemm ** Returns: 99c2aa98e2SPeter Wemm ** The number of MX records found. 100c2aa98e2SPeter Wemm ** -1 if there is an internal failure. 101c2aa98e2SPeter Wemm ** If no MX records are found, mxhosts[0] is set to host 102c2aa98e2SPeter Wemm ** and 1 is returned. 103c2aa98e2SPeter Wemm */ 104c2aa98e2SPeter Wemm 105c2aa98e2SPeter Wemm int 10606f25ae9SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) 107c2aa98e2SPeter Wemm char *host; 108c2aa98e2SPeter Wemm char **mxhosts; 10906f25ae9SGregory Neil Shapiro u_short *mxprefs; 110c2aa98e2SPeter Wemm bool droplocalhost; 111c2aa98e2SPeter Wemm int *rcode; 112c2aa98e2SPeter Wemm { 113c2aa98e2SPeter Wemm register u_char *eom, *cp; 114c2aa98e2SPeter Wemm register int i, j, n; 115c2aa98e2SPeter Wemm int nmx = 0; 116c2aa98e2SPeter Wemm register char *bp; 117c2aa98e2SPeter Wemm HEADER *hp; 118c2aa98e2SPeter Wemm querybuf answer; 119c2aa98e2SPeter Wemm int ancount, qdcount, buflen; 120c2aa98e2SPeter Wemm bool seenlocal = FALSE; 121c2aa98e2SPeter Wemm u_short pref, type; 122c2aa98e2SPeter Wemm u_short localpref = 256; 123c2aa98e2SPeter Wemm char *fallbackMX = FallBackMX; 124c2aa98e2SPeter Wemm bool trycanon = FALSE; 12506f25ae9SGregory Neil Shapiro u_short *prefs; 126c2aa98e2SPeter Wemm int (*resfunc)(); 127c2aa98e2SPeter Wemm u_short prefer[MAXMXHOSTS]; 128c2aa98e2SPeter Wemm int weight[MAXMXHOSTS]; 12906f25ae9SGregory Neil Shapiro extern int res_query(), res_search(); 130c2aa98e2SPeter Wemm 131c2aa98e2SPeter Wemm if (tTd(8, 2)) 13206f25ae9SGregory Neil Shapiro dprintf("getmxrr(%s, droplocalhost=%d)\n", 13306f25ae9SGregory Neil Shapiro host, droplocalhost); 134c2aa98e2SPeter Wemm 135c2aa98e2SPeter Wemm if (fallbackMX != NULL && droplocalhost && 136c2aa98e2SPeter Wemm wordinclass(fallbackMX, 'w')) 137c2aa98e2SPeter Wemm { 138c2aa98e2SPeter Wemm /* don't use fallback for this pass */ 139c2aa98e2SPeter Wemm fallbackMX = NULL; 140c2aa98e2SPeter Wemm } 141c2aa98e2SPeter Wemm 142c2aa98e2SPeter Wemm *rcode = EX_OK; 143c2aa98e2SPeter Wemm 14406f25ae9SGregory Neil Shapiro if (mxprefs != NULL) 14506f25ae9SGregory Neil Shapiro prefs = mxprefs; 14606f25ae9SGregory Neil Shapiro else 14706f25ae9SGregory Neil Shapiro prefs = prefer; 14806f25ae9SGregory Neil Shapiro 14906f25ae9SGregory Neil Shapiro 150c2aa98e2SPeter Wemm /* efficiency hack -- numeric or non-MX lookups */ 151c2aa98e2SPeter Wemm if (host[0] == '[') 152c2aa98e2SPeter Wemm goto punt; 153c2aa98e2SPeter Wemm 154c2aa98e2SPeter Wemm /* 155c2aa98e2SPeter Wemm ** If we don't have MX records in our host switch, don't 156c2aa98e2SPeter Wemm ** try for MX records. Note that this really isn't "right", 157c2aa98e2SPeter Wemm ** since we might be set up to try NIS first and then DNS; 158c2aa98e2SPeter Wemm ** if the host is found in NIS we really shouldn't be doing 159c2aa98e2SPeter Wemm ** MX lookups. However, that should be a degenerate case. 160c2aa98e2SPeter Wemm */ 161c2aa98e2SPeter Wemm 162c2aa98e2SPeter Wemm if (!UseNameServer) 163c2aa98e2SPeter Wemm goto punt; 164c2aa98e2SPeter Wemm if (HasWildcardMX && ConfigLevel >= 6) 165c2aa98e2SPeter Wemm resfunc = res_query; 166c2aa98e2SPeter Wemm else 167c2aa98e2SPeter Wemm resfunc = res_search; 168c2aa98e2SPeter Wemm 169c2aa98e2SPeter Wemm errno = 0; 170c2aa98e2SPeter Wemm n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); 171c2aa98e2SPeter Wemm if (n < 0) 172c2aa98e2SPeter Wemm { 173c2aa98e2SPeter Wemm if (tTd(8, 1)) 17406f25ae9SGregory Neil Shapiro dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 175c2aa98e2SPeter Wemm (host == NULL) ? "<NULL>" : host, errno, h_errno); 176c2aa98e2SPeter Wemm switch (h_errno) 177c2aa98e2SPeter Wemm { 178c2aa98e2SPeter Wemm case NO_DATA: 179c2aa98e2SPeter Wemm trycanon = TRUE; 18006f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 181c2aa98e2SPeter Wemm 182c2aa98e2SPeter Wemm case NO_RECOVERY: 183c2aa98e2SPeter Wemm /* no MX data on this host */ 184c2aa98e2SPeter Wemm goto punt; 185c2aa98e2SPeter Wemm 186c2aa98e2SPeter Wemm case HOST_NOT_FOUND: 187c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH 188c2aa98e2SPeter Wemm case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 18906f25ae9SGregory Neil Shapiro # endif /* BROKEN_RES_SEARCH */ 190c2aa98e2SPeter Wemm /* host doesn't exist in DNS; might be in /etc/hosts */ 191c2aa98e2SPeter Wemm trycanon = TRUE; 192c2aa98e2SPeter Wemm *rcode = EX_NOHOST; 193c2aa98e2SPeter Wemm goto punt; 194c2aa98e2SPeter Wemm 195c2aa98e2SPeter Wemm case TRY_AGAIN: 196c2aa98e2SPeter Wemm case -1: 197c2aa98e2SPeter Wemm /* couldn't connect to the name server */ 198c2aa98e2SPeter Wemm if (fallbackMX != NULL) 199c2aa98e2SPeter Wemm { 200c2aa98e2SPeter Wemm /* name server is hosed -- push to fallback */ 20106f25ae9SGregory Neil Shapiro if (nmx > 0) 20206f25ae9SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1; 20306f25ae9SGregory Neil Shapiro else 20406f25ae9SGregory Neil Shapiro prefs[nmx] = 0; 205c2aa98e2SPeter Wemm mxhosts[nmx++] = fallbackMX; 206c2aa98e2SPeter Wemm return nmx; 207c2aa98e2SPeter Wemm } 208c2aa98e2SPeter Wemm /* it might come up later; better queue it up */ 209c2aa98e2SPeter Wemm *rcode = EX_TEMPFAIL; 210c2aa98e2SPeter Wemm break; 211c2aa98e2SPeter Wemm 212c2aa98e2SPeter Wemm default: 213c2aa98e2SPeter Wemm syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", 214c2aa98e2SPeter Wemm host, h_errno); 215c2aa98e2SPeter Wemm *rcode = EX_OSERR; 216c2aa98e2SPeter Wemm break; 217c2aa98e2SPeter Wemm } 218c2aa98e2SPeter Wemm 219c2aa98e2SPeter Wemm /* irreconcilable differences */ 22006f25ae9SGregory Neil Shapiro return -1; 221c2aa98e2SPeter Wemm } 222c2aa98e2SPeter Wemm 223c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 224c2aa98e2SPeter Wemm if (n > sizeof(answer)) 225c2aa98e2SPeter Wemm n = sizeof(answer); 226c2aa98e2SPeter Wemm 227c2aa98e2SPeter Wemm /* find first satisfactory answer */ 228c2aa98e2SPeter Wemm hp = (HEADER *)&answer; 229c2aa98e2SPeter Wemm cp = (u_char *)&answer + HFIXEDSZ; 230c2aa98e2SPeter Wemm eom = (u_char *)&answer + n; 23106f25ae9SGregory Neil Shapiro for (qdcount = ntohs((u_short)hp->qdcount); 23206f25ae9SGregory Neil Shapiro qdcount--; 23306f25ae9SGregory Neil Shapiro cp += n + QFIXEDSZ) 23406f25ae9SGregory Neil Shapiro { 235c2aa98e2SPeter Wemm if ((n = dn_skipname(cp, eom)) < 0) 236c2aa98e2SPeter Wemm goto punt; 23706f25ae9SGregory Neil Shapiro } 238c2aa98e2SPeter Wemm buflen = sizeof(MXHostBuf) - 1; 239c2aa98e2SPeter Wemm bp = MXHostBuf; 24006f25ae9SGregory Neil Shapiro ancount = ntohs((u_short)hp->ancount); 241c2aa98e2SPeter Wemm while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 242c2aa98e2SPeter Wemm { 243c2aa98e2SPeter Wemm if ((n = dn_expand((u_char *)&answer, 244c2aa98e2SPeter Wemm eom, cp, (RES_UNC_T) bp, buflen)) < 0) 245c2aa98e2SPeter Wemm break; 246c2aa98e2SPeter Wemm cp += n; 247c2aa98e2SPeter Wemm GETSHORT(type, cp); 248c2aa98e2SPeter Wemm cp += INT16SZ + INT32SZ; 249c2aa98e2SPeter Wemm GETSHORT(n, cp); 250c2aa98e2SPeter Wemm if (type != T_MX) 251c2aa98e2SPeter Wemm { 252c2aa98e2SPeter Wemm if (tTd(8, 8) || _res.options & RES_DEBUG) 25306f25ae9SGregory Neil Shapiro dprintf("unexpected answer type %d, size %d\n", 254c2aa98e2SPeter Wemm type, n); 255c2aa98e2SPeter Wemm cp += n; 256c2aa98e2SPeter Wemm continue; 257c2aa98e2SPeter Wemm } 258c2aa98e2SPeter Wemm GETSHORT(pref, cp); 259c2aa98e2SPeter Wemm if ((n = dn_expand((u_char *)&answer, eom, cp, 260c2aa98e2SPeter Wemm (RES_UNC_T) bp, buflen)) < 0) 261c2aa98e2SPeter Wemm break; 262c2aa98e2SPeter Wemm cp += n; 263c2aa98e2SPeter Wemm if (wordinclass(bp, 'w')) 264c2aa98e2SPeter Wemm { 265c2aa98e2SPeter Wemm if (tTd(8, 3)) 26606f25ae9SGregory Neil Shapiro dprintf("found localhost (%s) in MX list, pref=%d\n", 267c2aa98e2SPeter Wemm bp, pref); 268c2aa98e2SPeter Wemm if (droplocalhost) 269c2aa98e2SPeter Wemm { 270c2aa98e2SPeter Wemm if (!seenlocal || pref < localpref) 271c2aa98e2SPeter Wemm localpref = pref; 272c2aa98e2SPeter Wemm seenlocal = TRUE; 273c2aa98e2SPeter Wemm continue; 274c2aa98e2SPeter Wemm } 275c2aa98e2SPeter Wemm weight[nmx] = 0; 276c2aa98e2SPeter Wemm } 277c2aa98e2SPeter Wemm else 278c2aa98e2SPeter Wemm weight[nmx] = mxrand(bp); 27906f25ae9SGregory Neil Shapiro prefs[nmx] = pref; 280c2aa98e2SPeter Wemm mxhosts[nmx++] = bp; 281c2aa98e2SPeter Wemm n = strlen(bp); 282c2aa98e2SPeter Wemm bp += n; 283c2aa98e2SPeter Wemm if (bp[-1] != '.') 284c2aa98e2SPeter Wemm { 285c2aa98e2SPeter Wemm *bp++ = '.'; 286c2aa98e2SPeter Wemm n++; 287c2aa98e2SPeter Wemm } 288c2aa98e2SPeter Wemm *bp++ = '\0'; 289c2aa98e2SPeter Wemm buflen -= n + 1; 290c2aa98e2SPeter Wemm } 291c2aa98e2SPeter Wemm 292c2aa98e2SPeter Wemm /* sort the records */ 293c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 294c2aa98e2SPeter Wemm { 295c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 296c2aa98e2SPeter Wemm { 29706f25ae9SGregory Neil Shapiro if (prefs[i] > prefs[j] || 29806f25ae9SGregory Neil Shapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 299c2aa98e2SPeter Wemm { 300c2aa98e2SPeter Wemm register int temp; 301c2aa98e2SPeter Wemm register char *temp1; 302c2aa98e2SPeter Wemm 30306f25ae9SGregory Neil Shapiro temp = prefs[i]; 30406f25ae9SGregory Neil Shapiro prefs[i] = prefs[j]; 30506f25ae9SGregory Neil Shapiro prefs[j] = temp; 306c2aa98e2SPeter Wemm temp1 = mxhosts[i]; 307c2aa98e2SPeter Wemm mxhosts[i] = mxhosts[j]; 308c2aa98e2SPeter Wemm mxhosts[j] = temp1; 309c2aa98e2SPeter Wemm temp = weight[i]; 310c2aa98e2SPeter Wemm weight[i] = weight[j]; 311c2aa98e2SPeter Wemm weight[j] = temp; 312c2aa98e2SPeter Wemm } 313c2aa98e2SPeter Wemm } 31406f25ae9SGregory Neil Shapiro if (seenlocal && prefs[i] >= localpref) 315c2aa98e2SPeter Wemm { 316c2aa98e2SPeter Wemm /* truncate higher preference part of list */ 317c2aa98e2SPeter Wemm nmx = i; 318c2aa98e2SPeter Wemm } 319c2aa98e2SPeter Wemm } 320c2aa98e2SPeter Wemm 321c2aa98e2SPeter Wemm /* delete duplicates from list (yes, some bozos have duplicates) */ 322c2aa98e2SPeter Wemm for (i = 0; i < nmx - 1; ) 323c2aa98e2SPeter Wemm { 324c2aa98e2SPeter Wemm if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 325c2aa98e2SPeter Wemm i++; 326c2aa98e2SPeter Wemm else 327c2aa98e2SPeter Wemm { 328c2aa98e2SPeter Wemm /* compress out duplicate */ 329c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 33006f25ae9SGregory Neil Shapiro { 331c2aa98e2SPeter Wemm mxhosts[j] = mxhosts[j + 1]; 33206f25ae9SGregory Neil Shapiro prefs[j] = prefs[j + 1]; 33306f25ae9SGregory Neil Shapiro } 334c2aa98e2SPeter Wemm nmx--; 335c2aa98e2SPeter Wemm } 336c2aa98e2SPeter Wemm } 337c2aa98e2SPeter Wemm 338c2aa98e2SPeter Wemm if (nmx == 0) 339c2aa98e2SPeter Wemm { 340c2aa98e2SPeter Wemm punt: 34106f25ae9SGregory Neil Shapiro if (seenlocal) 342c2aa98e2SPeter Wemm { 34306f25ae9SGregory Neil Shapiro struct hostent *h = NULL; 34406f25ae9SGregory Neil Shapiro 345c2aa98e2SPeter Wemm /* 346c2aa98e2SPeter Wemm ** If we have deleted all MX entries, this is 347c2aa98e2SPeter Wemm ** an error -- we should NEVER send to a host that 348c2aa98e2SPeter Wemm ** has an MX, and this should have been caught 349c2aa98e2SPeter Wemm ** earlier in the config file. 350c2aa98e2SPeter Wemm ** 351c2aa98e2SPeter Wemm ** Some sites prefer to go ahead and try the 352c2aa98e2SPeter Wemm ** A record anyway; that case is handled by 353c2aa98e2SPeter Wemm ** setting TryNullMXList. I believe this is a 354c2aa98e2SPeter Wemm ** bad idea, but it's up to you.... 355c2aa98e2SPeter Wemm */ 356c2aa98e2SPeter Wemm 35706f25ae9SGregory Neil Shapiro if (TryNullMXList) 35806f25ae9SGregory Neil Shapiro { 35906f25ae9SGregory Neil Shapiro h_errno = 0; 36006f25ae9SGregory Neil Shapiro errno = 0; 36106f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET); 36206f25ae9SGregory Neil Shapiro if (h == NULL) 36306f25ae9SGregory Neil Shapiro { 36406f25ae9SGregory Neil Shapiro if (errno == ETIMEDOUT || 36506f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 36606f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 36706f25ae9SGregory Neil Shapiro UseNameServer)) 36806f25ae9SGregory Neil Shapiro { 36906f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 37006f25ae9SGregory Neil Shapiro return -1; 37106f25ae9SGregory Neil Shapiro } 37206f25ae9SGregory Neil Shapiro # if NETINET6 37306f25ae9SGregory Neil Shapiro h_errno = 0; 37406f25ae9SGregory Neil Shapiro errno = 0; 37506f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET6); 37606f25ae9SGregory Neil Shapiro if (h == NULL && 37706f25ae9SGregory Neil Shapiro (errno == ETIMEDOUT || 37806f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 37906f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 38006f25ae9SGregory Neil Shapiro UseNameServer))) 38106f25ae9SGregory Neil Shapiro { 38206f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 38306f25ae9SGregory Neil Shapiro return -1; 38406f25ae9SGregory Neil Shapiro } 38506f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 38606f25ae9SGregory Neil Shapiro } 38706f25ae9SGregory Neil Shapiro } 38806f25ae9SGregory Neil Shapiro 38906f25ae9SGregory Neil Shapiro if (h == NULL) 39006f25ae9SGregory Neil Shapiro { 391c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 392c2aa98e2SPeter Wemm syserr("MX list for %s points back to %s", 393c2aa98e2SPeter Wemm host, MyHostName); 394c2aa98e2SPeter Wemm return -1; 395c2aa98e2SPeter Wemm } 39606f25ae9SGregory Neil Shapiro } 397c2aa98e2SPeter Wemm if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) 398c2aa98e2SPeter Wemm { 399c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 400c2aa98e2SPeter Wemm syserr("Host name %s too long", 401c2aa98e2SPeter Wemm shortenstring(host, MAXSHORTSTR)); 402c2aa98e2SPeter Wemm return -1; 403c2aa98e2SPeter Wemm } 404c2aa98e2SPeter Wemm snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); 405c2aa98e2SPeter Wemm mxhosts[0] = MXHostBuf; 40606f25ae9SGregory Neil Shapiro prefs[0] = 0; 407c2aa98e2SPeter Wemm if (host[0] == '[') 408c2aa98e2SPeter Wemm { 409c2aa98e2SPeter Wemm register char *p; 41006f25ae9SGregory Neil Shapiro # if NETINET6 41106f25ae9SGregory Neil Shapiro struct sockaddr_in6 tmp6; 41206f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 413c2aa98e2SPeter Wemm 414c2aa98e2SPeter Wemm /* this may be an MX suppression-style address */ 415c2aa98e2SPeter Wemm p = strchr(MXHostBuf, ']'); 416c2aa98e2SPeter Wemm if (p != NULL) 417c2aa98e2SPeter Wemm { 418c2aa98e2SPeter Wemm *p = '\0'; 41906f25ae9SGregory Neil Shapiro 420c2aa98e2SPeter Wemm if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 421c2aa98e2SPeter Wemm { 422c2aa98e2SPeter Wemm nmx++; 423c2aa98e2SPeter Wemm *p = ']'; 424c2aa98e2SPeter Wemm } 42506f25ae9SGregory Neil Shapiro # if NETINET6 42606f25ae9SGregory Neil Shapiro else if (inet_pton(AF_INET6, &MXHostBuf[1], 42706f25ae9SGregory Neil Shapiro &tmp6.sin6_addr) == 1) 42806f25ae9SGregory Neil Shapiro { 42906f25ae9SGregory Neil Shapiro nmx++; 43006f25ae9SGregory Neil Shapiro *p = ']'; 43106f25ae9SGregory Neil Shapiro } 43206f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 433c2aa98e2SPeter Wemm else 434c2aa98e2SPeter Wemm { 435c2aa98e2SPeter Wemm trycanon = TRUE; 436c2aa98e2SPeter Wemm mxhosts[0]++; 437c2aa98e2SPeter Wemm } 438c2aa98e2SPeter Wemm } 439c2aa98e2SPeter Wemm } 440c2aa98e2SPeter Wemm if (trycanon && 441c2aa98e2SPeter Wemm getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) 442c2aa98e2SPeter Wemm { 443c2aa98e2SPeter Wemm bp = &MXHostBuf[strlen(MXHostBuf)]; 444c2aa98e2SPeter Wemm if (bp[-1] != '.') 445c2aa98e2SPeter Wemm { 446c2aa98e2SPeter Wemm *bp++ = '.'; 447c2aa98e2SPeter Wemm *bp = '\0'; 448c2aa98e2SPeter Wemm } 449c2aa98e2SPeter Wemm nmx = 1; 450c2aa98e2SPeter Wemm } 451c2aa98e2SPeter Wemm } 452c2aa98e2SPeter Wemm 453c2aa98e2SPeter Wemm /* if we have a default lowest preference, include that */ 454c2aa98e2SPeter Wemm if (fallbackMX != NULL && !seenlocal) 45506f25ae9SGregory Neil Shapiro { 45606f25ae9SGregory Neil Shapiro if (nmx > 0) 45706f25ae9SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1; 45806f25ae9SGregory Neil Shapiro else 45906f25ae9SGregory Neil Shapiro prefs[nmx] = 0; 460c2aa98e2SPeter Wemm mxhosts[nmx++] = fallbackMX; 46106f25ae9SGregory Neil Shapiro } 462c2aa98e2SPeter Wemm 46306f25ae9SGregory Neil Shapiro return nmx; 464c2aa98e2SPeter Wemm } 465c2aa98e2SPeter Wemm /* 466c2aa98e2SPeter Wemm ** MXRAND -- create a randomizer for equal MX preferences 467c2aa98e2SPeter Wemm ** 468c2aa98e2SPeter Wemm ** If two MX hosts have equal preferences we want to randomize 469c2aa98e2SPeter Wemm ** the selection. But in order for signatures to be the same, 470c2aa98e2SPeter Wemm ** we need to randomize the same way each time. This function 471c2aa98e2SPeter Wemm ** computes a pseudo-random hash function from the host name. 472c2aa98e2SPeter Wemm ** 473c2aa98e2SPeter Wemm ** Parameters: 474c2aa98e2SPeter Wemm ** host -- the name of the host. 475c2aa98e2SPeter Wemm ** 476c2aa98e2SPeter Wemm ** Returns: 477c2aa98e2SPeter Wemm ** A random but repeatable value based on the host name. 478c2aa98e2SPeter Wemm ** 479c2aa98e2SPeter Wemm ** Side Effects: 480c2aa98e2SPeter Wemm ** none. 481c2aa98e2SPeter Wemm */ 482c2aa98e2SPeter Wemm 48306f25ae9SGregory Neil Shapiro static int 484c2aa98e2SPeter Wemm mxrand(host) 485c2aa98e2SPeter Wemm register char *host; 486c2aa98e2SPeter Wemm { 487c2aa98e2SPeter Wemm int hfunc; 488c2aa98e2SPeter Wemm static unsigned int seed; 489c2aa98e2SPeter Wemm 490c2aa98e2SPeter Wemm if (seed == 0) 491c2aa98e2SPeter Wemm { 492c2aa98e2SPeter Wemm seed = (int) curtime() & 0xffff; 493c2aa98e2SPeter Wemm if (seed == 0) 494c2aa98e2SPeter Wemm seed++; 495c2aa98e2SPeter Wemm } 496c2aa98e2SPeter Wemm 497c2aa98e2SPeter Wemm if (tTd(17, 9)) 49806f25ae9SGregory Neil Shapiro dprintf("mxrand(%s)", host); 499c2aa98e2SPeter Wemm 500c2aa98e2SPeter Wemm hfunc = seed; 501c2aa98e2SPeter Wemm while (*host != '\0') 502c2aa98e2SPeter Wemm { 503c2aa98e2SPeter Wemm int c = *host++; 504c2aa98e2SPeter Wemm 505c2aa98e2SPeter Wemm if (isascii(c) && isupper(c)) 506c2aa98e2SPeter Wemm c = tolower(c); 507c2aa98e2SPeter Wemm hfunc = ((hfunc << 1) ^ c) % 2003; 508c2aa98e2SPeter Wemm } 509c2aa98e2SPeter Wemm 510c2aa98e2SPeter Wemm hfunc &= 0xff; 511c2aa98e2SPeter Wemm hfunc++; 512c2aa98e2SPeter Wemm 513c2aa98e2SPeter Wemm if (tTd(17, 9)) 51406f25ae9SGregory Neil Shapiro dprintf(" = %d\n", hfunc); 515c2aa98e2SPeter Wemm return hfunc; 516c2aa98e2SPeter Wemm } 517c2aa98e2SPeter Wemm /* 518c2aa98e2SPeter Wemm ** BESTMX -- find the best MX for a name 519c2aa98e2SPeter Wemm ** 520c2aa98e2SPeter Wemm ** This is really a hack, but I don't see any obvious way 521c2aa98e2SPeter Wemm ** to generalize it at the moment. 522c2aa98e2SPeter Wemm */ 523c2aa98e2SPeter Wemm 524c2aa98e2SPeter Wemm /* ARGSUSED3 */ 525c2aa98e2SPeter Wemm char * 526c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp) 527c2aa98e2SPeter Wemm MAP *map; 528c2aa98e2SPeter Wemm char *name; 529c2aa98e2SPeter Wemm char **av; 530c2aa98e2SPeter Wemm int *statp; 531c2aa98e2SPeter Wemm { 532c2aa98e2SPeter Wemm int nmx; 533c2aa98e2SPeter Wemm int saveopts = _res.options; 534c2aa98e2SPeter Wemm int i, len = 0; 535c2aa98e2SPeter Wemm char *p; 536c2aa98e2SPeter Wemm char *mxhosts[MAXMXHOSTS + 1]; 537065a643dSPeter Wemm char buf[PSBUFSIZE / 2]; 538c2aa98e2SPeter Wemm 539c2aa98e2SPeter Wemm _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 54006f25ae9SGregory Neil Shapiro nmx = getmxrr(name, mxhosts, NULL, FALSE, statp); 541c2aa98e2SPeter Wemm _res.options = saveopts; 542c2aa98e2SPeter Wemm if (nmx <= 0) 543c2aa98e2SPeter Wemm return NULL; 544c2aa98e2SPeter Wemm if (bitset(MF_MATCHONLY, map->map_mflags)) 545c2aa98e2SPeter Wemm return map_rewrite(map, name, strlen(name), NULL); 546c2aa98e2SPeter Wemm if ((map->map_coldelim == '\0') || (nmx == 1)) 547c2aa98e2SPeter Wemm return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 548c2aa98e2SPeter Wemm 549c2aa98e2SPeter Wemm /* 550c2aa98e2SPeter Wemm ** We were given a -z flag (return all MXs) and there are multiple 551c2aa98e2SPeter Wemm ** ones. We need to build them all into a list. 552c2aa98e2SPeter Wemm */ 553c2aa98e2SPeter Wemm p = buf; 554c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 555c2aa98e2SPeter Wemm { 556c2aa98e2SPeter Wemm int slen; 557c2aa98e2SPeter Wemm 558c2aa98e2SPeter Wemm if (strchr(mxhosts[i], map->map_coldelim) != NULL) 559c2aa98e2SPeter Wemm { 560c2aa98e2SPeter Wemm syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 561c2aa98e2SPeter Wemm mxhosts[i], map->map_coldelim); 562c2aa98e2SPeter Wemm return NULL; 563c2aa98e2SPeter Wemm } 564c2aa98e2SPeter Wemm slen = strlen(mxhosts[i]); 565c2aa98e2SPeter Wemm if (len + slen + 2 > sizeof buf) 566c2aa98e2SPeter Wemm break; 567c2aa98e2SPeter Wemm if (i > 0) 568c2aa98e2SPeter Wemm { 569c2aa98e2SPeter Wemm *p++ = map->map_coldelim; 570c2aa98e2SPeter Wemm len++; 571c2aa98e2SPeter Wemm } 57206f25ae9SGregory Neil Shapiro (void) strlcpy(p, mxhosts[i], sizeof buf - len); 573c2aa98e2SPeter Wemm p += slen; 574c2aa98e2SPeter Wemm len += slen; 575c2aa98e2SPeter Wemm } 576c2aa98e2SPeter Wemm return map_rewrite(map, buf, len, av); 577c2aa98e2SPeter Wemm } 578c2aa98e2SPeter Wemm /* 579c2aa98e2SPeter Wemm ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 580c2aa98e2SPeter Wemm ** 581c2aa98e2SPeter Wemm ** This algorithm tries to be smart about wildcard MX records. 582c2aa98e2SPeter Wemm ** This is hard to do because DNS doesn't tell is if we matched 583c2aa98e2SPeter Wemm ** against a wildcard or a specific MX. 584c2aa98e2SPeter Wemm ** 585c2aa98e2SPeter Wemm ** We always prefer A & CNAME records, since these are presumed 586c2aa98e2SPeter Wemm ** to be specific. 587c2aa98e2SPeter Wemm ** 588c2aa98e2SPeter Wemm ** If we match an MX in one pass and lose it in the next, we use 589c2aa98e2SPeter Wemm ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 590c2aa98e2SPeter Wemm ** A hostname bletch.foo.bar.com will match against this MX, but 591c2aa98e2SPeter Wemm ** will stop matching when we try bletch.bar.com -- so we know 592c2aa98e2SPeter Wemm ** that bletch.foo.bar.com must have been right. This fails if 593c2aa98e2SPeter Wemm ** there was also an MX record matching *.BAR.COM, but there are 594c2aa98e2SPeter Wemm ** some things that just can't be fixed. 595c2aa98e2SPeter Wemm ** 596c2aa98e2SPeter Wemm ** Parameters: 597c2aa98e2SPeter Wemm ** host -- a buffer containing the name of the host. 598c2aa98e2SPeter Wemm ** This is a value-result parameter. 599c2aa98e2SPeter Wemm ** hbsize -- the size of the host buffer. 600c2aa98e2SPeter Wemm ** trymx -- if set, try MX records as well as A and CNAME. 601c2aa98e2SPeter Wemm ** statp -- pointer to place to store status. 602c2aa98e2SPeter Wemm ** 603c2aa98e2SPeter Wemm ** Returns: 604c2aa98e2SPeter Wemm ** TRUE -- if the host matched. 605c2aa98e2SPeter Wemm ** FALSE -- otherwise. 606c2aa98e2SPeter Wemm */ 607c2aa98e2SPeter Wemm 608c2aa98e2SPeter Wemm bool 609c2aa98e2SPeter Wemm dns_getcanonname(host, hbsize, trymx, statp) 610c2aa98e2SPeter Wemm char *host; 611c2aa98e2SPeter Wemm int hbsize; 612c2aa98e2SPeter Wemm bool trymx; 613c2aa98e2SPeter Wemm int *statp; 614c2aa98e2SPeter Wemm { 615c2aa98e2SPeter Wemm register u_char *eom, *ap; 616c2aa98e2SPeter Wemm register char *cp; 617c2aa98e2SPeter Wemm register int n; 618c2aa98e2SPeter Wemm HEADER *hp; 619c2aa98e2SPeter Wemm querybuf answer; 620c2aa98e2SPeter Wemm int ancount, qdcount; 621c2aa98e2SPeter Wemm int ret; 622c2aa98e2SPeter Wemm char **domain; 623c2aa98e2SPeter Wemm int type; 624c2aa98e2SPeter Wemm char **dp; 625c2aa98e2SPeter Wemm char *mxmatch; 626c2aa98e2SPeter Wemm bool amatch; 627c2aa98e2SPeter Wemm bool gotmx = FALSE; 628c2aa98e2SPeter Wemm int qtype; 629c2aa98e2SPeter Wemm int loopcnt; 630c2aa98e2SPeter Wemm char *xp; 631c2aa98e2SPeter Wemm char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; 632c2aa98e2SPeter Wemm char *searchlist[MAXDNSRCH+2]; 633c2aa98e2SPeter Wemm 634c2aa98e2SPeter Wemm if (tTd(8, 2)) 63506f25ae9SGregory Neil Shapiro dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 636c2aa98e2SPeter Wemm 637c2aa98e2SPeter Wemm if ((_res.options & RES_INIT) == 0 && res_init() == -1) 638c2aa98e2SPeter Wemm { 639c2aa98e2SPeter Wemm *statp = EX_UNAVAILABLE; 640c2aa98e2SPeter Wemm return FALSE; 641c2aa98e2SPeter Wemm } 642c2aa98e2SPeter Wemm 643c2aa98e2SPeter Wemm /* 644c2aa98e2SPeter Wemm ** Initialize domain search list. If there is at least one 645c2aa98e2SPeter Wemm ** dot in the name, search the unmodified name first so we 646c2aa98e2SPeter Wemm ** find "vse.CS" in Czechoslovakia instead of in the local 64706f25ae9SGregory Neil Shapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 64806f25ae9SGregory Neil Shapiro ** longer a country named Czechoslovakia but this type of problem 64906f25ae9SGregory Neil Shapiro ** is still present. 650c2aa98e2SPeter Wemm ** 651c2aa98e2SPeter Wemm ** Older versions of the resolver could create this 652c2aa98e2SPeter Wemm ** list by tearing apart the host name. 653c2aa98e2SPeter Wemm */ 654c2aa98e2SPeter Wemm 655c2aa98e2SPeter Wemm loopcnt = 0; 656c2aa98e2SPeter Wemm cnameloop: 657c2aa98e2SPeter Wemm /* Check for dots in the name */ 658c2aa98e2SPeter Wemm for (cp = host, n = 0; *cp != '\0'; cp++) 659c2aa98e2SPeter Wemm if (*cp == '.') 660c2aa98e2SPeter Wemm n++; 661c2aa98e2SPeter Wemm 662c2aa98e2SPeter Wemm /* 663c2aa98e2SPeter Wemm ** If this is a simple name, determine whether it matches an 664c2aa98e2SPeter Wemm ** alias in the file defined by the environment variable HOSTALIASES. 665c2aa98e2SPeter Wemm */ 666c2aa98e2SPeter Wemm if (n == 0 && (xp = gethostalias(host)) != NULL) 667c2aa98e2SPeter Wemm { 668c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH) 669c2aa98e2SPeter Wemm { 670c2aa98e2SPeter Wemm syserr("loop in ${HOSTALIASES} file"); 671c2aa98e2SPeter Wemm } 672c2aa98e2SPeter Wemm else 673c2aa98e2SPeter Wemm { 67406f25ae9SGregory Neil Shapiro (void) strlcpy(host, xp, hbsize); 675c2aa98e2SPeter Wemm goto cnameloop; 676c2aa98e2SPeter Wemm } 677c2aa98e2SPeter Wemm } 678c2aa98e2SPeter Wemm 679c2aa98e2SPeter Wemm /* 680c2aa98e2SPeter Wemm ** Build the search list. 681c2aa98e2SPeter Wemm ** If there is at least one dot in name, start with a null 682c2aa98e2SPeter Wemm ** domain to search the unmodified name first. 683c2aa98e2SPeter Wemm ** If name does not end with a dot and search up local domain 684c2aa98e2SPeter Wemm ** tree desired, append each local domain component to the 685c2aa98e2SPeter Wemm ** search list; if name contains no dots and default domain 686c2aa98e2SPeter Wemm ** name is desired, append default domain name to search list; 687c2aa98e2SPeter Wemm ** else if name ends in a dot, remove that dot. 688c2aa98e2SPeter Wemm */ 689c2aa98e2SPeter Wemm 690c2aa98e2SPeter Wemm dp = searchlist; 691c2aa98e2SPeter Wemm if (n > 0) 692c2aa98e2SPeter Wemm *dp++ = ""; 693c2aa98e2SPeter Wemm if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 694c2aa98e2SPeter Wemm { 69506f25ae9SGregory Neil Shapiro /* make sure there are less than MAXDNSRCH domains */ 69606f25ae9SGregory Neil Shapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 69706f25ae9SGregory Neil Shapiro *domain != NULL && ret < MAXDNSRCH; 69806f25ae9SGregory Neil Shapiro ret++) 699c2aa98e2SPeter Wemm *dp++ = *domain++; 700c2aa98e2SPeter Wemm } 701c2aa98e2SPeter Wemm else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 702c2aa98e2SPeter Wemm { 703c2aa98e2SPeter Wemm *dp++ = _res.defdname; 704c2aa98e2SPeter Wemm } 705c2aa98e2SPeter Wemm else if (*cp == '.') 706c2aa98e2SPeter Wemm { 707c2aa98e2SPeter Wemm *cp = '\0'; 708c2aa98e2SPeter Wemm } 709c2aa98e2SPeter Wemm *dp = NULL; 710c2aa98e2SPeter Wemm 711c2aa98e2SPeter Wemm /* 712c2aa98e2SPeter Wemm ** Now loop through the search list, appending each domain in turn 713c2aa98e2SPeter Wemm ** name and searching for a match. 714c2aa98e2SPeter Wemm */ 715c2aa98e2SPeter Wemm 716c2aa98e2SPeter Wemm mxmatch = NULL; 717c2aa98e2SPeter Wemm qtype = T_ANY; 718c2aa98e2SPeter Wemm 719c2aa98e2SPeter Wemm for (dp = searchlist; *dp != NULL; ) 720c2aa98e2SPeter Wemm { 721c2aa98e2SPeter Wemm if (qtype == T_ANY) 722c2aa98e2SPeter Wemm gotmx = FALSE; 723c2aa98e2SPeter Wemm if (tTd(8, 5)) 72406f25ae9SGregory Neil Shapiro dprintf("dns_getcanonname: trying %s.%s (%s)\n", 725c2aa98e2SPeter Wemm host, *dp, 72606f25ae9SGregory Neil Shapiro qtype == T_ANY ? "ANY" : 72706f25ae9SGregory Neil Shapiro # if NETINET6 72806f25ae9SGregory Neil Shapiro qtype == T_AAAA ? "AAAA" : 72906f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 73006f25ae9SGregory Neil Shapiro qtype == T_A ? "A" : 73106f25ae9SGregory Neil Shapiro qtype == T_MX ? "MX" : 73206f25ae9SGregory Neil Shapiro "???"); 733c2aa98e2SPeter Wemm ret = res_querydomain(host, *dp, C_IN, qtype, 734c2aa98e2SPeter Wemm answer.qb2, sizeof(answer.qb2)); 735c2aa98e2SPeter Wemm if (ret <= 0) 736c2aa98e2SPeter Wemm { 737c2aa98e2SPeter Wemm if (tTd(8, 7)) 73806f25ae9SGregory Neil Shapiro dprintf("\tNO: errno=%d, h_errno=%d\n", 739c2aa98e2SPeter Wemm errno, h_errno); 740c2aa98e2SPeter Wemm 741c2aa98e2SPeter Wemm if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 742c2aa98e2SPeter Wemm { 743c2aa98e2SPeter Wemm /* the name server seems to be down */ 744c2aa98e2SPeter Wemm h_errno = TRY_AGAIN; 745c2aa98e2SPeter Wemm *statp = EX_TEMPFAIL; 74606f25ae9SGregory Neil Shapiro 74706f25ae9SGregory Neil Shapiro /* 74806f25ae9SGregory Neil Shapiro ** If the ANY query is larger than the 74906f25ae9SGregory Neil Shapiro ** UDP packet size, the resolver will 75006f25ae9SGregory Neil Shapiro ** fall back to TCP. However, some 75106f25ae9SGregory Neil Shapiro ** misconfigured firewalls block 53/TCP 75206f25ae9SGregory Neil Shapiro ** so the ANY lookup fails whereas an MX 75306f25ae9SGregory Neil Shapiro ** or A record might work. Therefore, 75406f25ae9SGregory Neil Shapiro ** don't fail on ANY queries. 75506f25ae9SGregory Neil Shapiro ** 75606f25ae9SGregory Neil Shapiro ** The ANY query is really meant to prime 75706f25ae9SGregory Neil Shapiro ** the cache so this isn't dangerous. 75806f25ae9SGregory Neil Shapiro */ 75906f25ae9SGregory Neil Shapiro 76006f25ae9SGregory Neil Shapiro if (qtype != T_ANY) 761c2aa98e2SPeter Wemm return FALSE; 762c2aa98e2SPeter Wemm } 763c2aa98e2SPeter Wemm 764c2aa98e2SPeter Wemm if (h_errno != HOST_NOT_FOUND) 765c2aa98e2SPeter Wemm { 766c2aa98e2SPeter Wemm /* might have another type of interest */ 767c2aa98e2SPeter Wemm if (qtype == T_ANY) 768c2aa98e2SPeter Wemm { 76906f25ae9SGregory Neil Shapiro # if NETINET6 77006f25ae9SGregory Neil Shapiro qtype = T_AAAA; 77106f25ae9SGregory Neil Shapiro # else /* NETINET6 */ 77206f25ae9SGregory Neil Shapiro qtype = T_A; 77306f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 77406f25ae9SGregory Neil Shapiro continue; 77506f25ae9SGregory Neil Shapiro } 77606f25ae9SGregory Neil Shapiro # if NETINET6 77706f25ae9SGregory Neil Shapiro else if (qtype == T_AAAA) 77806f25ae9SGregory Neil Shapiro { 779c2aa98e2SPeter Wemm qtype = T_A; 780c2aa98e2SPeter Wemm continue; 781c2aa98e2SPeter Wemm } 78206f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 78306f25ae9SGregory Neil Shapiro else if (qtype == T_A && !gotmx && 78406f25ae9SGregory Neil Shapiro (trymx || **dp == '\0')) 785c2aa98e2SPeter Wemm { 786c2aa98e2SPeter Wemm qtype = T_MX; 787c2aa98e2SPeter Wemm continue; 788c2aa98e2SPeter Wemm } 789c2aa98e2SPeter Wemm } 790c2aa98e2SPeter Wemm 791c2aa98e2SPeter Wemm /* definite no -- try the next domain */ 792c2aa98e2SPeter Wemm dp++; 793c2aa98e2SPeter Wemm qtype = T_ANY; 794c2aa98e2SPeter Wemm continue; 795c2aa98e2SPeter Wemm } 796c2aa98e2SPeter Wemm else if (tTd(8, 7)) 79706f25ae9SGregory Neil Shapiro dprintf("\tYES\n"); 798c2aa98e2SPeter Wemm 799c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 800c2aa98e2SPeter Wemm if (ret > sizeof(answer)) 801c2aa98e2SPeter Wemm ret = sizeof(answer); 802c2aa98e2SPeter Wemm 803c2aa98e2SPeter Wemm /* 804c2aa98e2SPeter Wemm ** Appear to have a match. Confirm it by searching for A or 805c2aa98e2SPeter Wemm ** CNAME records. If we don't have a local domain 806c2aa98e2SPeter Wemm ** wild card MX record, we will accept MX as well. 807c2aa98e2SPeter Wemm */ 808c2aa98e2SPeter Wemm 809c2aa98e2SPeter Wemm hp = (HEADER *) &answer; 810c2aa98e2SPeter Wemm ap = (u_char *) &answer + HFIXEDSZ; 811c2aa98e2SPeter Wemm eom = (u_char *) &answer + ret; 812c2aa98e2SPeter Wemm 813c2aa98e2SPeter Wemm /* skip question part of response -- we know what we asked */ 81406f25ae9SGregory Neil Shapiro for (qdcount = ntohs((u_short)hp->qdcount); 81506f25ae9SGregory Neil Shapiro qdcount--; 81606f25ae9SGregory Neil Shapiro ap += ret + QFIXEDSZ) 817c2aa98e2SPeter Wemm { 818c2aa98e2SPeter Wemm if ((ret = dn_skipname(ap, eom)) < 0) 819c2aa98e2SPeter Wemm { 820c2aa98e2SPeter Wemm if (tTd(8, 20)) 82106f25ae9SGregory Neil Shapiro dprintf("qdcount failure (%d)\n", 82206f25ae9SGregory Neil Shapiro ntohs((u_short)hp->qdcount)); 823c2aa98e2SPeter Wemm *statp = EX_SOFTWARE; 824c2aa98e2SPeter Wemm return FALSE; /* ???XXX??? */ 825c2aa98e2SPeter Wemm } 826c2aa98e2SPeter Wemm } 827c2aa98e2SPeter Wemm 828c2aa98e2SPeter Wemm amatch = FALSE; 82906f25ae9SGregory Neil Shapiro for (ancount = ntohs((u_short)hp->ancount); 83006f25ae9SGregory Neil Shapiro --ancount >= 0 && ap < eom; 831c2aa98e2SPeter Wemm ap += n) 832c2aa98e2SPeter Wemm { 833c2aa98e2SPeter Wemm n = dn_expand((u_char *) &answer, eom, ap, 834c2aa98e2SPeter Wemm (RES_UNC_T) nbuf, sizeof nbuf); 835c2aa98e2SPeter Wemm if (n < 0) 836c2aa98e2SPeter Wemm break; 837c2aa98e2SPeter Wemm ap += n; 838c2aa98e2SPeter Wemm GETSHORT(type, ap); 839c2aa98e2SPeter Wemm ap += INT16SZ + INT32SZ; 840c2aa98e2SPeter Wemm GETSHORT(n, ap); 841c2aa98e2SPeter Wemm switch (type) 842c2aa98e2SPeter Wemm { 843c2aa98e2SPeter Wemm case T_MX: 844c2aa98e2SPeter Wemm gotmx = TRUE; 845c2aa98e2SPeter Wemm if (**dp != '\0' && HasWildcardMX) 846c2aa98e2SPeter Wemm { 847c2aa98e2SPeter Wemm /* 848c2aa98e2SPeter Wemm ** If we are using MX matches and have 849c2aa98e2SPeter Wemm ** not yet gotten one, save this one 850c2aa98e2SPeter Wemm ** but keep searching for an A or 851c2aa98e2SPeter Wemm ** CNAME match. 852c2aa98e2SPeter Wemm */ 853c2aa98e2SPeter Wemm 854c2aa98e2SPeter Wemm if (trymx && mxmatch == NULL) 855c2aa98e2SPeter Wemm mxmatch = *dp; 856c2aa98e2SPeter Wemm continue; 857c2aa98e2SPeter Wemm } 858c2aa98e2SPeter Wemm 859c2aa98e2SPeter Wemm /* 860c2aa98e2SPeter Wemm ** If we did not append a domain name, this 861c2aa98e2SPeter Wemm ** must have been a canonical name to start 862c2aa98e2SPeter Wemm ** with. Even if we did append a domain name, 863c2aa98e2SPeter Wemm ** in the absence of a wildcard MX this must 864c2aa98e2SPeter Wemm ** still be a real MX match. 865c2aa98e2SPeter Wemm ** Such MX matches are as good as an A match, 866c2aa98e2SPeter Wemm ** fall through. 867c2aa98e2SPeter Wemm */ 86806f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 86906f25ae9SGregory Neil Shapiro 87006f25ae9SGregory Neil Shapiro # if NETINET6 87106f25ae9SGregory Neil Shapiro case T_AAAA: 87206f25ae9SGregory Neil Shapiro /* Flag that a good match was found */ 87306f25ae9SGregory Neil Shapiro amatch = TRUE; 87406f25ae9SGregory Neil Shapiro 87506f25ae9SGregory Neil Shapiro /* continue in case a CNAME also exists */ 87606f25ae9SGregory Neil Shapiro continue; 87706f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 878c2aa98e2SPeter Wemm 879c2aa98e2SPeter Wemm case T_A: 880c2aa98e2SPeter Wemm /* Flag that a good match was found */ 881c2aa98e2SPeter Wemm amatch = TRUE; 882c2aa98e2SPeter Wemm 883c2aa98e2SPeter Wemm /* continue in case a CNAME also exists */ 884c2aa98e2SPeter Wemm continue; 885c2aa98e2SPeter Wemm 886c2aa98e2SPeter Wemm case T_CNAME: 887c2aa98e2SPeter Wemm if (DontExpandCnames) 888c2aa98e2SPeter Wemm { 889c2aa98e2SPeter Wemm /* got CNAME -- guaranteed canonical */ 890c2aa98e2SPeter Wemm amatch = TRUE; 891c2aa98e2SPeter Wemm break; 892c2aa98e2SPeter Wemm } 893c2aa98e2SPeter Wemm 894c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH) 895c2aa98e2SPeter Wemm { 896c2aa98e2SPeter Wemm /*XXX should notify postmaster XXX*/ 897c2aa98e2SPeter Wemm message("DNS failure: CNAME loop for %s", 898c2aa98e2SPeter Wemm host); 899c2aa98e2SPeter Wemm if (CurEnv->e_message == NULL) 900c2aa98e2SPeter Wemm { 901c2aa98e2SPeter Wemm char ebuf[MAXLINE]; 902c2aa98e2SPeter Wemm 903c2aa98e2SPeter Wemm snprintf(ebuf, sizeof ebuf, 904c2aa98e2SPeter Wemm "Deferred: DNS failure: CNAME loop for %.100s", 905c2aa98e2SPeter Wemm host); 906c2aa98e2SPeter Wemm CurEnv->e_message = newstr(ebuf); 907c2aa98e2SPeter Wemm } 908c2aa98e2SPeter Wemm h_errno = NO_RECOVERY; 909c2aa98e2SPeter Wemm *statp = EX_CONFIG; 910c2aa98e2SPeter Wemm return FALSE; 911c2aa98e2SPeter Wemm } 912c2aa98e2SPeter Wemm 913c2aa98e2SPeter Wemm /* value points at name */ 914c2aa98e2SPeter Wemm if ((ret = dn_expand((u_char *)&answer, 915c2aa98e2SPeter Wemm eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) 916c2aa98e2SPeter Wemm break; 91706f25ae9SGregory Neil Shapiro (void)strlcpy(host, nbuf, hbsize); 918c2aa98e2SPeter Wemm 919c2aa98e2SPeter Wemm /* 920c2aa98e2SPeter Wemm ** RFC 1034 section 3.6 specifies that CNAME 921c2aa98e2SPeter Wemm ** should point at the canonical name -- but 922c2aa98e2SPeter Wemm ** urges software to try again anyway. 923c2aa98e2SPeter Wemm */ 924c2aa98e2SPeter Wemm 925c2aa98e2SPeter Wemm goto cnameloop; 926c2aa98e2SPeter Wemm 927c2aa98e2SPeter Wemm default: 928c2aa98e2SPeter Wemm /* not a record of interest */ 929c2aa98e2SPeter Wemm continue; 930c2aa98e2SPeter Wemm } 931c2aa98e2SPeter Wemm } 932c2aa98e2SPeter Wemm 933c2aa98e2SPeter Wemm if (amatch) 934c2aa98e2SPeter Wemm { 935c2aa98e2SPeter Wemm /* 936c2aa98e2SPeter Wemm ** Got a good match -- either an A, CNAME, or an 937c2aa98e2SPeter Wemm ** exact MX record. Save it and get out of here. 938c2aa98e2SPeter Wemm */ 939c2aa98e2SPeter Wemm 940c2aa98e2SPeter Wemm mxmatch = *dp; 941c2aa98e2SPeter Wemm break; 942c2aa98e2SPeter Wemm } 943c2aa98e2SPeter Wemm 944c2aa98e2SPeter Wemm /* 945c2aa98e2SPeter Wemm ** Nothing definitive yet. 946c2aa98e2SPeter Wemm ** If this was a T_ANY query, we don't really know what 947c2aa98e2SPeter Wemm ** was returned -- it might have been a T_NS, 948c2aa98e2SPeter Wemm ** for example. Try T_A to be more specific 949c2aa98e2SPeter Wemm ** during the next pass. 950c2aa98e2SPeter Wemm ** If this was a T_A query and we haven't yet found a MX 951c2aa98e2SPeter Wemm ** match, try T_MX if allowed to do so. 952c2aa98e2SPeter Wemm ** Otherwise, try the next domain. 953c2aa98e2SPeter Wemm */ 954c2aa98e2SPeter Wemm 955c2aa98e2SPeter Wemm if (qtype == T_ANY) 95606f25ae9SGregory Neil Shapiro { 95706f25ae9SGregory Neil Shapiro # if NETINET6 95806f25ae9SGregory Neil Shapiro qtype = T_AAAA; 95906f25ae9SGregory Neil Shapiro # else /* NETINET6 */ 960c2aa98e2SPeter Wemm qtype = T_A; 96106f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 96206f25ae9SGregory Neil Shapiro } 96306f25ae9SGregory Neil Shapiro # if NETINET6 96406f25ae9SGregory Neil Shapiro else if (qtype == T_AAAA) 96506f25ae9SGregory Neil Shapiro qtype = T_A; 96606f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 967065a643dSPeter Wemm else if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 968c2aa98e2SPeter Wemm qtype = T_MX; 969c2aa98e2SPeter Wemm else 970c2aa98e2SPeter Wemm { 971c2aa98e2SPeter Wemm qtype = T_ANY; 972c2aa98e2SPeter Wemm dp++; 973c2aa98e2SPeter Wemm } 974c2aa98e2SPeter Wemm } 975c2aa98e2SPeter Wemm 976c2aa98e2SPeter Wemm /* if nothing was found, we are done */ 977c2aa98e2SPeter Wemm if (mxmatch == NULL) 978c2aa98e2SPeter Wemm { 979c2aa98e2SPeter Wemm *statp = EX_NOHOST; 980c2aa98e2SPeter Wemm return FALSE; 981c2aa98e2SPeter Wemm } 982c2aa98e2SPeter Wemm 983c2aa98e2SPeter Wemm /* 984c2aa98e2SPeter Wemm ** Create canonical name and return. 985c2aa98e2SPeter Wemm ** If saved domain name is null, name was already canonical. 986c2aa98e2SPeter Wemm ** Otherwise append the saved domain name. 987c2aa98e2SPeter Wemm */ 988c2aa98e2SPeter Wemm 989c2aa98e2SPeter Wemm (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, 990c2aa98e2SPeter Wemm *mxmatch == '\0' ? "" : ".", 991c2aa98e2SPeter Wemm MAXDNAME, mxmatch); 99206f25ae9SGregory Neil Shapiro (void) strlcpy(host, nbuf, hbsize); 993c2aa98e2SPeter Wemm if (tTd(8, 5)) 99406f25ae9SGregory Neil Shapiro dprintf("dns_getcanonname: %s\n", host); 995c2aa98e2SPeter Wemm *statp = EX_OK; 996c2aa98e2SPeter Wemm return TRUE; 997c2aa98e2SPeter Wemm } 998c2aa98e2SPeter Wemm 99906f25ae9SGregory Neil Shapiro static char * 1000c2aa98e2SPeter Wemm gethostalias(host) 1001c2aa98e2SPeter Wemm char *host; 1002c2aa98e2SPeter Wemm { 1003c2aa98e2SPeter Wemm char *fname; 1004c2aa98e2SPeter Wemm FILE *fp; 1005c2aa98e2SPeter Wemm register char *p = NULL; 100606f25ae9SGregory Neil Shapiro long sff = SFF_REGONLY; 1007c2aa98e2SPeter Wemm char buf[MAXLINE]; 1008c2aa98e2SPeter Wemm static char hbuf[MAXDNAME]; 1009c2aa98e2SPeter Wemm 1010c2aa98e2SPeter Wemm if (DontLockReadFiles) 1011c2aa98e2SPeter Wemm sff |= SFF_NOLOCK; 1012c2aa98e2SPeter Wemm fname = getenv("HOSTALIASES"); 1013c2aa98e2SPeter Wemm if (fname == NULL || 1014c2aa98e2SPeter Wemm (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL) 1015c2aa98e2SPeter Wemm return NULL; 1016c2aa98e2SPeter Wemm while (fgets(buf, sizeof buf, fp) != NULL) 1017c2aa98e2SPeter Wemm { 1018c2aa98e2SPeter Wemm for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) 1019c2aa98e2SPeter Wemm continue; 1020c2aa98e2SPeter Wemm if (*p == 0) 1021c2aa98e2SPeter Wemm { 1022c2aa98e2SPeter Wemm /* syntax error */ 1023c2aa98e2SPeter Wemm continue; 1024c2aa98e2SPeter Wemm } 1025c2aa98e2SPeter Wemm *p++ = '\0'; 1026c2aa98e2SPeter Wemm if (strcasecmp(buf, host) == 0) 1027c2aa98e2SPeter Wemm break; 1028c2aa98e2SPeter Wemm } 1029c2aa98e2SPeter Wemm 1030c2aa98e2SPeter Wemm if (feof(fp)) 1031c2aa98e2SPeter Wemm { 1032c2aa98e2SPeter Wemm /* no match */ 103306f25ae9SGregory Neil Shapiro (void) fclose(fp); 1034c2aa98e2SPeter Wemm return NULL; 1035c2aa98e2SPeter Wemm } 103606f25ae9SGregory Neil Shapiro (void) fclose(fp); 1037c2aa98e2SPeter Wemm 1038c2aa98e2SPeter Wemm /* got a match; extract the equivalent name */ 1039c2aa98e2SPeter Wemm while (*p != '\0' && isascii(*p) && isspace(*p)) 1040c2aa98e2SPeter Wemm p++; 1041c2aa98e2SPeter Wemm host = p; 1042c2aa98e2SPeter Wemm while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1043c2aa98e2SPeter Wemm p++; 1044c2aa98e2SPeter Wemm *p = '\0'; 104506f25ae9SGregory Neil Shapiro (void) strlcpy(hbuf, host, sizeof hbuf); 1046c2aa98e2SPeter Wemm return hbuf; 1047c2aa98e2SPeter Wemm } 1048c2aa98e2SPeter Wemm #endif /* NAMED_BIND */ 1049