1c2aa98e2SPeter Wemm /* 2af9557fdSGregory Neil Shapiro * Copyright (c) 1998-2004, 2006 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> 15d0cef73dSGregory Neil Shapiro #include "map.h" 16c2aa98e2SPeter Wemm 17c2aa98e2SPeter Wemm #if NAMED_BIND 18d0cef73dSGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.202 2006/12/19 01:15:07 ca Exp $ (with name server)") 1906f25ae9SGregory Neil Shapiro #else /* NAMED_BIND */ 20d0cef73dSGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.202 2006/12/19 01:15:07 ca Exp $ (without name server)") 2106f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */ 22c2aa98e2SPeter Wemm 23c2aa98e2SPeter Wemm #if NAMED_BIND 24c2aa98e2SPeter Wemm 25c2aa98e2SPeter Wemm # include <arpa/inet.h> 26c2aa98e2SPeter Wemm 2740266059SGregory Neil Shapiro 28c2aa98e2SPeter Wemm /* 29c2aa98e2SPeter Wemm ** The standard udp packet size PACKETSZ (512) is not sufficient for some 30c2aa98e2SPeter Wemm ** nameserver answers containing very many resource records. The resolver 31c2aa98e2SPeter Wemm ** may switch to tcp and retry if it detects udp packet overflow. 32c2aa98e2SPeter Wemm ** Also note that the resolver routines res_query and res_search return 33c2aa98e2SPeter Wemm ** the size of the *un*truncated answer in case the supplied answer buffer 34c2aa98e2SPeter Wemm ** it not big enough to accommodate the entire answer. 35c2aa98e2SPeter Wemm */ 36c2aa98e2SPeter Wemm 37c2aa98e2SPeter Wemm # ifndef MAXPACKET 38c2aa98e2SPeter Wemm # define MAXPACKET 8192 /* max packet size used internally by BIND */ 3906f25ae9SGregory Neil Shapiro # endif /* ! MAXPACKET */ 40c2aa98e2SPeter Wemm 41c2aa98e2SPeter Wemm typedef union 42c2aa98e2SPeter Wemm { 43c2aa98e2SPeter Wemm HEADER qb1; 4440266059SGregory Neil Shapiro unsigned char qb2[MAXPACKET]; 45c2aa98e2SPeter Wemm } querybuf; 46c2aa98e2SPeter Wemm 47c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE 48c2aa98e2SPeter Wemm # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 4906f25ae9SGregory Neil Shapiro # endif /* ! MXHOSTBUFSIZE */ 50c2aa98e2SPeter Wemm 51c2aa98e2SPeter Wemm static char MXHostBuf[MXHOSTBUFSIZE]; 5240266059SGregory Neil Shapiro #if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 5340266059SGregory Neil Shapiro ERROR: _MXHOSTBUFSIZE is out of range 5440266059SGregory Neil Shapiro #endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 55c2aa98e2SPeter Wemm 56c2aa98e2SPeter Wemm # ifndef MAXDNSRCH 57c2aa98e2SPeter Wemm # define MAXDNSRCH 6 /* number of possible domains to search */ 5806f25ae9SGregory Neil Shapiro # endif /* ! MAXDNSRCH */ 5906f25ae9SGregory Neil Shapiro 6006f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE 6106f25ae9SGregory Neil Shapiro # define RES_DNSRCH_VARIABLE _res.dnsrch 6206f25ae9SGregory Neil Shapiro # endif /* ! RES_DNSRCH_VARIABLE */ 63c2aa98e2SPeter Wemm 64c2aa98e2SPeter Wemm # ifndef NO_DATA 65c2aa98e2SPeter Wemm # define NO_DATA NO_ADDRESS 6606f25ae9SGregory Neil Shapiro # endif /* ! NO_DATA */ 67c2aa98e2SPeter Wemm 68c2aa98e2SPeter Wemm # ifndef HFIXEDSZ 69c2aa98e2SPeter Wemm # define HFIXEDSZ 12 /* sizeof(HEADER) */ 7006f25ae9SGregory Neil Shapiro # endif /* ! HFIXEDSZ */ 71c2aa98e2SPeter Wemm 72c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 73c2aa98e2SPeter Wemm 74c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415) 75c2aa98e2SPeter Wemm # define RES_UNC_T char * 7606f25ae9SGregory Neil Shapiro # else /* defined(__RES) && (__RES >= 19940415) */ 7740266059SGregory Neil Shapiro # define RES_UNC_T unsigned char * 7806f25ae9SGregory Neil Shapiro # endif /* defined(__RES) && (__RES >= 19940415) */ 7906f25ae9SGregory Neil Shapiro 8006f25ae9SGregory Neil Shapiro static int mxrand __P((char *)); 8140266059SGregory Neil Shapiro static int fallbackmxrr __P((int, unsigned short *, char **)); 8206f25ae9SGregory Neil Shapiro 8340266059SGregory Neil Shapiro /* 8440266059SGregory Neil Shapiro ** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 8540266059SGregory Neil Shapiro ** 8640266059SGregory Neil Shapiro ** We have to initialize this once before doing anything else. 8740266059SGregory Neil Shapiro ** Moreover, we have to repeat this from time to time to avoid 8840266059SGregory Neil Shapiro ** stale data, e.g., in persistent queue runners. 8940266059SGregory Neil Shapiro ** This should be done in a parent process so the child 9040266059SGregory Neil Shapiro ** processes have the right data. 9140266059SGregory Neil Shapiro ** 9240266059SGregory Neil Shapiro ** Parameters: 9340266059SGregory Neil Shapiro ** host -- the name of the fallback MX host. 9440266059SGregory Neil Shapiro ** 9540266059SGregory Neil Shapiro ** Returns: 9640266059SGregory Neil Shapiro ** number of MX records. 9740266059SGregory Neil Shapiro ** 9840266059SGregory Neil Shapiro ** Side Effects: 99e92d3f3fSGregory Neil Shapiro ** Populates NumFallbackMXHosts and fbhosts. 10040266059SGregory Neil Shapiro ** Sets renewal time (based on TTL). 10140266059SGregory Neil Shapiro */ 10240266059SGregory Neil Shapiro 103e92d3f3fSGregory Neil Shapiro int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 10440266059SGregory Neil Shapiro static char *fbhosts[MAXMXHOSTS + 1]; 10540266059SGregory Neil Shapiro 10640266059SGregory Neil Shapiro int 10740266059SGregory Neil Shapiro getfallbackmxrr(host) 10840266059SGregory Neil Shapiro char *host; 10940266059SGregory Neil Shapiro { 11040266059SGregory Neil Shapiro int i, rcode; 11140266059SGregory Neil Shapiro int ttl; 11240266059SGregory Neil Shapiro static time_t renew = 0; 11340266059SGregory Neil Shapiro 11440266059SGregory Neil Shapiro #if 0 11540266059SGregory Neil Shapiro /* This is currently done before this function is called. */ 11640266059SGregory Neil Shapiro if (host == NULL || *host == '\0') 11740266059SGregory Neil Shapiro return 0; 11840266059SGregory Neil Shapiro #endif /* 0 */ 119e92d3f3fSGregory Neil Shapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 120e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 12140266059SGregory Neil Shapiro if (host[0] == '[') 12240266059SGregory Neil Shapiro { 12340266059SGregory Neil Shapiro fbhosts[0] = host; 124e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = 1; 12540266059SGregory Neil Shapiro } 12640266059SGregory Neil Shapiro else 12740266059SGregory Neil Shapiro { 12840266059SGregory Neil Shapiro /* free old data */ 129e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 13040266059SGregory Neil Shapiro sm_free(fbhosts[i]); 13140266059SGregory Neil Shapiro 13240266059SGregory Neil Shapiro /* get new data */ 133e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false, 13440266059SGregory Neil Shapiro &rcode, false, &ttl); 13540266059SGregory Neil Shapiro renew = curtime() + ttl; 136e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 13740266059SGregory Neil Shapiro fbhosts[i] = newstr(fbhosts[i]); 13840266059SGregory Neil Shapiro } 139e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 14040266059SGregory Neil Shapiro } 14140266059SGregory Neil Shapiro 14240266059SGregory Neil Shapiro /* 14340266059SGregory Neil Shapiro ** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 14440266059SGregory Neil Shapiro ** 14540266059SGregory Neil Shapiro ** Parameters: 14640266059SGregory Neil Shapiro ** nmx -- current number of MX records. 14740266059SGregory Neil Shapiro ** prefs -- array of preferences. 14840266059SGregory Neil Shapiro ** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 14940266059SGregory Neil Shapiro ** 15040266059SGregory Neil Shapiro ** Returns: 15140266059SGregory Neil Shapiro ** new number of MX records. 15240266059SGregory Neil Shapiro ** 15340266059SGregory Neil Shapiro ** Side Effects: 154e92d3f3fSGregory Neil Shapiro ** If FallbackMX was set, it appends the MX records for 15540266059SGregory Neil Shapiro ** that host to mxhosts (and modifies prefs accordingly). 15640266059SGregory Neil Shapiro */ 15740266059SGregory Neil Shapiro 15840266059SGregory Neil Shapiro static int 15940266059SGregory Neil Shapiro fallbackmxrr(nmx, prefs, mxhosts) 16040266059SGregory Neil Shapiro int nmx; 16140266059SGregory Neil Shapiro unsigned short *prefs; 16240266059SGregory Neil Shapiro char **mxhosts; 16340266059SGregory Neil Shapiro { 16440266059SGregory Neil Shapiro int i; 16540266059SGregory Neil Shapiro 166e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 16740266059SGregory Neil Shapiro { 16840266059SGregory Neil Shapiro if (nmx > 0) 16940266059SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1; 17040266059SGregory Neil Shapiro else 17140266059SGregory Neil Shapiro prefs[nmx] = 0; 17240266059SGregory Neil Shapiro mxhosts[nmx++] = fbhosts[i]; 17340266059SGregory Neil Shapiro } 17440266059SGregory Neil Shapiro return nmx; 17540266059SGregory Neil Shapiro } 17640266059SGregory Neil Shapiro 17740266059SGregory Neil Shapiro /* 178c2aa98e2SPeter Wemm ** GETMXRR -- get MX resource records for a domain 179c2aa98e2SPeter Wemm ** 180c2aa98e2SPeter Wemm ** Parameters: 181c2aa98e2SPeter Wemm ** host -- the name of the host to MX. 182c2aa98e2SPeter Wemm ** mxhosts -- a pointer to a return buffer of MX records. 18306f25ae9SGregory Neil Shapiro ** mxprefs -- a pointer to a return buffer of MX preferences. 18406f25ae9SGregory Neil Shapiro ** If NULL, don't try to populate. 18540266059SGregory Neil Shapiro ** droplocalhost -- If true, all MX records less preferred 186c2aa98e2SPeter Wemm ** than the local host (as determined by $=w) will 187c2aa98e2SPeter Wemm ** be discarded. 188c2aa98e2SPeter Wemm ** rcode -- a pointer to an EX_ status code. 18940266059SGregory Neil Shapiro ** tryfallback -- add also fallback MX host? 19040266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 191c2aa98e2SPeter Wemm ** 192c2aa98e2SPeter Wemm ** Returns: 193c2aa98e2SPeter Wemm ** The number of MX records found. 194c2aa98e2SPeter Wemm ** -1 if there is an internal failure. 195c2aa98e2SPeter Wemm ** If no MX records are found, mxhosts[0] is set to host 196c2aa98e2SPeter Wemm ** and 1 is returned. 19740266059SGregory Neil Shapiro ** 19840266059SGregory Neil Shapiro ** Side Effects: 19940266059SGregory Neil Shapiro ** The entries made for mxhosts point to a static array 20040266059SGregory Neil Shapiro ** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 20140266059SGregory Neil Shapiro ** if it must be preserved across calls to this function. 202c2aa98e2SPeter Wemm */ 203c2aa98e2SPeter Wemm 204c2aa98e2SPeter Wemm int 20540266059SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) 206c2aa98e2SPeter Wemm char *host; 207c2aa98e2SPeter Wemm char **mxhosts; 20840266059SGregory Neil Shapiro unsigned short *mxprefs; 209c2aa98e2SPeter Wemm bool droplocalhost; 210c2aa98e2SPeter Wemm int *rcode; 21140266059SGregory Neil Shapiro bool tryfallback; 21240266059SGregory Neil Shapiro int *pttl; 213c2aa98e2SPeter Wemm { 21440266059SGregory Neil Shapiro register unsigned char *eom, *cp; 215c2aa98e2SPeter Wemm register int i, j, n; 216c2aa98e2SPeter Wemm int nmx = 0; 217c2aa98e2SPeter Wemm register char *bp; 218c2aa98e2SPeter Wemm HEADER *hp; 219c2aa98e2SPeter Wemm querybuf answer; 220c2aa98e2SPeter Wemm int ancount, qdcount, buflen; 22140266059SGregory Neil Shapiro bool seenlocal = false; 22240266059SGregory Neil Shapiro unsigned short pref, type; 22340266059SGregory Neil Shapiro unsigned short localpref = 256; 224e92d3f3fSGregory Neil Shapiro char *fallbackMX = FallbackMX; 22540266059SGregory Neil Shapiro bool trycanon = false; 22640266059SGregory Neil Shapiro unsigned short *prefs; 227b6bacd31SGregory Neil Shapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 22840266059SGregory Neil Shapiro unsigned short prefer[MAXMXHOSTS]; 229c2aa98e2SPeter Wemm int weight[MAXMXHOSTS]; 23040266059SGregory Neil Shapiro int ttl = 0; 23106f25ae9SGregory Neil Shapiro extern int res_query(), res_search(); 232c2aa98e2SPeter Wemm 233c2aa98e2SPeter Wemm if (tTd(8, 2)) 23440266059SGregory Neil Shapiro sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", 23506f25ae9SGregory Neil Shapiro host, droplocalhost); 23613d88268SGregory Neil Shapiro *rcode = EX_OK; 23713d88268SGregory Neil Shapiro if (pttl != NULL) 23813d88268SGregory Neil Shapiro *pttl = SM_DEFAULT_TTL; 239a7ec597cSGregory Neil Shapiro if (*host == '\0') 240a7ec597cSGregory Neil Shapiro return 0; 241c2aa98e2SPeter Wemm 24240266059SGregory Neil Shapiro if ((fallbackMX != NULL && droplocalhost && 24340266059SGregory Neil Shapiro wordinclass(fallbackMX, 'w')) || !tryfallback) 244c2aa98e2SPeter Wemm { 245c2aa98e2SPeter Wemm /* don't use fallback for this pass */ 246c2aa98e2SPeter Wemm fallbackMX = NULL; 247c2aa98e2SPeter Wemm } 248c2aa98e2SPeter Wemm 24906f25ae9SGregory Neil Shapiro if (mxprefs != NULL) 25006f25ae9SGregory Neil Shapiro prefs = mxprefs; 25106f25ae9SGregory Neil Shapiro else 25206f25ae9SGregory Neil Shapiro prefs = prefer; 25306f25ae9SGregory Neil Shapiro 254c2aa98e2SPeter Wemm /* efficiency hack -- numeric or non-MX lookups */ 255c2aa98e2SPeter Wemm if (host[0] == '[') 256c2aa98e2SPeter Wemm goto punt; 257c2aa98e2SPeter Wemm 258c2aa98e2SPeter Wemm /* 259c2aa98e2SPeter Wemm ** If we don't have MX records in our host switch, don't 260c2aa98e2SPeter Wemm ** try for MX records. Note that this really isn't "right", 261c2aa98e2SPeter Wemm ** since we might be set up to try NIS first and then DNS; 262c2aa98e2SPeter Wemm ** if the host is found in NIS we really shouldn't be doing 263c2aa98e2SPeter Wemm ** MX lookups. However, that should be a degenerate case. 264c2aa98e2SPeter Wemm */ 265c2aa98e2SPeter Wemm 266c2aa98e2SPeter Wemm if (!UseNameServer) 267c2aa98e2SPeter Wemm goto punt; 268c2aa98e2SPeter Wemm if (HasWildcardMX && ConfigLevel >= 6) 269c2aa98e2SPeter Wemm resfunc = res_query; 270c2aa98e2SPeter Wemm else 271c2aa98e2SPeter Wemm resfunc = res_search; 272c2aa98e2SPeter Wemm 273c2aa98e2SPeter Wemm errno = 0; 27440266059SGregory Neil Shapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 27540266059SGregory Neil Shapiro sizeof(answer)); 276c2aa98e2SPeter Wemm if (n < 0) 277c2aa98e2SPeter Wemm { 278c2aa98e2SPeter Wemm if (tTd(8, 1)) 27940266059SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 280d0cef73dSGregory Neil Shapiro host, errno, h_errno); 281c2aa98e2SPeter Wemm switch (h_errno) 282c2aa98e2SPeter Wemm { 283c2aa98e2SPeter Wemm case NO_DATA: 28440266059SGregory Neil Shapiro trycanon = true; 28506f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 286c2aa98e2SPeter Wemm 287c2aa98e2SPeter Wemm case NO_RECOVERY: 288c2aa98e2SPeter Wemm /* no MX data on this host */ 289c2aa98e2SPeter Wemm goto punt; 290c2aa98e2SPeter Wemm 291c2aa98e2SPeter Wemm case HOST_NOT_FOUND: 292c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH 293c2aa98e2SPeter Wemm case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 29406f25ae9SGregory Neil Shapiro # endif /* BROKEN_RES_SEARCH */ 295c2aa98e2SPeter Wemm /* host doesn't exist in DNS; might be in /etc/hosts */ 29640266059SGregory Neil Shapiro trycanon = true; 297c2aa98e2SPeter Wemm *rcode = EX_NOHOST; 298c2aa98e2SPeter Wemm goto punt; 299c2aa98e2SPeter Wemm 300c2aa98e2SPeter Wemm case TRY_AGAIN: 301c2aa98e2SPeter Wemm case -1: 302c2aa98e2SPeter Wemm /* couldn't connect to the name server */ 303c2aa98e2SPeter Wemm if (fallbackMX != NULL) 304c2aa98e2SPeter Wemm { 305c2aa98e2SPeter Wemm /* name server is hosed -- push to fallback */ 30640266059SGregory Neil Shapiro return fallbackmxrr(nmx, prefs, mxhosts); 307c2aa98e2SPeter Wemm } 308c2aa98e2SPeter Wemm /* it might come up later; better queue it up */ 309c2aa98e2SPeter Wemm *rcode = EX_TEMPFAIL; 310c2aa98e2SPeter Wemm break; 311c2aa98e2SPeter Wemm 312c2aa98e2SPeter Wemm default: 31340266059SGregory Neil Shapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 314c2aa98e2SPeter Wemm host, h_errno); 315c2aa98e2SPeter Wemm *rcode = EX_OSERR; 316c2aa98e2SPeter Wemm break; 317c2aa98e2SPeter Wemm } 318c2aa98e2SPeter Wemm 319c2aa98e2SPeter Wemm /* irreconcilable differences */ 32006f25ae9SGregory Neil Shapiro return -1; 321c2aa98e2SPeter Wemm } 322c2aa98e2SPeter Wemm 323c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 324c2aa98e2SPeter Wemm if (n > sizeof(answer)) 325c2aa98e2SPeter Wemm n = sizeof(answer); 326c2aa98e2SPeter Wemm 327c2aa98e2SPeter Wemm /* find first satisfactory answer */ 328c2aa98e2SPeter Wemm hp = (HEADER *)&answer; 32940266059SGregory Neil Shapiro cp = (unsigned char *)&answer + HFIXEDSZ; 33040266059SGregory Neil Shapiro eom = (unsigned char *)&answer + n; 33140266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 33206f25ae9SGregory Neil Shapiro qdcount--; 33306f25ae9SGregory Neil Shapiro cp += n + QFIXEDSZ) 33406f25ae9SGregory Neil Shapiro { 335c2aa98e2SPeter Wemm if ((n = dn_skipname(cp, eom)) < 0) 336c2aa98e2SPeter Wemm goto punt; 33706f25ae9SGregory Neil Shapiro } 33840266059SGregory Neil Shapiro 33940266059SGregory Neil Shapiro /* NOTE: see definition of MXHostBuf! */ 340c2aa98e2SPeter Wemm buflen = sizeof(MXHostBuf) - 1; 34140266059SGregory Neil Shapiro SM_ASSERT(buflen > 0); 342c2aa98e2SPeter Wemm bp = MXHostBuf; 34340266059SGregory Neil Shapiro ancount = ntohs((unsigned short) hp->ancount); 34440266059SGregory Neil Shapiro 34540266059SGregory Neil Shapiro /* See RFC 1035 for layout of RRs. */ 346e92d3f3fSGregory Neil Shapiro /* XXX leave room for FallbackMX ? */ 347c2aa98e2SPeter Wemm while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 348c2aa98e2SPeter Wemm { 34940266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 35040266059SGregory Neil Shapiro (RES_UNC_T) bp, buflen)) < 0) 351c2aa98e2SPeter Wemm break; 352c2aa98e2SPeter Wemm cp += n; 353c2aa98e2SPeter Wemm GETSHORT(type, cp); 35440266059SGregory Neil Shapiro cp += INT16SZ; /* skip over class */ 35540266059SGregory Neil Shapiro GETLONG(ttl, cp); 35640266059SGregory Neil Shapiro GETSHORT(n, cp); /* rdlength */ 357c2aa98e2SPeter Wemm if (type != T_MX) 358c2aa98e2SPeter Wemm { 359c2aa98e2SPeter Wemm if (tTd(8, 8) || _res.options & RES_DEBUG) 36040266059SGregory Neil Shapiro sm_dprintf("unexpected answer type %d, size %d\n", 361c2aa98e2SPeter Wemm type, n); 362c2aa98e2SPeter Wemm cp += n; 363c2aa98e2SPeter Wemm continue; 364c2aa98e2SPeter Wemm } 365c2aa98e2SPeter Wemm GETSHORT(pref, cp); 36640266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 367c2aa98e2SPeter Wemm (RES_UNC_T) bp, buflen)) < 0) 368c2aa98e2SPeter Wemm break; 369c2aa98e2SPeter Wemm cp += n; 37040266059SGregory Neil Shapiro n = strlen(bp); 37140266059SGregory Neil Shapiro # if 0 37240266059SGregory Neil Shapiro /* Can this happen? */ 37340266059SGregory Neil Shapiro if (n == 0) 37440266059SGregory Neil Shapiro { 37540266059SGregory Neil Shapiro if (LogLevel > 4) 37640266059SGregory Neil Shapiro sm_syslog(LOG_ERR, NOQID, 37740266059SGregory Neil Shapiro "MX records for %s contain empty string", 37840266059SGregory Neil Shapiro host); 37940266059SGregory Neil Shapiro continue; 38040266059SGregory Neil Shapiro } 38140266059SGregory Neil Shapiro # endif /* 0 */ 382c2aa98e2SPeter Wemm if (wordinclass(bp, 'w')) 383c2aa98e2SPeter Wemm { 384c2aa98e2SPeter Wemm if (tTd(8, 3)) 38540266059SGregory Neil Shapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 386c2aa98e2SPeter Wemm bp, pref); 387c2aa98e2SPeter Wemm if (droplocalhost) 388c2aa98e2SPeter Wemm { 389c2aa98e2SPeter Wemm if (!seenlocal || pref < localpref) 390c2aa98e2SPeter Wemm localpref = pref; 39140266059SGregory Neil Shapiro seenlocal = true; 392c2aa98e2SPeter Wemm continue; 393c2aa98e2SPeter Wemm } 394c2aa98e2SPeter Wemm weight[nmx] = 0; 395c2aa98e2SPeter Wemm } 396c2aa98e2SPeter Wemm else 397c2aa98e2SPeter Wemm weight[nmx] = mxrand(bp); 39806f25ae9SGregory Neil Shapiro prefs[nmx] = pref; 399c2aa98e2SPeter Wemm mxhosts[nmx++] = bp; 400c2aa98e2SPeter Wemm bp += n; 401c2aa98e2SPeter Wemm if (bp[-1] != '.') 402c2aa98e2SPeter Wemm { 403c2aa98e2SPeter Wemm *bp++ = '.'; 404c2aa98e2SPeter Wemm n++; 405c2aa98e2SPeter Wemm } 406c2aa98e2SPeter Wemm *bp++ = '\0'; 40740266059SGregory Neil Shapiro if (buflen < n + 1) 40840266059SGregory Neil Shapiro { 40940266059SGregory Neil Shapiro /* don't want to wrap buflen */ 41040266059SGregory Neil Shapiro break; 41140266059SGregory Neil Shapiro } 412c2aa98e2SPeter Wemm buflen -= n + 1; 413c2aa98e2SPeter Wemm } 414c2aa98e2SPeter Wemm 41540266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 41640266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 41740266059SGregory Neil Shapiro *pttl = ttl; 41840266059SGregory Neil Shapiro 419c2aa98e2SPeter Wemm /* sort the records */ 420c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 421c2aa98e2SPeter Wemm { 422c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 423c2aa98e2SPeter Wemm { 42406f25ae9SGregory Neil Shapiro if (prefs[i] > prefs[j] || 42506f25ae9SGregory Neil Shapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 426c2aa98e2SPeter Wemm { 427c2aa98e2SPeter Wemm register int temp; 428c2aa98e2SPeter Wemm register char *temp1; 429c2aa98e2SPeter Wemm 43006f25ae9SGregory Neil Shapiro temp = prefs[i]; 43106f25ae9SGregory Neil Shapiro prefs[i] = prefs[j]; 43206f25ae9SGregory Neil Shapiro prefs[j] = temp; 433c2aa98e2SPeter Wemm temp1 = mxhosts[i]; 434c2aa98e2SPeter Wemm mxhosts[i] = mxhosts[j]; 435c2aa98e2SPeter Wemm mxhosts[j] = temp1; 436c2aa98e2SPeter Wemm temp = weight[i]; 437c2aa98e2SPeter Wemm weight[i] = weight[j]; 438c2aa98e2SPeter Wemm weight[j] = temp; 439c2aa98e2SPeter Wemm } 440c2aa98e2SPeter Wemm } 44106f25ae9SGregory Neil Shapiro if (seenlocal && prefs[i] >= localpref) 442c2aa98e2SPeter Wemm { 443c2aa98e2SPeter Wemm /* truncate higher preference part of list */ 444c2aa98e2SPeter Wemm nmx = i; 445c2aa98e2SPeter Wemm } 446c2aa98e2SPeter Wemm } 447c2aa98e2SPeter Wemm 448c2aa98e2SPeter Wemm /* delete duplicates from list (yes, some bozos have duplicates) */ 449c2aa98e2SPeter Wemm for (i = 0; i < nmx - 1; ) 450c2aa98e2SPeter Wemm { 45140266059SGregory Neil Shapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 452c2aa98e2SPeter Wemm i++; 453c2aa98e2SPeter Wemm else 454c2aa98e2SPeter Wemm { 455c2aa98e2SPeter Wemm /* compress out duplicate */ 456c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 45706f25ae9SGregory Neil Shapiro { 458c2aa98e2SPeter Wemm mxhosts[j] = mxhosts[j + 1]; 45906f25ae9SGregory Neil Shapiro prefs[j] = prefs[j + 1]; 46006f25ae9SGregory Neil Shapiro } 461c2aa98e2SPeter Wemm nmx--; 462c2aa98e2SPeter Wemm } 463c2aa98e2SPeter Wemm } 464c2aa98e2SPeter Wemm 465c2aa98e2SPeter Wemm if (nmx == 0) 466c2aa98e2SPeter Wemm { 467c2aa98e2SPeter Wemm punt: 46806f25ae9SGregory Neil Shapiro if (seenlocal) 469c2aa98e2SPeter Wemm { 47006f25ae9SGregory Neil Shapiro struct hostent *h = NULL; 47106f25ae9SGregory Neil Shapiro 472c2aa98e2SPeter Wemm /* 473c2aa98e2SPeter Wemm ** If we have deleted all MX entries, this is 474c2aa98e2SPeter Wemm ** an error -- we should NEVER send to a host that 475c2aa98e2SPeter Wemm ** has an MX, and this should have been caught 476c2aa98e2SPeter Wemm ** earlier in the config file. 477c2aa98e2SPeter Wemm ** 478c2aa98e2SPeter Wemm ** Some sites prefer to go ahead and try the 479c2aa98e2SPeter Wemm ** A record anyway; that case is handled by 480c2aa98e2SPeter Wemm ** setting TryNullMXList. I believe this is a 481c2aa98e2SPeter Wemm ** bad idea, but it's up to you.... 482c2aa98e2SPeter Wemm */ 483c2aa98e2SPeter Wemm 48406f25ae9SGregory Neil Shapiro if (TryNullMXList) 48506f25ae9SGregory Neil Shapiro { 486602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 48706f25ae9SGregory Neil Shapiro errno = 0; 48806f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET); 48906f25ae9SGregory Neil Shapiro if (h == NULL) 49006f25ae9SGregory Neil Shapiro { 49106f25ae9SGregory Neil Shapiro if (errno == ETIMEDOUT || 49206f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 49306f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 49406f25ae9SGregory Neil Shapiro UseNameServer)) 49506f25ae9SGregory Neil Shapiro { 49606f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 49706f25ae9SGregory Neil Shapiro return -1; 49806f25ae9SGregory Neil Shapiro } 49906f25ae9SGregory Neil Shapiro # if NETINET6 500602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 50106f25ae9SGregory Neil Shapiro errno = 0; 50206f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET6); 50306f25ae9SGregory Neil Shapiro if (h == NULL && 50406f25ae9SGregory Neil Shapiro (errno == ETIMEDOUT || 50506f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 50606f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 50706f25ae9SGregory Neil Shapiro UseNameServer))) 50806f25ae9SGregory Neil Shapiro { 50906f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 51006f25ae9SGregory Neil Shapiro return -1; 51106f25ae9SGregory Neil Shapiro } 51206f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 51306f25ae9SGregory Neil Shapiro } 51406f25ae9SGregory Neil Shapiro } 51506f25ae9SGregory Neil Shapiro 51606f25ae9SGregory Neil Shapiro if (h == NULL) 51706f25ae9SGregory Neil Shapiro { 518c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 519c2aa98e2SPeter Wemm syserr("MX list for %s points back to %s", 520c2aa98e2SPeter Wemm host, MyHostName); 521c2aa98e2SPeter Wemm return -1; 522c2aa98e2SPeter Wemm } 52340266059SGregory Neil Shapiro # if NETINET6 524193538b7SGregory Neil Shapiro freehostent(h); 525af9557fdSGregory Neil Shapiro h = NULL; 52640266059SGregory Neil Shapiro # endif /* NETINET6 */ 52706f25ae9SGregory Neil Shapiro } 528d0cef73dSGregory Neil Shapiro if (strlen(host) >= sizeof(MXHostBuf)) 529c2aa98e2SPeter Wemm { 530c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 531c2aa98e2SPeter Wemm syserr("Host name %s too long", 532c2aa98e2SPeter Wemm shortenstring(host, MAXSHORTSTR)); 533c2aa98e2SPeter Wemm return -1; 534c2aa98e2SPeter Wemm } 535d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf)); 536c2aa98e2SPeter Wemm mxhosts[0] = MXHostBuf; 53706f25ae9SGregory Neil Shapiro prefs[0] = 0; 538c2aa98e2SPeter Wemm if (host[0] == '[') 539c2aa98e2SPeter Wemm { 540c2aa98e2SPeter Wemm register char *p; 54106f25ae9SGregory Neil Shapiro # if NETINET6 54206f25ae9SGregory Neil Shapiro struct sockaddr_in6 tmp6; 54306f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 544c2aa98e2SPeter Wemm 545c2aa98e2SPeter Wemm /* this may be an MX suppression-style address */ 546c2aa98e2SPeter Wemm p = strchr(MXHostBuf, ']'); 547c2aa98e2SPeter Wemm if (p != NULL) 548c2aa98e2SPeter Wemm { 549c2aa98e2SPeter Wemm *p = '\0'; 55006f25ae9SGregory Neil Shapiro 551c2aa98e2SPeter Wemm if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 552c2aa98e2SPeter Wemm { 553c2aa98e2SPeter Wemm nmx++; 554c2aa98e2SPeter Wemm *p = ']'; 555c2aa98e2SPeter Wemm } 55606f25ae9SGregory Neil Shapiro # if NETINET6 55740266059SGregory Neil Shapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 55806f25ae9SGregory Neil Shapiro &tmp6.sin6_addr) == 1) 55906f25ae9SGregory Neil Shapiro { 56006f25ae9SGregory Neil Shapiro nmx++; 56106f25ae9SGregory Neil Shapiro *p = ']'; 56206f25ae9SGregory Neil Shapiro } 56306f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 564c2aa98e2SPeter Wemm else 565c2aa98e2SPeter Wemm { 56640266059SGregory Neil Shapiro trycanon = true; 567c2aa98e2SPeter Wemm mxhosts[0]++; 568c2aa98e2SPeter Wemm } 569c2aa98e2SPeter Wemm } 570c2aa98e2SPeter Wemm } 571c2aa98e2SPeter Wemm if (trycanon && 572d0cef73dSGregory Neil Shapiro getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, pttl)) 573c2aa98e2SPeter Wemm { 57440266059SGregory Neil Shapiro /* XXX MXHostBuf == "" ? is that possible? */ 575c2aa98e2SPeter Wemm bp = &MXHostBuf[strlen(MXHostBuf)]; 576c2aa98e2SPeter Wemm if (bp[-1] != '.') 577c2aa98e2SPeter Wemm { 578c2aa98e2SPeter Wemm *bp++ = '.'; 579c2aa98e2SPeter Wemm *bp = '\0'; 580c2aa98e2SPeter Wemm } 581c2aa98e2SPeter Wemm nmx = 1; 582c2aa98e2SPeter Wemm } 583c2aa98e2SPeter Wemm } 584c2aa98e2SPeter Wemm 585c2aa98e2SPeter Wemm /* if we have a default lowest preference, include that */ 586c2aa98e2SPeter Wemm if (fallbackMX != NULL && !seenlocal) 58706f25ae9SGregory Neil Shapiro { 58840266059SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 58906f25ae9SGregory Neil Shapiro } 59006f25ae9SGregory Neil Shapiro return nmx; 591c2aa98e2SPeter Wemm } 59240266059SGregory Neil Shapiro /* 593c2aa98e2SPeter Wemm ** MXRAND -- create a randomizer for equal MX preferences 594c2aa98e2SPeter Wemm ** 595c2aa98e2SPeter Wemm ** If two MX hosts have equal preferences we want to randomize 596c2aa98e2SPeter Wemm ** the selection. But in order for signatures to be the same, 597c2aa98e2SPeter Wemm ** we need to randomize the same way each time. This function 598c2aa98e2SPeter Wemm ** computes a pseudo-random hash function from the host name. 599c2aa98e2SPeter Wemm ** 600c2aa98e2SPeter Wemm ** Parameters: 601c2aa98e2SPeter Wemm ** host -- the name of the host. 602c2aa98e2SPeter Wemm ** 603c2aa98e2SPeter Wemm ** Returns: 604c2aa98e2SPeter Wemm ** A random but repeatable value based on the host name. 605c2aa98e2SPeter Wemm */ 606c2aa98e2SPeter Wemm 60706f25ae9SGregory Neil Shapiro static int 608c2aa98e2SPeter Wemm mxrand(host) 609c2aa98e2SPeter Wemm register char *host; 610c2aa98e2SPeter Wemm { 611c2aa98e2SPeter Wemm int hfunc; 612c2aa98e2SPeter Wemm static unsigned int seed; 613c2aa98e2SPeter Wemm 614c2aa98e2SPeter Wemm if (seed == 0) 615c2aa98e2SPeter Wemm { 616c2aa98e2SPeter Wemm seed = (int) curtime() & 0xffff; 617c2aa98e2SPeter Wemm if (seed == 0) 618c2aa98e2SPeter Wemm seed++; 619c2aa98e2SPeter Wemm } 620c2aa98e2SPeter Wemm 621c2aa98e2SPeter Wemm if (tTd(17, 9)) 62240266059SGregory Neil Shapiro sm_dprintf("mxrand(%s)", host); 623c2aa98e2SPeter Wemm 624c2aa98e2SPeter Wemm hfunc = seed; 625c2aa98e2SPeter Wemm while (*host != '\0') 626c2aa98e2SPeter Wemm { 627c2aa98e2SPeter Wemm int c = *host++; 628c2aa98e2SPeter Wemm 629c2aa98e2SPeter Wemm if (isascii(c) && isupper(c)) 630c2aa98e2SPeter Wemm c = tolower(c); 631c2aa98e2SPeter Wemm hfunc = ((hfunc << 1) ^ c) % 2003; 632c2aa98e2SPeter Wemm } 633c2aa98e2SPeter Wemm 634c2aa98e2SPeter Wemm hfunc &= 0xff; 635c2aa98e2SPeter Wemm hfunc++; 636c2aa98e2SPeter Wemm 637c2aa98e2SPeter Wemm if (tTd(17, 9)) 63840266059SGregory Neil Shapiro sm_dprintf(" = %d\n", hfunc); 639c2aa98e2SPeter Wemm return hfunc; 640c2aa98e2SPeter Wemm } 64140266059SGregory Neil Shapiro /* 642c2aa98e2SPeter Wemm ** BESTMX -- find the best MX for a name 643c2aa98e2SPeter Wemm ** 644c2aa98e2SPeter Wemm ** This is really a hack, but I don't see any obvious way 645c2aa98e2SPeter Wemm ** to generalize it at the moment. 646c2aa98e2SPeter Wemm */ 647c2aa98e2SPeter Wemm 648c2aa98e2SPeter Wemm /* ARGSUSED3 */ 649c2aa98e2SPeter Wemm char * 650c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp) 651c2aa98e2SPeter Wemm MAP *map; 652c2aa98e2SPeter Wemm char *name; 653c2aa98e2SPeter Wemm char **av; 654c2aa98e2SPeter Wemm int *statp; 655c2aa98e2SPeter Wemm { 656c2aa98e2SPeter Wemm int nmx; 657c2aa98e2SPeter Wemm int saveopts = _res.options; 65840266059SGregory Neil Shapiro int i; 65940266059SGregory Neil Shapiro ssize_t len = 0; 66040266059SGregory Neil Shapiro char *result; 661c2aa98e2SPeter Wemm char *mxhosts[MAXMXHOSTS + 1]; 66240266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 66340266059SGregory Neil Shapiro char *buf; 66440266059SGregory Neil Shapiro #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 66540266059SGregory Neil Shapiro char *p; 666065a643dSPeter Wemm char buf[PSBUFSIZE / 2]; 66740266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 668c2aa98e2SPeter Wemm 669c2aa98e2SPeter Wemm _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 670959366dcSGregory Neil Shapiro nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); 671c2aa98e2SPeter Wemm _res.options = saveopts; 672c2aa98e2SPeter Wemm if (nmx <= 0) 673c2aa98e2SPeter Wemm return NULL; 674c2aa98e2SPeter Wemm if (bitset(MF_MATCHONLY, map->map_mflags)) 675c2aa98e2SPeter Wemm return map_rewrite(map, name, strlen(name), NULL); 676c2aa98e2SPeter Wemm if ((map->map_coldelim == '\0') || (nmx == 1)) 677c2aa98e2SPeter Wemm return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 678c2aa98e2SPeter Wemm 679c2aa98e2SPeter Wemm /* 680c2aa98e2SPeter Wemm ** We were given a -z flag (return all MXs) and there are multiple 681c2aa98e2SPeter Wemm ** ones. We need to build them all into a list. 682c2aa98e2SPeter Wemm */ 68340266059SGregory Neil Shapiro 68440266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 68540266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 68640266059SGregory Neil Shapiro { 68740266059SGregory Neil Shapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 68840266059SGregory Neil Shapiro { 68940266059SGregory Neil Shapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 69040266059SGregory Neil Shapiro mxhosts[i], map->map_coldelim); 69140266059SGregory Neil Shapiro return NULL; 69240266059SGregory Neil Shapiro } 69340266059SGregory Neil Shapiro len += strlen(mxhosts[i]) + 1; 69440266059SGregory Neil Shapiro if (len < 0) 69540266059SGregory Neil Shapiro { 69640266059SGregory Neil Shapiro len -= strlen(mxhosts[i]) + 1; 69740266059SGregory Neil Shapiro break; 69840266059SGregory Neil Shapiro } 69940266059SGregory Neil Shapiro } 70040266059SGregory Neil Shapiro buf = (char *) sm_malloc(len); 70140266059SGregory Neil Shapiro if (buf == NULL) 70240266059SGregory Neil Shapiro { 70340266059SGregory Neil Shapiro *statp = EX_UNAVAILABLE; 70440266059SGregory Neil Shapiro return NULL; 70540266059SGregory Neil Shapiro } 70640266059SGregory Neil Shapiro *buf = '\0'; 70740266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 70840266059SGregory Neil Shapiro { 70940266059SGregory Neil Shapiro int end; 71040266059SGregory Neil Shapiro 71140266059SGregory Neil Shapiro end = sm_strlcat(buf, mxhosts[i], len); 71240266059SGregory Neil Shapiro if (i != nmx && end + 1 < len) 71340266059SGregory Neil Shapiro { 71440266059SGregory Neil Shapiro buf[end] = map->map_coldelim; 71540266059SGregory Neil Shapiro buf[end + 1] = '\0'; 71640266059SGregory Neil Shapiro } 71740266059SGregory Neil Shapiro } 71840266059SGregory Neil Shapiro 71940266059SGregory Neil Shapiro /* Cleanly truncate for rulesets */ 72040266059SGregory Neil Shapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 72140266059SGregory Neil Shapiro #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 722c2aa98e2SPeter Wemm p = buf; 723c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 724c2aa98e2SPeter Wemm { 72540266059SGregory Neil Shapiro size_t slen; 726c2aa98e2SPeter Wemm 727c2aa98e2SPeter Wemm if (strchr(mxhosts[i], map->map_coldelim) != NULL) 728c2aa98e2SPeter Wemm { 729c2aa98e2SPeter Wemm syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 730c2aa98e2SPeter Wemm mxhosts[i], map->map_coldelim); 731c2aa98e2SPeter Wemm return NULL; 732c2aa98e2SPeter Wemm } 733c2aa98e2SPeter Wemm slen = strlen(mxhosts[i]); 734d0cef73dSGregory Neil Shapiro if (len + slen + 2 > sizeof(buf)) 735c2aa98e2SPeter Wemm break; 736c2aa98e2SPeter Wemm if (i > 0) 737c2aa98e2SPeter Wemm { 738c2aa98e2SPeter Wemm *p++ = map->map_coldelim; 739c2aa98e2SPeter Wemm len++; 740c2aa98e2SPeter Wemm } 741d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len); 742c2aa98e2SPeter Wemm p += slen; 743c2aa98e2SPeter Wemm len += slen; 744c2aa98e2SPeter Wemm } 74540266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 74640266059SGregory Neil Shapiro 74740266059SGregory Neil Shapiro result = map_rewrite(map, buf, len, av); 74840266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 74940266059SGregory Neil Shapiro sm_free(buf); 75040266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 75140266059SGregory Neil Shapiro return result; 752c2aa98e2SPeter Wemm } 75340266059SGregory Neil Shapiro /* 754c2aa98e2SPeter Wemm ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 755c2aa98e2SPeter Wemm ** 756c2aa98e2SPeter Wemm ** This algorithm tries to be smart about wildcard MX records. 757c2aa98e2SPeter Wemm ** This is hard to do because DNS doesn't tell is if we matched 758c2aa98e2SPeter Wemm ** against a wildcard or a specific MX. 759c2aa98e2SPeter Wemm ** 760c2aa98e2SPeter Wemm ** We always prefer A & CNAME records, since these are presumed 761c2aa98e2SPeter Wemm ** to be specific. 762c2aa98e2SPeter Wemm ** 763c2aa98e2SPeter Wemm ** If we match an MX in one pass and lose it in the next, we use 764c2aa98e2SPeter Wemm ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 765c2aa98e2SPeter Wemm ** A hostname bletch.foo.bar.com will match against this MX, but 766c2aa98e2SPeter Wemm ** will stop matching when we try bletch.bar.com -- so we know 767c2aa98e2SPeter Wemm ** that bletch.foo.bar.com must have been right. This fails if 768c2aa98e2SPeter Wemm ** there was also an MX record matching *.BAR.COM, but there are 769c2aa98e2SPeter Wemm ** some things that just can't be fixed. 770c2aa98e2SPeter Wemm ** 771c2aa98e2SPeter Wemm ** Parameters: 772c2aa98e2SPeter Wemm ** host -- a buffer containing the name of the host. 773c2aa98e2SPeter Wemm ** This is a value-result parameter. 774c2aa98e2SPeter Wemm ** hbsize -- the size of the host buffer. 775c2aa98e2SPeter Wemm ** trymx -- if set, try MX records as well as A and CNAME. 776c2aa98e2SPeter Wemm ** statp -- pointer to place to store status. 77740266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 778c2aa98e2SPeter Wemm ** 779c2aa98e2SPeter Wemm ** Returns: 78040266059SGregory Neil Shapiro ** true -- if the host matched. 78140266059SGregory Neil Shapiro ** false -- otherwise. 782c2aa98e2SPeter Wemm */ 783c2aa98e2SPeter Wemm 784c2aa98e2SPeter Wemm bool 78540266059SGregory Neil Shapiro dns_getcanonname(host, hbsize, trymx, statp, pttl) 786c2aa98e2SPeter Wemm char *host; 787c2aa98e2SPeter Wemm int hbsize; 788c2aa98e2SPeter Wemm bool trymx; 789c2aa98e2SPeter Wemm int *statp; 79040266059SGregory Neil Shapiro int *pttl; 791c2aa98e2SPeter Wemm { 79240266059SGregory Neil Shapiro register unsigned char *eom, *ap; 793c2aa98e2SPeter Wemm register char *cp; 794c2aa98e2SPeter Wemm register int n; 795c2aa98e2SPeter Wemm HEADER *hp; 796c2aa98e2SPeter Wemm querybuf answer; 797c2aa98e2SPeter Wemm int ancount, qdcount; 798c2aa98e2SPeter Wemm int ret; 799c2aa98e2SPeter Wemm char **domain; 800c2aa98e2SPeter Wemm int type; 80140266059SGregory Neil Shapiro int ttl = 0; 802c2aa98e2SPeter Wemm char **dp; 803c2aa98e2SPeter Wemm char *mxmatch; 804c2aa98e2SPeter Wemm bool amatch; 80540266059SGregory Neil Shapiro bool gotmx = false; 806c2aa98e2SPeter Wemm int qtype; 807a7ec597cSGregory Neil Shapiro int initial; 808c2aa98e2SPeter Wemm int loopcnt; 80940266059SGregory Neil Shapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 810c2aa98e2SPeter Wemm char *searchlist[MAXDNSRCH + 2]; 811c2aa98e2SPeter Wemm 812c2aa98e2SPeter Wemm if (tTd(8, 2)) 81340266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 814c2aa98e2SPeter Wemm 815c2aa98e2SPeter Wemm if ((_res.options & RES_INIT) == 0 && res_init() == -1) 816c2aa98e2SPeter Wemm { 817c2aa98e2SPeter Wemm *statp = EX_UNAVAILABLE; 81840266059SGregory Neil Shapiro return false; 819c2aa98e2SPeter Wemm } 820c2aa98e2SPeter Wemm 821193538b7SGregory Neil Shapiro *statp = EX_OK; 822193538b7SGregory Neil Shapiro 823c2aa98e2SPeter Wemm /* 824c2aa98e2SPeter Wemm ** Initialize domain search list. If there is at least one 825c2aa98e2SPeter Wemm ** dot in the name, search the unmodified name first so we 826c2aa98e2SPeter Wemm ** find "vse.CS" in Czechoslovakia instead of in the local 82706f25ae9SGregory Neil Shapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 82806f25ae9SGregory Neil Shapiro ** longer a country named Czechoslovakia but this type of problem 82906f25ae9SGregory Neil Shapiro ** is still present. 830c2aa98e2SPeter Wemm ** 831c2aa98e2SPeter Wemm ** Older versions of the resolver could create this 832c2aa98e2SPeter Wemm ** list by tearing apart the host name. 833c2aa98e2SPeter Wemm */ 834c2aa98e2SPeter Wemm 835c2aa98e2SPeter Wemm loopcnt = 0; 836c2aa98e2SPeter Wemm cnameloop: 837c2aa98e2SPeter Wemm /* Check for dots in the name */ 838c2aa98e2SPeter Wemm for (cp = host, n = 0; *cp != '\0'; cp++) 839c2aa98e2SPeter Wemm if (*cp == '.') 840c2aa98e2SPeter Wemm n++; 841c2aa98e2SPeter Wemm 842c2aa98e2SPeter Wemm /* 843c2aa98e2SPeter Wemm ** Build the search list. 844c2aa98e2SPeter Wemm ** If there is at least one dot in name, start with a null 845c2aa98e2SPeter Wemm ** domain to search the unmodified name first. 846c2aa98e2SPeter Wemm ** If name does not end with a dot and search up local domain 847c2aa98e2SPeter Wemm ** tree desired, append each local domain component to the 848c2aa98e2SPeter Wemm ** search list; if name contains no dots and default domain 849c2aa98e2SPeter Wemm ** name is desired, append default domain name to search list; 850c2aa98e2SPeter Wemm ** else if name ends in a dot, remove that dot. 851c2aa98e2SPeter Wemm */ 852c2aa98e2SPeter Wemm 853c2aa98e2SPeter Wemm dp = searchlist; 854c2aa98e2SPeter Wemm if (n > 0) 855c2aa98e2SPeter Wemm *dp++ = ""; 856c2aa98e2SPeter Wemm if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 857c2aa98e2SPeter Wemm { 85806f25ae9SGregory Neil Shapiro /* make sure there are less than MAXDNSRCH domains */ 85906f25ae9SGregory Neil Shapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 86006f25ae9SGregory Neil Shapiro *domain != NULL && ret < MAXDNSRCH; 86106f25ae9SGregory Neil Shapiro ret++) 862c2aa98e2SPeter Wemm *dp++ = *domain++; 863c2aa98e2SPeter Wemm } 864c2aa98e2SPeter Wemm else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 865c2aa98e2SPeter Wemm { 866c2aa98e2SPeter Wemm *dp++ = _res.defdname; 867c2aa98e2SPeter Wemm } 868c2aa98e2SPeter Wemm else if (*cp == '.') 869c2aa98e2SPeter Wemm { 870c2aa98e2SPeter Wemm *cp = '\0'; 871c2aa98e2SPeter Wemm } 872c2aa98e2SPeter Wemm *dp = NULL; 873c2aa98e2SPeter Wemm 874c2aa98e2SPeter Wemm /* 875c2aa98e2SPeter Wemm ** Now loop through the search list, appending each domain in turn 876c2aa98e2SPeter Wemm ** name and searching for a match. 877c2aa98e2SPeter Wemm */ 878c2aa98e2SPeter Wemm 879c2aa98e2SPeter Wemm mxmatch = NULL; 880a7ec597cSGregory Neil Shapiro initial = T_A; 881a7ec597cSGregory Neil Shapiro # if NETINET6 882a7ec597cSGregory Neil Shapiro if (InetMode == AF_INET6) 883a7ec597cSGregory Neil Shapiro initial = T_AAAA; 884a7ec597cSGregory Neil Shapiro # endif /* NETINET6 */ 885a7ec597cSGregory Neil Shapiro qtype = initial; 886c2aa98e2SPeter Wemm 887c2aa98e2SPeter Wemm for (dp = searchlist; *dp != NULL; ) 888c2aa98e2SPeter Wemm { 889a7ec597cSGregory Neil Shapiro if (qtype == initial) 89040266059SGregory Neil Shapiro gotmx = false; 891c2aa98e2SPeter Wemm if (tTd(8, 5)) 89240266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 893c2aa98e2SPeter Wemm host, *dp, 89406f25ae9SGregory Neil Shapiro # if NETINET6 89506f25ae9SGregory Neil Shapiro qtype == T_AAAA ? "AAAA" : 89606f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 89706f25ae9SGregory Neil Shapiro qtype == T_A ? "A" : 89806f25ae9SGregory Neil Shapiro qtype == T_MX ? "MX" : 89906f25ae9SGregory Neil Shapiro "???"); 900193538b7SGregory Neil Shapiro errno = 0; 901c2aa98e2SPeter Wemm ret = res_querydomain(host, *dp, C_IN, qtype, 902c2aa98e2SPeter Wemm answer.qb2, sizeof(answer.qb2)); 903c2aa98e2SPeter Wemm if (ret <= 0) 904c2aa98e2SPeter Wemm { 90540266059SGregory Neil Shapiro int save_errno = errno; 906c2aa98e2SPeter Wemm 90740266059SGregory Neil Shapiro if (tTd(8, 7)) 90840266059SGregory Neil Shapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 90940266059SGregory Neil Shapiro save_errno, h_errno); 91040266059SGregory Neil Shapiro 91140266059SGregory Neil Shapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 912c2aa98e2SPeter Wemm { 913193538b7SGregory Neil Shapiro /* 91440266059SGregory Neil Shapiro ** the name server seems to be down or broken. 915193538b7SGregory Neil Shapiro */ 916193538b7SGregory Neil Shapiro 917602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(TRY_AGAIN); 918605302a5SGregory Neil Shapiro if (**dp == '\0') 919605302a5SGregory Neil Shapiro { 920605302a5SGregory Neil Shapiro if (*statp == EX_OK) 921605302a5SGregory Neil Shapiro *statp = EX_TEMPFAIL; 922605302a5SGregory Neil Shapiro goto nexttype; 923605302a5SGregory Neil Shapiro } 924c2aa98e2SPeter Wemm *statp = EX_TEMPFAIL; 92506f25ae9SGregory Neil Shapiro 926602a2b1bSGregory Neil Shapiro if (WorkAroundBrokenAAAA) 927602a2b1bSGregory Neil Shapiro { 928193538b7SGregory Neil Shapiro /* 929193538b7SGregory Neil Shapiro ** Only return if not TRY_AGAIN as an 930193538b7SGregory Neil Shapiro ** attempt with a different qtype may 931193538b7SGregory Neil Shapiro ** succeed (res_querydomain() calls 932193538b7SGregory Neil Shapiro ** res_query() calls res_send() which 933193538b7SGregory Neil Shapiro ** sets errno to ETIMEDOUT if the 934193538b7SGregory Neil Shapiro ** nameservers could be contacted but 935193538b7SGregory Neil Shapiro ** didn't give an answer). 936193538b7SGregory Neil Shapiro */ 937193538b7SGregory Neil Shapiro 93840266059SGregory Neil Shapiro if (save_errno != ETIMEDOUT) 93940266059SGregory Neil Shapiro return false; 940602a2b1bSGregory Neil Shapiro } 94140266059SGregory Neil Shapiro else 94240266059SGregory Neil Shapiro return false; 943c2aa98e2SPeter Wemm } 944c2aa98e2SPeter Wemm 945605302a5SGregory Neil Shapiro nexttype: 946c2aa98e2SPeter Wemm if (h_errno != HOST_NOT_FOUND) 947c2aa98e2SPeter Wemm { 948c2aa98e2SPeter Wemm /* might have another type of interest */ 94906f25ae9SGregory Neil Shapiro # if NETINET6 95040266059SGregory Neil Shapiro if (qtype == T_AAAA) 95106f25ae9SGregory Neil Shapiro { 952c2aa98e2SPeter Wemm qtype = T_A; 953c2aa98e2SPeter Wemm continue; 954c2aa98e2SPeter Wemm } 95540266059SGregory Neil Shapiro else 95606f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 95740266059SGregory Neil Shapiro if (qtype == T_A && !gotmx && 95806f25ae9SGregory Neil Shapiro (trymx || **dp == '\0')) 959c2aa98e2SPeter Wemm { 960c2aa98e2SPeter Wemm qtype = T_MX; 961c2aa98e2SPeter Wemm continue; 962c2aa98e2SPeter Wemm } 963c2aa98e2SPeter Wemm } 964c2aa98e2SPeter Wemm 965c2aa98e2SPeter Wemm /* definite no -- try the next domain */ 966c2aa98e2SPeter Wemm dp++; 967a7ec597cSGregory Neil Shapiro qtype = initial; 968c2aa98e2SPeter Wemm continue; 969c2aa98e2SPeter Wemm } 970c2aa98e2SPeter Wemm else if (tTd(8, 7)) 97140266059SGregory Neil Shapiro sm_dprintf("\tYES\n"); 972c2aa98e2SPeter Wemm 973c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 974c2aa98e2SPeter Wemm if (ret > sizeof(answer)) 975c2aa98e2SPeter Wemm ret = sizeof(answer); 976af9557fdSGregory Neil Shapiro SM_ASSERT(ret >= 0); 977c2aa98e2SPeter Wemm 978c2aa98e2SPeter Wemm /* 979c2aa98e2SPeter Wemm ** Appear to have a match. Confirm it by searching for A or 980c2aa98e2SPeter Wemm ** CNAME records. If we don't have a local domain 981c2aa98e2SPeter Wemm ** wild card MX record, we will accept MX as well. 982c2aa98e2SPeter Wemm */ 983c2aa98e2SPeter Wemm 984c2aa98e2SPeter Wemm hp = (HEADER *) &answer; 98540266059SGregory Neil Shapiro ap = (unsigned char *) &answer + HFIXEDSZ; 98640266059SGregory Neil Shapiro eom = (unsigned char *) &answer + ret; 987c2aa98e2SPeter Wemm 988c2aa98e2SPeter Wemm /* skip question part of response -- we know what we asked */ 98940266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 99006f25ae9SGregory Neil Shapiro qdcount--; 99106f25ae9SGregory Neil Shapiro ap += ret + QFIXEDSZ) 992c2aa98e2SPeter Wemm { 993c2aa98e2SPeter Wemm if ((ret = dn_skipname(ap, eom)) < 0) 994c2aa98e2SPeter Wemm { 995c2aa98e2SPeter Wemm if (tTd(8, 20)) 99640266059SGregory Neil Shapiro sm_dprintf("qdcount failure (%d)\n", 99740266059SGregory Neil Shapiro ntohs((unsigned short) hp->qdcount)); 998c2aa98e2SPeter Wemm *statp = EX_SOFTWARE; 99940266059SGregory Neil Shapiro return false; /* ???XXX??? */ 1000c2aa98e2SPeter Wemm } 1001c2aa98e2SPeter Wemm } 1002c2aa98e2SPeter Wemm 100340266059SGregory Neil Shapiro amatch = false; 100440266059SGregory Neil Shapiro for (ancount = ntohs((unsigned short) hp->ancount); 100506f25ae9SGregory Neil Shapiro --ancount >= 0 && ap < eom; 1006c2aa98e2SPeter Wemm ap += n) 1007c2aa98e2SPeter Wemm { 100840266059SGregory Neil Shapiro n = dn_expand((unsigned char *) &answer, eom, ap, 1009d0cef73dSGregory Neil Shapiro (RES_UNC_T) nbuf, sizeof(nbuf)); 1010c2aa98e2SPeter Wemm if (n < 0) 1011c2aa98e2SPeter Wemm break; 1012c2aa98e2SPeter Wemm ap += n; 1013c2aa98e2SPeter Wemm GETSHORT(type, ap); 101440266059SGregory Neil Shapiro ap += INT16SZ; /* skip over class */ 101540266059SGregory Neil Shapiro GETLONG(ttl, ap); 101640266059SGregory Neil Shapiro GETSHORT(n, ap); /* rdlength */ 1017c2aa98e2SPeter Wemm switch (type) 1018c2aa98e2SPeter Wemm { 1019c2aa98e2SPeter Wemm case T_MX: 102040266059SGregory Neil Shapiro gotmx = true; 1021c2aa98e2SPeter Wemm if (**dp != '\0' && HasWildcardMX) 1022c2aa98e2SPeter Wemm { 1023c2aa98e2SPeter Wemm /* 1024c2aa98e2SPeter Wemm ** If we are using MX matches and have 1025c2aa98e2SPeter Wemm ** not yet gotten one, save this one 1026c2aa98e2SPeter Wemm ** but keep searching for an A or 1027c2aa98e2SPeter Wemm ** CNAME match. 1028c2aa98e2SPeter Wemm */ 1029c2aa98e2SPeter Wemm 1030c2aa98e2SPeter Wemm if (trymx && mxmatch == NULL) 1031c2aa98e2SPeter Wemm mxmatch = *dp; 1032c2aa98e2SPeter Wemm continue; 1033c2aa98e2SPeter Wemm } 1034c2aa98e2SPeter Wemm 1035c2aa98e2SPeter Wemm /* 1036c2aa98e2SPeter Wemm ** If we did not append a domain name, this 1037c2aa98e2SPeter Wemm ** must have been a canonical name to start 1038c2aa98e2SPeter Wemm ** with. Even if we did append a domain name, 1039c2aa98e2SPeter Wemm ** in the absence of a wildcard MX this must 1040c2aa98e2SPeter Wemm ** still be a real MX match. 1041c2aa98e2SPeter Wemm ** Such MX matches are as good as an A match, 1042c2aa98e2SPeter Wemm ** fall through. 1043c2aa98e2SPeter Wemm */ 104406f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 104506f25ae9SGregory Neil Shapiro 104606f25ae9SGregory Neil Shapiro # if NETINET6 104706f25ae9SGregory Neil Shapiro case T_AAAA: 104806f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 1049c2aa98e2SPeter Wemm case T_A: 1050c2aa98e2SPeter Wemm /* Flag that a good match was found */ 105140266059SGregory Neil Shapiro amatch = true; 1052c2aa98e2SPeter Wemm 1053c2aa98e2SPeter Wemm /* continue in case a CNAME also exists */ 1054c2aa98e2SPeter Wemm continue; 1055c2aa98e2SPeter Wemm 1056c2aa98e2SPeter Wemm case T_CNAME: 1057c2aa98e2SPeter Wemm if (DontExpandCnames) 1058c2aa98e2SPeter Wemm { 1059c2aa98e2SPeter Wemm /* got CNAME -- guaranteed canonical */ 106040266059SGregory Neil Shapiro amatch = true; 1061c2aa98e2SPeter Wemm break; 1062c2aa98e2SPeter Wemm } 1063c2aa98e2SPeter Wemm 1064c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH) 1065c2aa98e2SPeter Wemm { 1066c2aa98e2SPeter Wemm /*XXX should notify postmaster XXX*/ 1067c2aa98e2SPeter Wemm message("DNS failure: CNAME loop for %s", 1068c2aa98e2SPeter Wemm host); 1069c2aa98e2SPeter Wemm if (CurEnv->e_message == NULL) 1070c2aa98e2SPeter Wemm { 1071c2aa98e2SPeter Wemm char ebuf[MAXLINE]; 1072c2aa98e2SPeter Wemm 107340266059SGregory Neil Shapiro (void) sm_snprintf(ebuf, 1074d0cef73dSGregory Neil Shapiro sizeof(ebuf), 1075c2aa98e2SPeter Wemm "Deferred: DNS failure: CNAME loop for %.100s", 1076c2aa98e2SPeter Wemm host); 107740266059SGregory Neil Shapiro CurEnv->e_message = 107840266059SGregory Neil Shapiro sm_rpool_strdup_x( 107940266059SGregory Neil Shapiro CurEnv->e_rpool, ebuf); 1080c2aa98e2SPeter Wemm } 1081602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(NO_RECOVERY); 1082c2aa98e2SPeter Wemm *statp = EX_CONFIG; 108340266059SGregory Neil Shapiro return false; 1084c2aa98e2SPeter Wemm } 1085c2aa98e2SPeter Wemm 1086c2aa98e2SPeter Wemm /* value points at name */ 108740266059SGregory Neil Shapiro if ((ret = dn_expand((unsigned char *)&answer, 108840266059SGregory Neil Shapiro eom, ap, (RES_UNC_T) nbuf, 108940266059SGregory Neil Shapiro sizeof(nbuf))) < 0) 1090c2aa98e2SPeter Wemm break; 109140266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1092c2aa98e2SPeter Wemm 1093c2aa98e2SPeter Wemm /* 1094c2aa98e2SPeter Wemm ** RFC 1034 section 3.6 specifies that CNAME 1095c2aa98e2SPeter Wemm ** should point at the canonical name -- but 1096c2aa98e2SPeter Wemm ** urges software to try again anyway. 1097c2aa98e2SPeter Wemm */ 1098c2aa98e2SPeter Wemm 1099c2aa98e2SPeter Wemm goto cnameloop; 1100c2aa98e2SPeter Wemm 1101c2aa98e2SPeter Wemm default: 1102c2aa98e2SPeter Wemm /* not a record of interest */ 1103c2aa98e2SPeter Wemm continue; 1104c2aa98e2SPeter Wemm } 1105c2aa98e2SPeter Wemm } 1106c2aa98e2SPeter Wemm 1107c2aa98e2SPeter Wemm if (amatch) 1108c2aa98e2SPeter Wemm { 1109c2aa98e2SPeter Wemm /* 1110c2aa98e2SPeter Wemm ** Got a good match -- either an A, CNAME, or an 1111c2aa98e2SPeter Wemm ** exact MX record. Save it and get out of here. 1112c2aa98e2SPeter Wemm */ 1113c2aa98e2SPeter Wemm 1114c2aa98e2SPeter Wemm mxmatch = *dp; 1115c2aa98e2SPeter Wemm break; 1116c2aa98e2SPeter Wemm } 1117c2aa98e2SPeter Wemm 1118c2aa98e2SPeter Wemm /* 1119c2aa98e2SPeter Wemm ** Nothing definitive yet. 1120c2aa98e2SPeter Wemm ** If this was a T_A query and we haven't yet found a MX 1121c2aa98e2SPeter Wemm ** match, try T_MX if allowed to do so. 1122c2aa98e2SPeter Wemm ** Otherwise, try the next domain. 1123c2aa98e2SPeter Wemm */ 1124c2aa98e2SPeter Wemm 112506f25ae9SGregory Neil Shapiro # if NETINET6 112640266059SGregory Neil Shapiro if (qtype == T_AAAA) 1127c2aa98e2SPeter Wemm qtype = T_A; 112840266059SGregory Neil Shapiro else 112906f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 113040266059SGregory Neil Shapiro if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 1131c2aa98e2SPeter Wemm qtype = T_MX; 1132c2aa98e2SPeter Wemm else 1133c2aa98e2SPeter Wemm { 1134a7ec597cSGregory Neil Shapiro qtype = initial; 1135c2aa98e2SPeter Wemm dp++; 1136c2aa98e2SPeter Wemm } 1137c2aa98e2SPeter Wemm } 1138c2aa98e2SPeter Wemm 1139c2aa98e2SPeter Wemm /* if nothing was found, we are done */ 1140c2aa98e2SPeter Wemm if (mxmatch == NULL) 1141c2aa98e2SPeter Wemm { 1142193538b7SGregory Neil Shapiro if (*statp == EX_OK) 1143c2aa98e2SPeter Wemm *statp = EX_NOHOST; 114440266059SGregory Neil Shapiro return false; 1145c2aa98e2SPeter Wemm } 1146c2aa98e2SPeter Wemm 1147c2aa98e2SPeter Wemm /* 1148c2aa98e2SPeter Wemm ** Create canonical name and return. 1149c2aa98e2SPeter Wemm ** If saved domain name is null, name was already canonical. 1150c2aa98e2SPeter Wemm ** Otherwise append the saved domain name. 1151c2aa98e2SPeter Wemm */ 1152c2aa98e2SPeter Wemm 1153d0cef73dSGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host, 1154c2aa98e2SPeter Wemm *mxmatch == '\0' ? "" : ".", 1155c2aa98e2SPeter Wemm MAXDNAME, mxmatch); 115640266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1157c2aa98e2SPeter Wemm if (tTd(8, 5)) 115840266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: %s\n", host); 1159c2aa98e2SPeter Wemm *statp = EX_OK; 116040266059SGregory Neil Shapiro 116140266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 116240266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 116340266059SGregory Neil Shapiro *pttl = ttl; 116440266059SGregory Neil Shapiro return true; 1165c2aa98e2SPeter Wemm } 1166c2aa98e2SPeter Wemm #endif /* NAMED_BIND */ 1167