1c2aa98e2SPeter Wemm /* 2e92d3f3fSGregory Neil Shapiro * Copyright (c) 1998-2004 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 #if NAMED_BIND 17b6bacd31SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.195 2004/08/04 21:11:31 ca Exp $ (with name server)") 1806f25ae9SGregory Neil Shapiro #else /* NAMED_BIND */ 19b6bacd31SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.195 2004/08/04 21:11:31 ca Exp $ (without name server)") 2006f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */ 21c2aa98e2SPeter Wemm 22c2aa98e2SPeter Wemm #if NAMED_BIND 23c2aa98e2SPeter Wemm 24c2aa98e2SPeter Wemm # include <arpa/inet.h> 25c2aa98e2SPeter Wemm 2640266059SGregory Neil Shapiro 27c2aa98e2SPeter Wemm /* 28c2aa98e2SPeter Wemm ** The standard udp packet size PACKETSZ (512) is not sufficient for some 29c2aa98e2SPeter Wemm ** nameserver answers containing very many resource records. The resolver 30c2aa98e2SPeter Wemm ** may switch to tcp and retry if it detects udp packet overflow. 31c2aa98e2SPeter Wemm ** Also note that the resolver routines res_query and res_search return 32c2aa98e2SPeter Wemm ** the size of the *un*truncated answer in case the supplied answer buffer 33c2aa98e2SPeter Wemm ** it not big enough to accommodate the entire answer. 34c2aa98e2SPeter Wemm */ 35c2aa98e2SPeter Wemm 36c2aa98e2SPeter Wemm # ifndef MAXPACKET 37c2aa98e2SPeter Wemm # define MAXPACKET 8192 /* max packet size used internally by BIND */ 3806f25ae9SGregory Neil Shapiro # endif /* ! MAXPACKET */ 39c2aa98e2SPeter Wemm 40c2aa98e2SPeter Wemm typedef union 41c2aa98e2SPeter Wemm { 42c2aa98e2SPeter Wemm HEADER qb1; 4340266059SGregory Neil Shapiro unsigned char qb2[MAXPACKET]; 44c2aa98e2SPeter Wemm } querybuf; 45c2aa98e2SPeter Wemm 46c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE 47c2aa98e2SPeter Wemm # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 4806f25ae9SGregory Neil Shapiro # endif /* ! MXHOSTBUFSIZE */ 49c2aa98e2SPeter Wemm 50c2aa98e2SPeter Wemm static char MXHostBuf[MXHOSTBUFSIZE]; 5140266059SGregory Neil Shapiro #if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 5240266059SGregory Neil Shapiro ERROR: _MXHOSTBUFSIZE is out of range 5340266059SGregory Neil Shapiro #endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 54c2aa98e2SPeter Wemm 55c2aa98e2SPeter Wemm # ifndef MAXDNSRCH 56c2aa98e2SPeter Wemm # define MAXDNSRCH 6 /* number of possible domains to search */ 5706f25ae9SGregory Neil Shapiro # endif /* ! MAXDNSRCH */ 5806f25ae9SGregory Neil Shapiro 5906f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE 6006f25ae9SGregory Neil Shapiro # define RES_DNSRCH_VARIABLE _res.dnsrch 6106f25ae9SGregory Neil Shapiro # endif /* ! RES_DNSRCH_VARIABLE */ 62c2aa98e2SPeter Wemm 63c2aa98e2SPeter Wemm # ifndef NO_DATA 64c2aa98e2SPeter Wemm # define NO_DATA NO_ADDRESS 6506f25ae9SGregory Neil Shapiro # endif /* ! NO_DATA */ 66c2aa98e2SPeter Wemm 67c2aa98e2SPeter Wemm # ifndef HFIXEDSZ 68c2aa98e2SPeter Wemm # define HFIXEDSZ 12 /* sizeof(HEADER) */ 6906f25ae9SGregory Neil Shapiro # endif /* ! HFIXEDSZ */ 70c2aa98e2SPeter Wemm 71c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 72c2aa98e2SPeter Wemm 73c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415) 74c2aa98e2SPeter Wemm # define RES_UNC_T char * 7506f25ae9SGregory Neil Shapiro # else /* defined(__RES) && (__RES >= 19940415) */ 7640266059SGregory Neil Shapiro # define RES_UNC_T unsigned char * 7706f25ae9SGregory Neil Shapiro # endif /* defined(__RES) && (__RES >= 19940415) */ 7806f25ae9SGregory Neil Shapiro 7906f25ae9SGregory Neil Shapiro static int mxrand __P((char *)); 8040266059SGregory Neil Shapiro static int fallbackmxrr __P((int, unsigned short *, char **)); 8106f25ae9SGregory Neil Shapiro 8240266059SGregory Neil Shapiro /* 8340266059SGregory Neil Shapiro ** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 8440266059SGregory Neil Shapiro ** 8540266059SGregory Neil Shapiro ** We have to initialize this once before doing anything else. 8640266059SGregory Neil Shapiro ** Moreover, we have to repeat this from time to time to avoid 8740266059SGregory Neil Shapiro ** stale data, e.g., in persistent queue runners. 8840266059SGregory Neil Shapiro ** This should be done in a parent process so the child 8940266059SGregory Neil Shapiro ** processes have the right data. 9040266059SGregory Neil Shapiro ** 9140266059SGregory Neil Shapiro ** Parameters: 9240266059SGregory Neil Shapiro ** host -- the name of the fallback MX host. 9340266059SGregory Neil Shapiro ** 9440266059SGregory Neil Shapiro ** Returns: 9540266059SGregory Neil Shapiro ** number of MX records. 9640266059SGregory Neil Shapiro ** 9740266059SGregory Neil Shapiro ** Side Effects: 98e92d3f3fSGregory Neil Shapiro ** Populates NumFallbackMXHosts and fbhosts. 9940266059SGregory Neil Shapiro ** Sets renewal time (based on TTL). 10040266059SGregory Neil Shapiro */ 10140266059SGregory Neil Shapiro 102e92d3f3fSGregory Neil Shapiro int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 10340266059SGregory Neil Shapiro static char *fbhosts[MAXMXHOSTS + 1]; 10440266059SGregory Neil Shapiro 10540266059SGregory Neil Shapiro int 10640266059SGregory Neil Shapiro getfallbackmxrr(host) 10740266059SGregory Neil Shapiro char *host; 10840266059SGregory Neil Shapiro { 10940266059SGregory Neil Shapiro int i, rcode; 11040266059SGregory Neil Shapiro int ttl; 11140266059SGregory Neil Shapiro static time_t renew = 0; 11240266059SGregory Neil Shapiro 11340266059SGregory Neil Shapiro #if 0 11440266059SGregory Neil Shapiro /* This is currently done before this function is called. */ 11540266059SGregory Neil Shapiro if (host == NULL || *host == '\0') 11640266059SGregory Neil Shapiro return 0; 11740266059SGregory Neil Shapiro #endif /* 0 */ 118e92d3f3fSGregory Neil Shapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 119e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 12040266059SGregory Neil Shapiro if (host[0] == '[') 12140266059SGregory Neil Shapiro { 12240266059SGregory Neil Shapiro fbhosts[0] = host; 123e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = 1; 12440266059SGregory Neil Shapiro } 12540266059SGregory Neil Shapiro else 12640266059SGregory Neil Shapiro { 12740266059SGregory Neil Shapiro /* free old data */ 128e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 12940266059SGregory Neil Shapiro sm_free(fbhosts[i]); 13040266059SGregory Neil Shapiro 13140266059SGregory Neil Shapiro /* get new data */ 132e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false, 13340266059SGregory Neil Shapiro &rcode, false, &ttl); 13440266059SGregory Neil Shapiro renew = curtime() + ttl; 135e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 13640266059SGregory Neil Shapiro fbhosts[i] = newstr(fbhosts[i]); 13740266059SGregory Neil Shapiro } 138e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 13940266059SGregory Neil Shapiro } 14040266059SGregory Neil Shapiro 14140266059SGregory Neil Shapiro /* 14240266059SGregory Neil Shapiro ** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 14340266059SGregory Neil Shapiro ** 14440266059SGregory Neil Shapiro ** Parameters: 14540266059SGregory Neil Shapiro ** nmx -- current number of MX records. 14640266059SGregory Neil Shapiro ** prefs -- array of preferences. 14740266059SGregory Neil Shapiro ** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 14840266059SGregory Neil Shapiro ** 14940266059SGregory Neil Shapiro ** Returns: 15040266059SGregory Neil Shapiro ** new number of MX records. 15140266059SGregory Neil Shapiro ** 15240266059SGregory Neil Shapiro ** Side Effects: 153e92d3f3fSGregory Neil Shapiro ** If FallbackMX was set, it appends the MX records for 15440266059SGregory Neil Shapiro ** that host to mxhosts (and modifies prefs accordingly). 15540266059SGregory Neil Shapiro */ 15640266059SGregory Neil Shapiro 15740266059SGregory Neil Shapiro static int 15840266059SGregory Neil Shapiro fallbackmxrr(nmx, prefs, mxhosts) 15940266059SGregory Neil Shapiro int nmx; 16040266059SGregory Neil Shapiro unsigned short *prefs; 16140266059SGregory Neil Shapiro char **mxhosts; 16240266059SGregory Neil Shapiro { 16340266059SGregory Neil Shapiro int i; 16440266059SGregory Neil Shapiro 165e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 16640266059SGregory Neil Shapiro { 16740266059SGregory Neil Shapiro if (nmx > 0) 16840266059SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1; 16940266059SGregory Neil Shapiro else 17040266059SGregory Neil Shapiro prefs[nmx] = 0; 17140266059SGregory Neil Shapiro mxhosts[nmx++] = fbhosts[i]; 17240266059SGregory Neil Shapiro } 17340266059SGregory Neil Shapiro return nmx; 17440266059SGregory Neil Shapiro } 17540266059SGregory Neil Shapiro 17640266059SGregory Neil Shapiro /* 177c2aa98e2SPeter Wemm ** GETMXRR -- get MX resource records for a domain 178c2aa98e2SPeter Wemm ** 179c2aa98e2SPeter Wemm ** Parameters: 180c2aa98e2SPeter Wemm ** host -- the name of the host to MX. 181c2aa98e2SPeter Wemm ** mxhosts -- a pointer to a return buffer of MX records. 18206f25ae9SGregory Neil Shapiro ** mxprefs -- a pointer to a return buffer of MX preferences. 18306f25ae9SGregory Neil Shapiro ** If NULL, don't try to populate. 18440266059SGregory Neil Shapiro ** droplocalhost -- If true, all MX records less preferred 185c2aa98e2SPeter Wemm ** than the local host (as determined by $=w) will 186c2aa98e2SPeter Wemm ** be discarded. 187c2aa98e2SPeter Wemm ** rcode -- a pointer to an EX_ status code. 18840266059SGregory Neil Shapiro ** tryfallback -- add also fallback MX host? 18940266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 190c2aa98e2SPeter Wemm ** 191c2aa98e2SPeter Wemm ** Returns: 192c2aa98e2SPeter Wemm ** The number of MX records found. 193c2aa98e2SPeter Wemm ** -1 if there is an internal failure. 194c2aa98e2SPeter Wemm ** If no MX records are found, mxhosts[0] is set to host 195c2aa98e2SPeter Wemm ** and 1 is returned. 19640266059SGregory Neil Shapiro ** 19740266059SGregory Neil Shapiro ** Side Effects: 19840266059SGregory Neil Shapiro ** The entries made for mxhosts point to a static array 19940266059SGregory Neil Shapiro ** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 20040266059SGregory Neil Shapiro ** if it must be preserved across calls to this function. 201c2aa98e2SPeter Wemm */ 202c2aa98e2SPeter Wemm 203c2aa98e2SPeter Wemm int 20440266059SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) 205c2aa98e2SPeter Wemm char *host; 206c2aa98e2SPeter Wemm char **mxhosts; 20740266059SGregory Neil Shapiro unsigned short *mxprefs; 208c2aa98e2SPeter Wemm bool droplocalhost; 209c2aa98e2SPeter Wemm int *rcode; 21040266059SGregory Neil Shapiro bool tryfallback; 21140266059SGregory Neil Shapiro int *pttl; 212c2aa98e2SPeter Wemm { 21340266059SGregory Neil Shapiro register unsigned char *eom, *cp; 214c2aa98e2SPeter Wemm register int i, j, n; 215c2aa98e2SPeter Wemm int nmx = 0; 216c2aa98e2SPeter Wemm register char *bp; 217c2aa98e2SPeter Wemm HEADER *hp; 218c2aa98e2SPeter Wemm querybuf answer; 219c2aa98e2SPeter Wemm int ancount, qdcount, buflen; 22040266059SGregory Neil Shapiro bool seenlocal = false; 22140266059SGregory Neil Shapiro unsigned short pref, type; 22240266059SGregory Neil Shapiro unsigned short localpref = 256; 223e92d3f3fSGregory Neil Shapiro char *fallbackMX = FallbackMX; 22440266059SGregory Neil Shapiro bool trycanon = false; 22540266059SGregory Neil Shapiro unsigned short *prefs; 226b6bacd31SGregory Neil Shapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 22740266059SGregory Neil Shapiro unsigned short prefer[MAXMXHOSTS]; 228c2aa98e2SPeter Wemm int weight[MAXMXHOSTS]; 22940266059SGregory Neil Shapiro int ttl = 0; 23006f25ae9SGregory Neil Shapiro extern int res_query(), res_search(); 231c2aa98e2SPeter Wemm 232c2aa98e2SPeter Wemm if (tTd(8, 2)) 23340266059SGregory Neil Shapiro sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", 23406f25ae9SGregory Neil Shapiro host, droplocalhost); 235a7ec597cSGregory Neil Shapiro if (*host == '\0') 236a7ec597cSGregory Neil Shapiro return 0; 237c2aa98e2SPeter Wemm 23840266059SGregory Neil Shapiro if ((fallbackMX != NULL && droplocalhost && 23940266059SGregory Neil Shapiro wordinclass(fallbackMX, 'w')) || !tryfallback) 240c2aa98e2SPeter Wemm { 241c2aa98e2SPeter Wemm /* don't use fallback for this pass */ 242c2aa98e2SPeter Wemm fallbackMX = NULL; 243c2aa98e2SPeter Wemm } 244c2aa98e2SPeter Wemm 245c2aa98e2SPeter Wemm *rcode = EX_OK; 246c2aa98e2SPeter Wemm 24706f25ae9SGregory Neil Shapiro if (mxprefs != NULL) 24806f25ae9SGregory Neil Shapiro prefs = mxprefs; 24906f25ae9SGregory Neil Shapiro else 25006f25ae9SGregory Neil Shapiro prefs = prefer; 25106f25ae9SGregory Neil Shapiro 252c2aa98e2SPeter Wemm /* efficiency hack -- numeric or non-MX lookups */ 253c2aa98e2SPeter Wemm if (host[0] == '[') 254c2aa98e2SPeter Wemm goto punt; 255c2aa98e2SPeter Wemm 256c2aa98e2SPeter Wemm /* 257c2aa98e2SPeter Wemm ** If we don't have MX records in our host switch, don't 258c2aa98e2SPeter Wemm ** try for MX records. Note that this really isn't "right", 259c2aa98e2SPeter Wemm ** since we might be set up to try NIS first and then DNS; 260c2aa98e2SPeter Wemm ** if the host is found in NIS we really shouldn't be doing 261c2aa98e2SPeter Wemm ** MX lookups. However, that should be a degenerate case. 262c2aa98e2SPeter Wemm */ 263c2aa98e2SPeter Wemm 264c2aa98e2SPeter Wemm if (!UseNameServer) 265c2aa98e2SPeter Wemm goto punt; 266c2aa98e2SPeter Wemm if (HasWildcardMX && ConfigLevel >= 6) 267c2aa98e2SPeter Wemm resfunc = res_query; 268c2aa98e2SPeter Wemm else 269c2aa98e2SPeter Wemm resfunc = res_search; 270c2aa98e2SPeter Wemm 271c2aa98e2SPeter Wemm errno = 0; 27240266059SGregory Neil Shapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 27340266059SGregory Neil Shapiro sizeof(answer)); 274c2aa98e2SPeter Wemm if (n < 0) 275c2aa98e2SPeter Wemm { 276c2aa98e2SPeter Wemm if (tTd(8, 1)) 27740266059SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 27840266059SGregory Neil Shapiro host == NULL ? "<NULL>" : host, errno, h_errno); 279c2aa98e2SPeter Wemm switch (h_errno) 280c2aa98e2SPeter Wemm { 281c2aa98e2SPeter Wemm case NO_DATA: 28240266059SGregory Neil Shapiro trycanon = true; 28306f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 284c2aa98e2SPeter Wemm 285c2aa98e2SPeter Wemm case NO_RECOVERY: 286c2aa98e2SPeter Wemm /* no MX data on this host */ 287c2aa98e2SPeter Wemm goto punt; 288c2aa98e2SPeter Wemm 289c2aa98e2SPeter Wemm case HOST_NOT_FOUND: 290c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH 291c2aa98e2SPeter Wemm case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 29206f25ae9SGregory Neil Shapiro # endif /* BROKEN_RES_SEARCH */ 293c2aa98e2SPeter Wemm /* host doesn't exist in DNS; might be in /etc/hosts */ 29440266059SGregory Neil Shapiro trycanon = true; 295c2aa98e2SPeter Wemm *rcode = EX_NOHOST; 296c2aa98e2SPeter Wemm goto punt; 297c2aa98e2SPeter Wemm 298c2aa98e2SPeter Wemm case TRY_AGAIN: 299c2aa98e2SPeter Wemm case -1: 300c2aa98e2SPeter Wemm /* couldn't connect to the name server */ 301c2aa98e2SPeter Wemm if (fallbackMX != NULL) 302c2aa98e2SPeter Wemm { 303c2aa98e2SPeter Wemm /* name server is hosed -- push to fallback */ 30440266059SGregory Neil Shapiro return fallbackmxrr(nmx, prefs, mxhosts); 305c2aa98e2SPeter Wemm } 306c2aa98e2SPeter Wemm /* it might come up later; better queue it up */ 307c2aa98e2SPeter Wemm *rcode = EX_TEMPFAIL; 308c2aa98e2SPeter Wemm break; 309c2aa98e2SPeter Wemm 310c2aa98e2SPeter Wemm default: 31140266059SGregory Neil Shapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 312c2aa98e2SPeter Wemm host, h_errno); 313c2aa98e2SPeter Wemm *rcode = EX_OSERR; 314c2aa98e2SPeter Wemm break; 315c2aa98e2SPeter Wemm } 316c2aa98e2SPeter Wemm 317c2aa98e2SPeter Wemm /* irreconcilable differences */ 31806f25ae9SGregory Neil Shapiro return -1; 319c2aa98e2SPeter Wemm } 320c2aa98e2SPeter Wemm 321c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 322c2aa98e2SPeter Wemm if (n > sizeof(answer)) 323c2aa98e2SPeter Wemm n = sizeof(answer); 324c2aa98e2SPeter Wemm 325c2aa98e2SPeter Wemm /* find first satisfactory answer */ 326c2aa98e2SPeter Wemm hp = (HEADER *)&answer; 32740266059SGregory Neil Shapiro cp = (unsigned char *)&answer + HFIXEDSZ; 32840266059SGregory Neil Shapiro eom = (unsigned char *)&answer + n; 32940266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 33006f25ae9SGregory Neil Shapiro qdcount--; 33106f25ae9SGregory Neil Shapiro cp += n + QFIXEDSZ) 33206f25ae9SGregory Neil Shapiro { 333c2aa98e2SPeter Wemm if ((n = dn_skipname(cp, eom)) < 0) 334c2aa98e2SPeter Wemm goto punt; 33506f25ae9SGregory Neil Shapiro } 33640266059SGregory Neil Shapiro 33740266059SGregory Neil Shapiro /* NOTE: see definition of MXHostBuf! */ 338c2aa98e2SPeter Wemm buflen = sizeof(MXHostBuf) - 1; 33940266059SGregory Neil Shapiro SM_ASSERT(buflen > 0); 340c2aa98e2SPeter Wemm bp = MXHostBuf; 34140266059SGregory Neil Shapiro ancount = ntohs((unsigned short) hp->ancount); 34240266059SGregory Neil Shapiro 34340266059SGregory Neil Shapiro /* See RFC 1035 for layout of RRs. */ 344e92d3f3fSGregory Neil Shapiro /* XXX leave room for FallbackMX ? */ 345c2aa98e2SPeter Wemm while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 346c2aa98e2SPeter Wemm { 34740266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 34840266059SGregory Neil Shapiro (RES_UNC_T) bp, buflen)) < 0) 349c2aa98e2SPeter Wemm break; 350c2aa98e2SPeter Wemm cp += n; 351c2aa98e2SPeter Wemm GETSHORT(type, cp); 35240266059SGregory Neil Shapiro cp += INT16SZ; /* skip over class */ 35340266059SGregory Neil Shapiro GETLONG(ttl, cp); 35440266059SGregory Neil Shapiro GETSHORT(n, cp); /* rdlength */ 355c2aa98e2SPeter Wemm if (type != T_MX) 356c2aa98e2SPeter Wemm { 357c2aa98e2SPeter Wemm if (tTd(8, 8) || _res.options & RES_DEBUG) 35840266059SGregory Neil Shapiro sm_dprintf("unexpected answer type %d, size %d\n", 359c2aa98e2SPeter Wemm type, n); 360c2aa98e2SPeter Wemm cp += n; 361c2aa98e2SPeter Wemm continue; 362c2aa98e2SPeter Wemm } 363c2aa98e2SPeter Wemm GETSHORT(pref, cp); 36440266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 365c2aa98e2SPeter Wemm (RES_UNC_T) bp, buflen)) < 0) 366c2aa98e2SPeter Wemm break; 367c2aa98e2SPeter Wemm cp += n; 36840266059SGregory Neil Shapiro n = strlen(bp); 36940266059SGregory Neil Shapiro # if 0 37040266059SGregory Neil Shapiro /* Can this happen? */ 37140266059SGregory Neil Shapiro if (n == 0) 37240266059SGregory Neil Shapiro { 37340266059SGregory Neil Shapiro if (LogLevel > 4) 37440266059SGregory Neil Shapiro sm_syslog(LOG_ERR, NOQID, 37540266059SGregory Neil Shapiro "MX records for %s contain empty string", 37640266059SGregory Neil Shapiro host); 37740266059SGregory Neil Shapiro continue; 37840266059SGregory Neil Shapiro } 37940266059SGregory Neil Shapiro # endif /* 0 */ 380c2aa98e2SPeter Wemm if (wordinclass(bp, 'w')) 381c2aa98e2SPeter Wemm { 382c2aa98e2SPeter Wemm if (tTd(8, 3)) 38340266059SGregory Neil Shapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 384c2aa98e2SPeter Wemm bp, pref); 385c2aa98e2SPeter Wemm if (droplocalhost) 386c2aa98e2SPeter Wemm { 387c2aa98e2SPeter Wemm if (!seenlocal || pref < localpref) 388c2aa98e2SPeter Wemm localpref = pref; 38940266059SGregory Neil Shapiro seenlocal = true; 390c2aa98e2SPeter Wemm continue; 391c2aa98e2SPeter Wemm } 392c2aa98e2SPeter Wemm weight[nmx] = 0; 393c2aa98e2SPeter Wemm } 394c2aa98e2SPeter Wemm else 395c2aa98e2SPeter Wemm weight[nmx] = mxrand(bp); 39606f25ae9SGregory Neil Shapiro prefs[nmx] = pref; 397c2aa98e2SPeter Wemm mxhosts[nmx++] = bp; 398c2aa98e2SPeter Wemm bp += n; 399c2aa98e2SPeter Wemm if (bp[-1] != '.') 400c2aa98e2SPeter Wemm { 401c2aa98e2SPeter Wemm *bp++ = '.'; 402c2aa98e2SPeter Wemm n++; 403c2aa98e2SPeter Wemm } 404c2aa98e2SPeter Wemm *bp++ = '\0'; 40540266059SGregory Neil Shapiro if (buflen < n + 1) 40640266059SGregory Neil Shapiro { 40740266059SGregory Neil Shapiro /* don't want to wrap buflen */ 40840266059SGregory Neil Shapiro break; 40940266059SGregory Neil Shapiro } 410c2aa98e2SPeter Wemm buflen -= n + 1; 411c2aa98e2SPeter Wemm } 412c2aa98e2SPeter Wemm 41340266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 41440266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 41540266059SGregory Neil Shapiro *pttl = ttl; 41640266059SGregory Neil Shapiro 417c2aa98e2SPeter Wemm /* sort the records */ 418c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 419c2aa98e2SPeter Wemm { 420c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 421c2aa98e2SPeter Wemm { 42206f25ae9SGregory Neil Shapiro if (prefs[i] > prefs[j] || 42306f25ae9SGregory Neil Shapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 424c2aa98e2SPeter Wemm { 425c2aa98e2SPeter Wemm register int temp; 426c2aa98e2SPeter Wemm register char *temp1; 427c2aa98e2SPeter Wemm 42806f25ae9SGregory Neil Shapiro temp = prefs[i]; 42906f25ae9SGregory Neil Shapiro prefs[i] = prefs[j]; 43006f25ae9SGregory Neil Shapiro prefs[j] = temp; 431c2aa98e2SPeter Wemm temp1 = mxhosts[i]; 432c2aa98e2SPeter Wemm mxhosts[i] = mxhosts[j]; 433c2aa98e2SPeter Wemm mxhosts[j] = temp1; 434c2aa98e2SPeter Wemm temp = weight[i]; 435c2aa98e2SPeter Wemm weight[i] = weight[j]; 436c2aa98e2SPeter Wemm weight[j] = temp; 437c2aa98e2SPeter Wemm } 438c2aa98e2SPeter Wemm } 43906f25ae9SGregory Neil Shapiro if (seenlocal && prefs[i] >= localpref) 440c2aa98e2SPeter Wemm { 441c2aa98e2SPeter Wemm /* truncate higher preference part of list */ 442c2aa98e2SPeter Wemm nmx = i; 443c2aa98e2SPeter Wemm } 444c2aa98e2SPeter Wemm } 445c2aa98e2SPeter Wemm 446c2aa98e2SPeter Wemm /* delete duplicates from list (yes, some bozos have duplicates) */ 447c2aa98e2SPeter Wemm for (i = 0; i < nmx - 1; ) 448c2aa98e2SPeter Wemm { 44940266059SGregory Neil Shapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 450c2aa98e2SPeter Wemm i++; 451c2aa98e2SPeter Wemm else 452c2aa98e2SPeter Wemm { 453c2aa98e2SPeter Wemm /* compress out duplicate */ 454c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 45506f25ae9SGregory Neil Shapiro { 456c2aa98e2SPeter Wemm mxhosts[j] = mxhosts[j + 1]; 45706f25ae9SGregory Neil Shapiro prefs[j] = prefs[j + 1]; 45806f25ae9SGregory Neil Shapiro } 459c2aa98e2SPeter Wemm nmx--; 460c2aa98e2SPeter Wemm } 461c2aa98e2SPeter Wemm } 462c2aa98e2SPeter Wemm 463c2aa98e2SPeter Wemm if (nmx == 0) 464c2aa98e2SPeter Wemm { 465c2aa98e2SPeter Wemm punt: 46606f25ae9SGregory Neil Shapiro if (seenlocal) 467c2aa98e2SPeter Wemm { 46806f25ae9SGregory Neil Shapiro struct hostent *h = NULL; 46906f25ae9SGregory Neil Shapiro 470c2aa98e2SPeter Wemm /* 471c2aa98e2SPeter Wemm ** If we have deleted all MX entries, this is 472c2aa98e2SPeter Wemm ** an error -- we should NEVER send to a host that 473c2aa98e2SPeter Wemm ** has an MX, and this should have been caught 474c2aa98e2SPeter Wemm ** earlier in the config file. 475c2aa98e2SPeter Wemm ** 476c2aa98e2SPeter Wemm ** Some sites prefer to go ahead and try the 477c2aa98e2SPeter Wemm ** A record anyway; that case is handled by 478c2aa98e2SPeter Wemm ** setting TryNullMXList. I believe this is a 479c2aa98e2SPeter Wemm ** bad idea, but it's up to you.... 480c2aa98e2SPeter Wemm */ 481c2aa98e2SPeter Wemm 48206f25ae9SGregory Neil Shapiro if (TryNullMXList) 48306f25ae9SGregory Neil Shapiro { 484602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 48506f25ae9SGregory Neil Shapiro errno = 0; 48606f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET); 48706f25ae9SGregory Neil Shapiro if (h == NULL) 48806f25ae9SGregory Neil Shapiro { 48906f25ae9SGregory Neil Shapiro if (errno == ETIMEDOUT || 49006f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 49106f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 49206f25ae9SGregory Neil Shapiro UseNameServer)) 49306f25ae9SGregory Neil Shapiro { 49406f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 49506f25ae9SGregory Neil Shapiro return -1; 49606f25ae9SGregory Neil Shapiro } 49706f25ae9SGregory Neil Shapiro # if NETINET6 498602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 49906f25ae9SGregory Neil Shapiro errno = 0; 50006f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET6); 50106f25ae9SGregory Neil Shapiro if (h == NULL && 50206f25ae9SGregory Neil Shapiro (errno == ETIMEDOUT || 50306f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 50406f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 50506f25ae9SGregory Neil Shapiro UseNameServer))) 50606f25ae9SGregory Neil Shapiro { 50706f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 50806f25ae9SGregory Neil Shapiro return -1; 50906f25ae9SGregory Neil Shapiro } 51006f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 51106f25ae9SGregory Neil Shapiro } 51206f25ae9SGregory Neil Shapiro } 51306f25ae9SGregory Neil Shapiro 51406f25ae9SGregory Neil Shapiro if (h == NULL) 51506f25ae9SGregory Neil Shapiro { 516c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 517c2aa98e2SPeter Wemm syserr("MX list for %s points back to %s", 518c2aa98e2SPeter Wemm host, MyHostName); 519c2aa98e2SPeter Wemm return -1; 520c2aa98e2SPeter Wemm } 52140266059SGregory Neil Shapiro # if NETINET6 522193538b7SGregory Neil Shapiro freehostent(h); 523193538b7SGregory Neil Shapiro hp = NULL; 52440266059SGregory Neil Shapiro # endif /* NETINET6 */ 52506f25ae9SGregory Neil Shapiro } 52640266059SGregory Neil Shapiro if (strlen(host) >= sizeof MXHostBuf) 527c2aa98e2SPeter Wemm { 528c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 529c2aa98e2SPeter Wemm syserr("Host name %s too long", 530c2aa98e2SPeter Wemm shortenstring(host, MAXSHORTSTR)); 531c2aa98e2SPeter Wemm return -1; 532c2aa98e2SPeter Wemm } 53340266059SGregory Neil Shapiro (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf); 534c2aa98e2SPeter Wemm mxhosts[0] = MXHostBuf; 53506f25ae9SGregory Neil Shapiro prefs[0] = 0; 536c2aa98e2SPeter Wemm if (host[0] == '[') 537c2aa98e2SPeter Wemm { 538c2aa98e2SPeter Wemm register char *p; 53906f25ae9SGregory Neil Shapiro # if NETINET6 54006f25ae9SGregory Neil Shapiro struct sockaddr_in6 tmp6; 54106f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 542c2aa98e2SPeter Wemm 543c2aa98e2SPeter Wemm /* this may be an MX suppression-style address */ 544c2aa98e2SPeter Wemm p = strchr(MXHostBuf, ']'); 545c2aa98e2SPeter Wemm if (p != NULL) 546c2aa98e2SPeter Wemm { 547c2aa98e2SPeter Wemm *p = '\0'; 54806f25ae9SGregory Neil Shapiro 549c2aa98e2SPeter Wemm if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 550c2aa98e2SPeter Wemm { 551c2aa98e2SPeter Wemm nmx++; 552c2aa98e2SPeter Wemm *p = ']'; 553c2aa98e2SPeter Wemm } 55406f25ae9SGregory Neil Shapiro # if NETINET6 55540266059SGregory Neil Shapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 55606f25ae9SGregory Neil Shapiro &tmp6.sin6_addr) == 1) 55706f25ae9SGregory Neil Shapiro { 55806f25ae9SGregory Neil Shapiro nmx++; 55906f25ae9SGregory Neil Shapiro *p = ']'; 56006f25ae9SGregory Neil Shapiro } 56106f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 562c2aa98e2SPeter Wemm else 563c2aa98e2SPeter Wemm { 56440266059SGregory Neil Shapiro trycanon = true; 565c2aa98e2SPeter Wemm mxhosts[0]++; 566c2aa98e2SPeter Wemm } 567c2aa98e2SPeter Wemm } 568c2aa98e2SPeter Wemm } 569c2aa98e2SPeter Wemm if (trycanon && 57040266059SGregory Neil Shapiro getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl)) 571c2aa98e2SPeter Wemm { 57240266059SGregory Neil Shapiro /* XXX MXHostBuf == "" ? is that possible? */ 573c2aa98e2SPeter Wemm bp = &MXHostBuf[strlen(MXHostBuf)]; 574c2aa98e2SPeter Wemm if (bp[-1] != '.') 575c2aa98e2SPeter Wemm { 576c2aa98e2SPeter Wemm *bp++ = '.'; 577c2aa98e2SPeter Wemm *bp = '\0'; 578c2aa98e2SPeter Wemm } 579c2aa98e2SPeter Wemm nmx = 1; 580c2aa98e2SPeter Wemm } 581c2aa98e2SPeter Wemm } 582c2aa98e2SPeter Wemm 583c2aa98e2SPeter Wemm /* if we have a default lowest preference, include that */ 584c2aa98e2SPeter Wemm if (fallbackMX != NULL && !seenlocal) 58506f25ae9SGregory Neil Shapiro { 58640266059SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 58706f25ae9SGregory Neil Shapiro } 58806f25ae9SGregory Neil Shapiro return nmx; 589c2aa98e2SPeter Wemm } 59040266059SGregory Neil Shapiro /* 591c2aa98e2SPeter Wemm ** MXRAND -- create a randomizer for equal MX preferences 592c2aa98e2SPeter Wemm ** 593c2aa98e2SPeter Wemm ** If two MX hosts have equal preferences we want to randomize 594c2aa98e2SPeter Wemm ** the selection. But in order for signatures to be the same, 595c2aa98e2SPeter Wemm ** we need to randomize the same way each time. This function 596c2aa98e2SPeter Wemm ** computes a pseudo-random hash function from the host name. 597c2aa98e2SPeter Wemm ** 598c2aa98e2SPeter Wemm ** Parameters: 599c2aa98e2SPeter Wemm ** host -- the name of the host. 600c2aa98e2SPeter Wemm ** 601c2aa98e2SPeter Wemm ** Returns: 602c2aa98e2SPeter Wemm ** A random but repeatable value based on the host name. 603c2aa98e2SPeter Wemm */ 604c2aa98e2SPeter Wemm 60506f25ae9SGregory Neil Shapiro static int 606c2aa98e2SPeter Wemm mxrand(host) 607c2aa98e2SPeter Wemm register char *host; 608c2aa98e2SPeter Wemm { 609c2aa98e2SPeter Wemm int hfunc; 610c2aa98e2SPeter Wemm static unsigned int seed; 611c2aa98e2SPeter Wemm 612c2aa98e2SPeter Wemm if (seed == 0) 613c2aa98e2SPeter Wemm { 614c2aa98e2SPeter Wemm seed = (int) curtime() & 0xffff; 615c2aa98e2SPeter Wemm if (seed == 0) 616c2aa98e2SPeter Wemm seed++; 617c2aa98e2SPeter Wemm } 618c2aa98e2SPeter Wemm 619c2aa98e2SPeter Wemm if (tTd(17, 9)) 62040266059SGregory Neil Shapiro sm_dprintf("mxrand(%s)", host); 621c2aa98e2SPeter Wemm 622c2aa98e2SPeter Wemm hfunc = seed; 623c2aa98e2SPeter Wemm while (*host != '\0') 624c2aa98e2SPeter Wemm { 625c2aa98e2SPeter Wemm int c = *host++; 626c2aa98e2SPeter Wemm 627c2aa98e2SPeter Wemm if (isascii(c) && isupper(c)) 628c2aa98e2SPeter Wemm c = tolower(c); 629c2aa98e2SPeter Wemm hfunc = ((hfunc << 1) ^ c) % 2003; 630c2aa98e2SPeter Wemm } 631c2aa98e2SPeter Wemm 632c2aa98e2SPeter Wemm hfunc &= 0xff; 633c2aa98e2SPeter Wemm hfunc++; 634c2aa98e2SPeter Wemm 635c2aa98e2SPeter Wemm if (tTd(17, 9)) 63640266059SGregory Neil Shapiro sm_dprintf(" = %d\n", hfunc); 637c2aa98e2SPeter Wemm return hfunc; 638c2aa98e2SPeter Wemm } 63940266059SGregory Neil Shapiro /* 640c2aa98e2SPeter Wemm ** BESTMX -- find the best MX for a name 641c2aa98e2SPeter Wemm ** 642c2aa98e2SPeter Wemm ** This is really a hack, but I don't see any obvious way 643c2aa98e2SPeter Wemm ** to generalize it at the moment. 644c2aa98e2SPeter Wemm */ 645c2aa98e2SPeter Wemm 646c2aa98e2SPeter Wemm /* ARGSUSED3 */ 647c2aa98e2SPeter Wemm char * 648c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp) 649c2aa98e2SPeter Wemm MAP *map; 650c2aa98e2SPeter Wemm char *name; 651c2aa98e2SPeter Wemm char **av; 652c2aa98e2SPeter Wemm int *statp; 653c2aa98e2SPeter Wemm { 654c2aa98e2SPeter Wemm int nmx; 655c2aa98e2SPeter Wemm int saveopts = _res.options; 65640266059SGregory Neil Shapiro int i; 65740266059SGregory Neil Shapiro ssize_t len = 0; 65840266059SGregory Neil Shapiro char *result; 659c2aa98e2SPeter Wemm char *mxhosts[MAXMXHOSTS + 1]; 66040266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 66140266059SGregory Neil Shapiro char *buf; 66240266059SGregory Neil Shapiro #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 66340266059SGregory Neil Shapiro char *p; 664065a643dSPeter Wemm char buf[PSBUFSIZE / 2]; 66540266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 666c2aa98e2SPeter Wemm 667c2aa98e2SPeter Wemm _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 668959366dcSGregory Neil Shapiro nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); 669c2aa98e2SPeter Wemm _res.options = saveopts; 670c2aa98e2SPeter Wemm if (nmx <= 0) 671c2aa98e2SPeter Wemm return NULL; 672c2aa98e2SPeter Wemm if (bitset(MF_MATCHONLY, map->map_mflags)) 673c2aa98e2SPeter Wemm return map_rewrite(map, name, strlen(name), NULL); 674c2aa98e2SPeter Wemm if ((map->map_coldelim == '\0') || (nmx == 1)) 675c2aa98e2SPeter Wemm return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 676c2aa98e2SPeter Wemm 677c2aa98e2SPeter Wemm /* 678c2aa98e2SPeter Wemm ** We were given a -z flag (return all MXs) and there are multiple 679c2aa98e2SPeter Wemm ** ones. We need to build them all into a list. 680c2aa98e2SPeter Wemm */ 68140266059SGregory Neil Shapiro 68240266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 68340266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 68440266059SGregory Neil Shapiro { 68540266059SGregory Neil Shapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 68640266059SGregory Neil Shapiro { 68740266059SGregory Neil Shapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 68840266059SGregory Neil Shapiro mxhosts[i], map->map_coldelim); 68940266059SGregory Neil Shapiro return NULL; 69040266059SGregory Neil Shapiro } 69140266059SGregory Neil Shapiro len += strlen(mxhosts[i]) + 1; 69240266059SGregory Neil Shapiro if (len < 0) 69340266059SGregory Neil Shapiro { 69440266059SGregory Neil Shapiro len -= strlen(mxhosts[i]) + 1; 69540266059SGregory Neil Shapiro break; 69640266059SGregory Neil Shapiro } 69740266059SGregory Neil Shapiro } 69840266059SGregory Neil Shapiro buf = (char *) sm_malloc(len); 69940266059SGregory Neil Shapiro if (buf == NULL) 70040266059SGregory Neil Shapiro { 70140266059SGregory Neil Shapiro *statp = EX_UNAVAILABLE; 70240266059SGregory Neil Shapiro return NULL; 70340266059SGregory Neil Shapiro } 70440266059SGregory Neil Shapiro *buf = '\0'; 70540266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 70640266059SGregory Neil Shapiro { 70740266059SGregory Neil Shapiro int end; 70840266059SGregory Neil Shapiro 70940266059SGregory Neil Shapiro end = sm_strlcat(buf, mxhosts[i], len); 71040266059SGregory Neil Shapiro if (i != nmx && end + 1 < len) 71140266059SGregory Neil Shapiro { 71240266059SGregory Neil Shapiro buf[end] = map->map_coldelim; 71340266059SGregory Neil Shapiro buf[end + 1] = '\0'; 71440266059SGregory Neil Shapiro } 71540266059SGregory Neil Shapiro } 71640266059SGregory Neil Shapiro 71740266059SGregory Neil Shapiro /* Cleanly truncate for rulesets */ 71840266059SGregory Neil Shapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 71940266059SGregory Neil Shapiro #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 720c2aa98e2SPeter Wemm p = buf; 721c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 722c2aa98e2SPeter Wemm { 72340266059SGregory Neil Shapiro size_t slen; 724c2aa98e2SPeter Wemm 725c2aa98e2SPeter Wemm if (strchr(mxhosts[i], map->map_coldelim) != NULL) 726c2aa98e2SPeter Wemm { 727c2aa98e2SPeter Wemm syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 728c2aa98e2SPeter Wemm mxhosts[i], map->map_coldelim); 729c2aa98e2SPeter Wemm return NULL; 730c2aa98e2SPeter Wemm } 731c2aa98e2SPeter Wemm slen = strlen(mxhosts[i]); 732c2aa98e2SPeter Wemm if (len + slen + 2 > sizeof buf) 733c2aa98e2SPeter Wemm break; 734c2aa98e2SPeter Wemm if (i > 0) 735c2aa98e2SPeter Wemm { 736c2aa98e2SPeter Wemm *p++ = map->map_coldelim; 737c2aa98e2SPeter Wemm len++; 738c2aa98e2SPeter Wemm } 73940266059SGregory Neil Shapiro (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len); 740c2aa98e2SPeter Wemm p += slen; 741c2aa98e2SPeter Wemm len += slen; 742c2aa98e2SPeter Wemm } 74340266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 74440266059SGregory Neil Shapiro 74540266059SGregory Neil Shapiro result = map_rewrite(map, buf, len, av); 74640266059SGregory Neil Shapiro #if _FFR_BESTMX_BETTER_TRUNCATION 74740266059SGregory Neil Shapiro sm_free(buf); 74840266059SGregory Neil Shapiro #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 74940266059SGregory Neil Shapiro return result; 750c2aa98e2SPeter Wemm } 75140266059SGregory Neil Shapiro /* 752c2aa98e2SPeter Wemm ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 753c2aa98e2SPeter Wemm ** 754c2aa98e2SPeter Wemm ** This algorithm tries to be smart about wildcard MX records. 755c2aa98e2SPeter Wemm ** This is hard to do because DNS doesn't tell is if we matched 756c2aa98e2SPeter Wemm ** against a wildcard or a specific MX. 757c2aa98e2SPeter Wemm ** 758c2aa98e2SPeter Wemm ** We always prefer A & CNAME records, since these are presumed 759c2aa98e2SPeter Wemm ** to be specific. 760c2aa98e2SPeter Wemm ** 761c2aa98e2SPeter Wemm ** If we match an MX in one pass and lose it in the next, we use 762c2aa98e2SPeter Wemm ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 763c2aa98e2SPeter Wemm ** A hostname bletch.foo.bar.com will match against this MX, but 764c2aa98e2SPeter Wemm ** will stop matching when we try bletch.bar.com -- so we know 765c2aa98e2SPeter Wemm ** that bletch.foo.bar.com must have been right. This fails if 766c2aa98e2SPeter Wemm ** there was also an MX record matching *.BAR.COM, but there are 767c2aa98e2SPeter Wemm ** some things that just can't be fixed. 768c2aa98e2SPeter Wemm ** 769c2aa98e2SPeter Wemm ** Parameters: 770c2aa98e2SPeter Wemm ** host -- a buffer containing the name of the host. 771c2aa98e2SPeter Wemm ** This is a value-result parameter. 772c2aa98e2SPeter Wemm ** hbsize -- the size of the host buffer. 773c2aa98e2SPeter Wemm ** trymx -- if set, try MX records as well as A and CNAME. 774c2aa98e2SPeter Wemm ** statp -- pointer to place to store status. 77540266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 776c2aa98e2SPeter Wemm ** 777c2aa98e2SPeter Wemm ** Returns: 77840266059SGregory Neil Shapiro ** true -- if the host matched. 77940266059SGregory Neil Shapiro ** false -- otherwise. 780c2aa98e2SPeter Wemm */ 781c2aa98e2SPeter Wemm 782c2aa98e2SPeter Wemm bool 78340266059SGregory Neil Shapiro dns_getcanonname(host, hbsize, trymx, statp, pttl) 784c2aa98e2SPeter Wemm char *host; 785c2aa98e2SPeter Wemm int hbsize; 786c2aa98e2SPeter Wemm bool trymx; 787c2aa98e2SPeter Wemm int *statp; 78840266059SGregory Neil Shapiro int *pttl; 789c2aa98e2SPeter Wemm { 79040266059SGregory Neil Shapiro register unsigned char *eom, *ap; 791c2aa98e2SPeter Wemm register char *cp; 792c2aa98e2SPeter Wemm register int n; 793c2aa98e2SPeter Wemm HEADER *hp; 794c2aa98e2SPeter Wemm querybuf answer; 795c2aa98e2SPeter Wemm int ancount, qdcount; 796c2aa98e2SPeter Wemm int ret; 797c2aa98e2SPeter Wemm char **domain; 798c2aa98e2SPeter Wemm int type; 79940266059SGregory Neil Shapiro int ttl = 0; 800c2aa98e2SPeter Wemm char **dp; 801c2aa98e2SPeter Wemm char *mxmatch; 802c2aa98e2SPeter Wemm bool amatch; 80340266059SGregory Neil Shapiro bool gotmx = false; 804c2aa98e2SPeter Wemm int qtype; 805a7ec597cSGregory Neil Shapiro int initial; 806c2aa98e2SPeter Wemm int loopcnt; 80740266059SGregory Neil Shapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 808c2aa98e2SPeter Wemm char *searchlist[MAXDNSRCH + 2]; 809c2aa98e2SPeter Wemm 810c2aa98e2SPeter Wemm if (tTd(8, 2)) 81140266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 812c2aa98e2SPeter Wemm 813c2aa98e2SPeter Wemm if ((_res.options & RES_INIT) == 0 && res_init() == -1) 814c2aa98e2SPeter Wemm { 815c2aa98e2SPeter Wemm *statp = EX_UNAVAILABLE; 81640266059SGregory Neil Shapiro return false; 817c2aa98e2SPeter Wemm } 818c2aa98e2SPeter Wemm 819193538b7SGregory Neil Shapiro *statp = EX_OK; 820193538b7SGregory Neil Shapiro 821c2aa98e2SPeter Wemm /* 822c2aa98e2SPeter Wemm ** Initialize domain search list. If there is at least one 823c2aa98e2SPeter Wemm ** dot in the name, search the unmodified name first so we 824c2aa98e2SPeter Wemm ** find "vse.CS" in Czechoslovakia instead of in the local 82506f25ae9SGregory Neil Shapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 82606f25ae9SGregory Neil Shapiro ** longer a country named Czechoslovakia but this type of problem 82706f25ae9SGregory Neil Shapiro ** is still present. 828c2aa98e2SPeter Wemm ** 829c2aa98e2SPeter Wemm ** Older versions of the resolver could create this 830c2aa98e2SPeter Wemm ** list by tearing apart the host name. 831c2aa98e2SPeter Wemm */ 832c2aa98e2SPeter Wemm 833c2aa98e2SPeter Wemm loopcnt = 0; 834c2aa98e2SPeter Wemm cnameloop: 835c2aa98e2SPeter Wemm /* Check for dots in the name */ 836c2aa98e2SPeter Wemm for (cp = host, n = 0; *cp != '\0'; cp++) 837c2aa98e2SPeter Wemm if (*cp == '.') 838c2aa98e2SPeter Wemm n++; 839c2aa98e2SPeter Wemm 840c2aa98e2SPeter Wemm /* 841c2aa98e2SPeter Wemm ** Build the search list. 842c2aa98e2SPeter Wemm ** If there is at least one dot in name, start with a null 843c2aa98e2SPeter Wemm ** domain to search the unmodified name first. 844c2aa98e2SPeter Wemm ** If name does not end with a dot and search up local domain 845c2aa98e2SPeter Wemm ** tree desired, append each local domain component to the 846c2aa98e2SPeter Wemm ** search list; if name contains no dots and default domain 847c2aa98e2SPeter Wemm ** name is desired, append default domain name to search list; 848c2aa98e2SPeter Wemm ** else if name ends in a dot, remove that dot. 849c2aa98e2SPeter Wemm */ 850c2aa98e2SPeter Wemm 851c2aa98e2SPeter Wemm dp = searchlist; 852c2aa98e2SPeter Wemm if (n > 0) 853c2aa98e2SPeter Wemm *dp++ = ""; 854c2aa98e2SPeter Wemm if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 855c2aa98e2SPeter Wemm { 85606f25ae9SGregory Neil Shapiro /* make sure there are less than MAXDNSRCH domains */ 85706f25ae9SGregory Neil Shapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 85806f25ae9SGregory Neil Shapiro *domain != NULL && ret < MAXDNSRCH; 85906f25ae9SGregory Neil Shapiro ret++) 860c2aa98e2SPeter Wemm *dp++ = *domain++; 861c2aa98e2SPeter Wemm } 862c2aa98e2SPeter Wemm else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 863c2aa98e2SPeter Wemm { 864c2aa98e2SPeter Wemm *dp++ = _res.defdname; 865c2aa98e2SPeter Wemm } 866c2aa98e2SPeter Wemm else if (*cp == '.') 867c2aa98e2SPeter Wemm { 868c2aa98e2SPeter Wemm *cp = '\0'; 869c2aa98e2SPeter Wemm } 870c2aa98e2SPeter Wemm *dp = NULL; 871c2aa98e2SPeter Wemm 872c2aa98e2SPeter Wemm /* 873c2aa98e2SPeter Wemm ** Now loop through the search list, appending each domain in turn 874c2aa98e2SPeter Wemm ** name and searching for a match. 875c2aa98e2SPeter Wemm */ 876c2aa98e2SPeter Wemm 877c2aa98e2SPeter Wemm mxmatch = NULL; 878a7ec597cSGregory Neil Shapiro initial = T_A; 879a7ec597cSGregory Neil Shapiro # if NETINET6 880a7ec597cSGregory Neil Shapiro if (InetMode == AF_INET6) 881a7ec597cSGregory Neil Shapiro initial = T_AAAA; 882a7ec597cSGregory Neil Shapiro # endif /* NETINET6 */ 883a7ec597cSGregory Neil Shapiro qtype = initial; 884c2aa98e2SPeter Wemm 885c2aa98e2SPeter Wemm for (dp = searchlist; *dp != NULL; ) 886c2aa98e2SPeter Wemm { 887a7ec597cSGregory Neil Shapiro if (qtype == initial) 88840266059SGregory Neil Shapiro gotmx = false; 889c2aa98e2SPeter Wemm if (tTd(8, 5)) 89040266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 891c2aa98e2SPeter Wemm host, *dp, 89206f25ae9SGregory Neil Shapiro # if NETINET6 89306f25ae9SGregory Neil Shapiro qtype == T_AAAA ? "AAAA" : 89406f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 89506f25ae9SGregory Neil Shapiro qtype == T_A ? "A" : 89606f25ae9SGregory Neil Shapiro qtype == T_MX ? "MX" : 89706f25ae9SGregory Neil Shapiro "???"); 898193538b7SGregory Neil Shapiro errno = 0; 899c2aa98e2SPeter Wemm ret = res_querydomain(host, *dp, C_IN, qtype, 900c2aa98e2SPeter Wemm answer.qb2, sizeof(answer.qb2)); 901c2aa98e2SPeter Wemm if (ret <= 0) 902c2aa98e2SPeter Wemm { 90340266059SGregory Neil Shapiro int save_errno = errno; 904c2aa98e2SPeter Wemm 90540266059SGregory Neil Shapiro if (tTd(8, 7)) 90640266059SGregory Neil Shapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 90740266059SGregory Neil Shapiro save_errno, h_errno); 90840266059SGregory Neil Shapiro 90940266059SGregory Neil Shapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 910c2aa98e2SPeter Wemm { 911193538b7SGregory Neil Shapiro /* 91240266059SGregory Neil Shapiro ** the name server seems to be down or broken. 913193538b7SGregory Neil Shapiro */ 914193538b7SGregory Neil Shapiro 915602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(TRY_AGAIN); 916605302a5SGregory Neil Shapiro if (**dp == '\0') 917605302a5SGregory Neil Shapiro { 918605302a5SGregory Neil Shapiro if (*statp == EX_OK) 919605302a5SGregory Neil Shapiro *statp = EX_TEMPFAIL; 920605302a5SGregory Neil Shapiro goto nexttype; 921605302a5SGregory Neil Shapiro } 922c2aa98e2SPeter Wemm *statp = EX_TEMPFAIL; 92306f25ae9SGregory Neil Shapiro 924602a2b1bSGregory Neil Shapiro if (WorkAroundBrokenAAAA) 925602a2b1bSGregory Neil Shapiro { 926193538b7SGregory Neil Shapiro /* 927193538b7SGregory Neil Shapiro ** Only return if not TRY_AGAIN as an 928193538b7SGregory Neil Shapiro ** attempt with a different qtype may 929193538b7SGregory Neil Shapiro ** succeed (res_querydomain() calls 930193538b7SGregory Neil Shapiro ** res_query() calls res_send() which 931193538b7SGregory Neil Shapiro ** sets errno to ETIMEDOUT if the 932193538b7SGregory Neil Shapiro ** nameservers could be contacted but 933193538b7SGregory Neil Shapiro ** didn't give an answer). 934193538b7SGregory Neil Shapiro */ 935193538b7SGregory Neil Shapiro 93640266059SGregory Neil Shapiro if (save_errno != ETIMEDOUT) 93740266059SGregory Neil Shapiro return false; 938602a2b1bSGregory Neil Shapiro } 93940266059SGregory Neil Shapiro else 94040266059SGregory Neil Shapiro return false; 941c2aa98e2SPeter Wemm } 942c2aa98e2SPeter Wemm 943605302a5SGregory Neil Shapiro nexttype: 944c2aa98e2SPeter Wemm if (h_errno != HOST_NOT_FOUND) 945c2aa98e2SPeter Wemm { 946c2aa98e2SPeter Wemm /* might have another type of interest */ 94706f25ae9SGregory Neil Shapiro # if NETINET6 94840266059SGregory Neil Shapiro if (qtype == T_AAAA) 94906f25ae9SGregory Neil Shapiro { 950c2aa98e2SPeter Wemm qtype = T_A; 951c2aa98e2SPeter Wemm continue; 952c2aa98e2SPeter Wemm } 95340266059SGregory Neil Shapiro else 95406f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 95540266059SGregory Neil Shapiro if (qtype == T_A && !gotmx && 95606f25ae9SGregory Neil Shapiro (trymx || **dp == '\0')) 957c2aa98e2SPeter Wemm { 958c2aa98e2SPeter Wemm qtype = T_MX; 959c2aa98e2SPeter Wemm continue; 960c2aa98e2SPeter Wemm } 961c2aa98e2SPeter Wemm } 962c2aa98e2SPeter Wemm 963c2aa98e2SPeter Wemm /* definite no -- try the next domain */ 964c2aa98e2SPeter Wemm dp++; 965a7ec597cSGregory Neil Shapiro qtype = initial; 966c2aa98e2SPeter Wemm continue; 967c2aa98e2SPeter Wemm } 968c2aa98e2SPeter Wemm else if (tTd(8, 7)) 96940266059SGregory Neil Shapiro sm_dprintf("\tYES\n"); 970c2aa98e2SPeter Wemm 971c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 972c2aa98e2SPeter Wemm if (ret > sizeof(answer)) 973c2aa98e2SPeter Wemm ret = sizeof(answer); 97440266059SGregory Neil Shapiro if (ret < 0) 97540266059SGregory Neil Shapiro { 97640266059SGregory Neil Shapiro *statp = EX_SOFTWARE; 97740266059SGregory Neil Shapiro return false; 97840266059SGregory Neil Shapiro } 979c2aa98e2SPeter Wemm 980c2aa98e2SPeter Wemm /* 981c2aa98e2SPeter Wemm ** Appear to have a match. Confirm it by searching for A or 982c2aa98e2SPeter Wemm ** CNAME records. If we don't have a local domain 983c2aa98e2SPeter Wemm ** wild card MX record, we will accept MX as well. 984c2aa98e2SPeter Wemm */ 985c2aa98e2SPeter Wemm 986c2aa98e2SPeter Wemm hp = (HEADER *) &answer; 98740266059SGregory Neil Shapiro ap = (unsigned char *) &answer + HFIXEDSZ; 98840266059SGregory Neil Shapiro eom = (unsigned char *) &answer + ret; 989c2aa98e2SPeter Wemm 990c2aa98e2SPeter Wemm /* skip question part of response -- we know what we asked */ 99140266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 99206f25ae9SGregory Neil Shapiro qdcount--; 99306f25ae9SGregory Neil Shapiro ap += ret + QFIXEDSZ) 994c2aa98e2SPeter Wemm { 995c2aa98e2SPeter Wemm if ((ret = dn_skipname(ap, eom)) < 0) 996c2aa98e2SPeter Wemm { 997c2aa98e2SPeter Wemm if (tTd(8, 20)) 99840266059SGregory Neil Shapiro sm_dprintf("qdcount failure (%d)\n", 99940266059SGregory Neil Shapiro ntohs((unsigned short) hp->qdcount)); 1000c2aa98e2SPeter Wemm *statp = EX_SOFTWARE; 100140266059SGregory Neil Shapiro return false; /* ???XXX??? */ 1002c2aa98e2SPeter Wemm } 1003c2aa98e2SPeter Wemm } 1004c2aa98e2SPeter Wemm 100540266059SGregory Neil Shapiro amatch = false; 100640266059SGregory Neil Shapiro for (ancount = ntohs((unsigned short) hp->ancount); 100706f25ae9SGregory Neil Shapiro --ancount >= 0 && ap < eom; 1008c2aa98e2SPeter Wemm ap += n) 1009c2aa98e2SPeter Wemm { 101040266059SGregory Neil Shapiro n = dn_expand((unsigned char *) &answer, eom, ap, 1011c2aa98e2SPeter Wemm (RES_UNC_T) nbuf, sizeof nbuf); 1012c2aa98e2SPeter Wemm if (n < 0) 1013c2aa98e2SPeter Wemm break; 1014c2aa98e2SPeter Wemm ap += n; 1015c2aa98e2SPeter Wemm GETSHORT(type, ap); 101640266059SGregory Neil Shapiro ap += INT16SZ; /* skip over class */ 101740266059SGregory Neil Shapiro GETLONG(ttl, ap); 101840266059SGregory Neil Shapiro GETSHORT(n, ap); /* rdlength */ 1019c2aa98e2SPeter Wemm switch (type) 1020c2aa98e2SPeter Wemm { 1021c2aa98e2SPeter Wemm case T_MX: 102240266059SGregory Neil Shapiro gotmx = true; 1023c2aa98e2SPeter Wemm if (**dp != '\0' && HasWildcardMX) 1024c2aa98e2SPeter Wemm { 1025c2aa98e2SPeter Wemm /* 1026c2aa98e2SPeter Wemm ** If we are using MX matches and have 1027c2aa98e2SPeter Wemm ** not yet gotten one, save this one 1028c2aa98e2SPeter Wemm ** but keep searching for an A or 1029c2aa98e2SPeter Wemm ** CNAME match. 1030c2aa98e2SPeter Wemm */ 1031c2aa98e2SPeter Wemm 1032c2aa98e2SPeter Wemm if (trymx && mxmatch == NULL) 1033c2aa98e2SPeter Wemm mxmatch = *dp; 1034c2aa98e2SPeter Wemm continue; 1035c2aa98e2SPeter Wemm } 1036c2aa98e2SPeter Wemm 1037c2aa98e2SPeter Wemm /* 1038c2aa98e2SPeter Wemm ** If we did not append a domain name, this 1039c2aa98e2SPeter Wemm ** must have been a canonical name to start 1040c2aa98e2SPeter Wemm ** with. Even if we did append a domain name, 1041c2aa98e2SPeter Wemm ** in the absence of a wildcard MX this must 1042c2aa98e2SPeter Wemm ** still be a real MX match. 1043c2aa98e2SPeter Wemm ** Such MX matches are as good as an A match, 1044c2aa98e2SPeter Wemm ** fall through. 1045c2aa98e2SPeter Wemm */ 104606f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 104706f25ae9SGregory Neil Shapiro 104806f25ae9SGregory Neil Shapiro # if NETINET6 104906f25ae9SGregory Neil Shapiro case T_AAAA: 105006f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 1051c2aa98e2SPeter Wemm case T_A: 1052c2aa98e2SPeter Wemm /* Flag that a good match was found */ 105340266059SGregory Neil Shapiro amatch = true; 1054c2aa98e2SPeter Wemm 1055c2aa98e2SPeter Wemm /* continue in case a CNAME also exists */ 1056c2aa98e2SPeter Wemm continue; 1057c2aa98e2SPeter Wemm 1058c2aa98e2SPeter Wemm case T_CNAME: 1059c2aa98e2SPeter Wemm if (DontExpandCnames) 1060c2aa98e2SPeter Wemm { 1061c2aa98e2SPeter Wemm /* got CNAME -- guaranteed canonical */ 106240266059SGregory Neil Shapiro amatch = true; 1063c2aa98e2SPeter Wemm break; 1064c2aa98e2SPeter Wemm } 1065c2aa98e2SPeter Wemm 1066c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH) 1067c2aa98e2SPeter Wemm { 1068c2aa98e2SPeter Wemm /*XXX should notify postmaster XXX*/ 1069c2aa98e2SPeter Wemm message("DNS failure: CNAME loop for %s", 1070c2aa98e2SPeter Wemm host); 1071c2aa98e2SPeter Wemm if (CurEnv->e_message == NULL) 1072c2aa98e2SPeter Wemm { 1073c2aa98e2SPeter Wemm char ebuf[MAXLINE]; 1074c2aa98e2SPeter Wemm 107540266059SGregory Neil Shapiro (void) sm_snprintf(ebuf, 107640266059SGregory Neil Shapiro sizeof ebuf, 1077c2aa98e2SPeter Wemm "Deferred: DNS failure: CNAME loop for %.100s", 1078c2aa98e2SPeter Wemm host); 107940266059SGregory Neil Shapiro CurEnv->e_message = 108040266059SGregory Neil Shapiro sm_rpool_strdup_x( 108140266059SGregory Neil Shapiro CurEnv->e_rpool, ebuf); 1082c2aa98e2SPeter Wemm } 1083602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(NO_RECOVERY); 1084c2aa98e2SPeter Wemm *statp = EX_CONFIG; 108540266059SGregory Neil Shapiro return false; 1086c2aa98e2SPeter Wemm } 1087c2aa98e2SPeter Wemm 1088c2aa98e2SPeter Wemm /* value points at name */ 108940266059SGregory Neil Shapiro if ((ret = dn_expand((unsigned char *)&answer, 109040266059SGregory Neil Shapiro eom, ap, (RES_UNC_T) nbuf, 109140266059SGregory Neil Shapiro sizeof(nbuf))) < 0) 1092c2aa98e2SPeter Wemm break; 109340266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1094c2aa98e2SPeter Wemm 1095c2aa98e2SPeter Wemm /* 1096c2aa98e2SPeter Wemm ** RFC 1034 section 3.6 specifies that CNAME 1097c2aa98e2SPeter Wemm ** should point at the canonical name -- but 1098c2aa98e2SPeter Wemm ** urges software to try again anyway. 1099c2aa98e2SPeter Wemm */ 1100c2aa98e2SPeter Wemm 1101c2aa98e2SPeter Wemm goto cnameloop; 1102c2aa98e2SPeter Wemm 1103c2aa98e2SPeter Wemm default: 1104c2aa98e2SPeter Wemm /* not a record of interest */ 1105c2aa98e2SPeter Wemm continue; 1106c2aa98e2SPeter Wemm } 1107c2aa98e2SPeter Wemm } 1108c2aa98e2SPeter Wemm 1109c2aa98e2SPeter Wemm if (amatch) 1110c2aa98e2SPeter Wemm { 1111c2aa98e2SPeter Wemm /* 1112c2aa98e2SPeter Wemm ** Got a good match -- either an A, CNAME, or an 1113c2aa98e2SPeter Wemm ** exact MX record. Save it and get out of here. 1114c2aa98e2SPeter Wemm */ 1115c2aa98e2SPeter Wemm 1116c2aa98e2SPeter Wemm mxmatch = *dp; 1117c2aa98e2SPeter Wemm break; 1118c2aa98e2SPeter Wemm } 1119c2aa98e2SPeter Wemm 1120c2aa98e2SPeter Wemm /* 1121c2aa98e2SPeter Wemm ** Nothing definitive yet. 1122c2aa98e2SPeter Wemm ** If this was a T_A query and we haven't yet found a MX 1123c2aa98e2SPeter Wemm ** match, try T_MX if allowed to do so. 1124c2aa98e2SPeter Wemm ** Otherwise, try the next domain. 1125c2aa98e2SPeter Wemm */ 1126c2aa98e2SPeter Wemm 112706f25ae9SGregory Neil Shapiro # if NETINET6 112840266059SGregory Neil Shapiro if (qtype == T_AAAA) 1129c2aa98e2SPeter Wemm qtype = T_A; 113040266059SGregory Neil Shapiro else 113106f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 113240266059SGregory Neil Shapiro if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 1133c2aa98e2SPeter Wemm qtype = T_MX; 1134c2aa98e2SPeter Wemm else 1135c2aa98e2SPeter Wemm { 1136a7ec597cSGregory Neil Shapiro qtype = initial; 1137c2aa98e2SPeter Wemm dp++; 1138c2aa98e2SPeter Wemm } 1139c2aa98e2SPeter Wemm } 1140c2aa98e2SPeter Wemm 1141c2aa98e2SPeter Wemm /* if nothing was found, we are done */ 1142c2aa98e2SPeter Wemm if (mxmatch == NULL) 1143c2aa98e2SPeter Wemm { 1144193538b7SGregory Neil Shapiro if (*statp == EX_OK) 1145c2aa98e2SPeter Wemm *statp = EX_NOHOST; 114640266059SGregory Neil Shapiro return false; 1147c2aa98e2SPeter Wemm } 1148c2aa98e2SPeter Wemm 1149c2aa98e2SPeter Wemm /* 1150c2aa98e2SPeter Wemm ** Create canonical name and return. 1151c2aa98e2SPeter Wemm ** If saved domain name is null, name was already canonical. 1152c2aa98e2SPeter Wemm ** Otherwise append the saved domain name. 1153c2aa98e2SPeter Wemm */ 1154c2aa98e2SPeter Wemm 115540266059SGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, 1156c2aa98e2SPeter Wemm *mxmatch == '\0' ? "" : ".", 1157c2aa98e2SPeter Wemm MAXDNAME, mxmatch); 115840266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1159c2aa98e2SPeter Wemm if (tTd(8, 5)) 116040266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: %s\n", host); 1161c2aa98e2SPeter Wemm *statp = EX_OK; 116240266059SGregory Neil Shapiro 116340266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 116440266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 116540266059SGregory Neil Shapiro *pttl = ttl; 116640266059SGregory Neil Shapiro return true; 1167c2aa98e2SPeter Wemm } 1168c2aa98e2SPeter Wemm #endif /* NAMED_BIND */ 1169