domain.c revision 141858
138032Speter/* 2132943Sgshapiro * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1538032Speter 1690792Sgshapiro#if NAMED_BIND 17141858SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.195 2004/08/04 21:11:31 ca Exp $ (with name server)") 1890792Sgshapiro#else /* NAMED_BIND */ 19141858SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.195 2004/08/04 21:11:31 ca Exp $ (without name server)") 2090792Sgshapiro#endif /* NAMED_BIND */ 2138032Speter 2238032Speter#if NAMED_BIND 2338032Speter 2464562Sgshapiro# include <arpa/inet.h> 2538032Speter 2690792Sgshapiro 2738032Speter/* 2838032Speter** The standard udp packet size PACKETSZ (512) is not sufficient for some 2938032Speter** nameserver answers containing very many resource records. The resolver 3038032Speter** may switch to tcp and retry if it detects udp packet overflow. 3138032Speter** Also note that the resolver routines res_query and res_search return 3238032Speter** the size of the *un*truncated answer in case the supplied answer buffer 3338032Speter** it not big enough to accommodate the entire answer. 3438032Speter*/ 3538032Speter 3664562Sgshapiro# ifndef MAXPACKET 3764562Sgshapiro# define MAXPACKET 8192 /* max packet size used internally by BIND */ 3864562Sgshapiro# endif /* ! MAXPACKET */ 3938032Speter 4038032Spetertypedef union 4138032Speter{ 4290792Sgshapiro HEADER qb1; 4390792Sgshapiro unsigned char qb2[MAXPACKET]; 4438032Speter} querybuf; 4538032Speter 4664562Sgshapiro# ifndef MXHOSTBUFSIZE 4764562Sgshapiro# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 4864562Sgshapiro# endif /* ! MXHOSTBUFSIZE */ 4938032Speter 5038032Speterstatic char MXHostBuf[MXHOSTBUFSIZE]; 5190792Sgshapiro#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 5290792Sgshapiro ERROR: _MXHOSTBUFSIZE is out of range 5390792Sgshapiro#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 5438032Speter 5564562Sgshapiro# ifndef MAXDNSRCH 5664562Sgshapiro# define MAXDNSRCH 6 /* number of possible domains to search */ 5764562Sgshapiro# endif /* ! MAXDNSRCH */ 5838032Speter 5964562Sgshapiro# ifndef RES_DNSRCH_VARIABLE 6064562Sgshapiro# define RES_DNSRCH_VARIABLE _res.dnsrch 6164562Sgshapiro# endif /* ! RES_DNSRCH_VARIABLE */ 6238032Speter 6364562Sgshapiro# ifndef NO_DATA 6464562Sgshapiro# define NO_DATA NO_ADDRESS 6564562Sgshapiro# endif /* ! NO_DATA */ 6638032Speter 6764562Sgshapiro# ifndef HFIXEDSZ 6864562Sgshapiro# define HFIXEDSZ 12 /* sizeof(HEADER) */ 6964562Sgshapiro# endif /* ! HFIXEDSZ */ 7038032Speter 7164562Sgshapiro# define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 7264562Sgshapiro 7364562Sgshapiro# if defined(__RES) && (__RES >= 19940415) 7464562Sgshapiro# define RES_UNC_T char * 7564562Sgshapiro# else /* defined(__RES) && (__RES >= 19940415) */ 7690792Sgshapiro# define RES_UNC_T unsigned char * 7764562Sgshapiro# endif /* defined(__RES) && (__RES >= 19940415) */ 7864562Sgshapiro 7964562Sgshapirostatic int mxrand __P((char *)); 8090792Sgshapirostatic int fallbackmxrr __P((int, unsigned short *, char **)); 8164562Sgshapiro 8290792Sgshapiro/* 8390792Sgshapiro** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 8490792Sgshapiro** 8590792Sgshapiro** We have to initialize this once before doing anything else. 8690792Sgshapiro** Moreover, we have to repeat this from time to time to avoid 8790792Sgshapiro** stale data, e.g., in persistent queue runners. 8890792Sgshapiro** This should be done in a parent process so the child 8990792Sgshapiro** processes have the right data. 9090792Sgshapiro** 9190792Sgshapiro** Parameters: 9290792Sgshapiro** host -- the name of the fallback MX host. 9390792Sgshapiro** 9490792Sgshapiro** Returns: 9590792Sgshapiro** number of MX records. 9690792Sgshapiro** 9790792Sgshapiro** Side Effects: 98132943Sgshapiro** Populates NumFallbackMXHosts and fbhosts. 9990792Sgshapiro** Sets renewal time (based on TTL). 10090792Sgshapiro*/ 10190792Sgshapiro 102132943Sgshapiroint NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 10390792Sgshapirostatic char *fbhosts[MAXMXHOSTS + 1]; 10490792Sgshapiro 10590792Sgshapiroint 10690792Sgshapirogetfallbackmxrr(host) 10790792Sgshapiro char *host; 10890792Sgshapiro{ 10990792Sgshapiro int i, rcode; 11090792Sgshapiro int ttl; 11190792Sgshapiro static time_t renew = 0; 11290792Sgshapiro 11390792Sgshapiro#if 0 11490792Sgshapiro /* This is currently done before this function is called. */ 11590792Sgshapiro if (host == NULL || *host == '\0') 11690792Sgshapiro return 0; 11790792Sgshapiro#endif /* 0 */ 118132943Sgshapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 119132943Sgshapiro return NumFallbackMXHosts; 12090792Sgshapiro if (host[0] == '[') 12190792Sgshapiro { 12290792Sgshapiro fbhosts[0] = host; 123132943Sgshapiro NumFallbackMXHosts = 1; 12490792Sgshapiro } 12590792Sgshapiro else 12690792Sgshapiro { 12790792Sgshapiro /* free old data */ 128132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 12990792Sgshapiro sm_free(fbhosts[i]); 13090792Sgshapiro 13190792Sgshapiro /* get new data */ 132132943Sgshapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false, 13390792Sgshapiro &rcode, false, &ttl); 13490792Sgshapiro renew = curtime() + ttl; 135132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 13690792Sgshapiro fbhosts[i] = newstr(fbhosts[i]); 13790792Sgshapiro } 138132943Sgshapiro return NumFallbackMXHosts; 13990792Sgshapiro} 14090792Sgshapiro 14190792Sgshapiro/* 14290792Sgshapiro** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 14390792Sgshapiro** 14490792Sgshapiro** Parameters: 14590792Sgshapiro** nmx -- current number of MX records. 14690792Sgshapiro** prefs -- array of preferences. 14790792Sgshapiro** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 14890792Sgshapiro** 14990792Sgshapiro** Returns: 15090792Sgshapiro** new number of MX records. 15190792Sgshapiro** 15290792Sgshapiro** Side Effects: 153132943Sgshapiro** If FallbackMX was set, it appends the MX records for 15490792Sgshapiro** that host to mxhosts (and modifies prefs accordingly). 15590792Sgshapiro*/ 15690792Sgshapiro 15790792Sgshapirostatic int 15890792Sgshapirofallbackmxrr(nmx, prefs, mxhosts) 15990792Sgshapiro int nmx; 16090792Sgshapiro unsigned short *prefs; 16190792Sgshapiro char **mxhosts; 16290792Sgshapiro{ 16390792Sgshapiro int i; 16490792Sgshapiro 165132943Sgshapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 16690792Sgshapiro { 16790792Sgshapiro if (nmx > 0) 16890792Sgshapiro prefs[nmx] = prefs[nmx - 1] + 1; 16990792Sgshapiro else 17090792Sgshapiro prefs[nmx] = 0; 17190792Sgshapiro mxhosts[nmx++] = fbhosts[i]; 17290792Sgshapiro } 17390792Sgshapiro return nmx; 17490792Sgshapiro} 17590792Sgshapiro 17690792Sgshapiro/* 17738032Speter** GETMXRR -- get MX resource records for a domain 17838032Speter** 17938032Speter** Parameters: 18038032Speter** host -- the name of the host to MX. 18138032Speter** mxhosts -- a pointer to a return buffer of MX records. 18264562Sgshapiro** mxprefs -- a pointer to a return buffer of MX preferences. 18364562Sgshapiro** If NULL, don't try to populate. 18490792Sgshapiro** droplocalhost -- If true, all MX records less preferred 18538032Speter** than the local host (as determined by $=w) will 18638032Speter** be discarded. 18738032Speter** rcode -- a pointer to an EX_ status code. 18890792Sgshapiro** tryfallback -- add also fallback MX host? 18990792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 19038032Speter** 19138032Speter** Returns: 19238032Speter** The number of MX records found. 19338032Speter** -1 if there is an internal failure. 19438032Speter** If no MX records are found, mxhosts[0] is set to host 19538032Speter** and 1 is returned. 19690792Sgshapiro** 19790792Sgshapiro** Side Effects: 19890792Sgshapiro** The entries made for mxhosts point to a static array 19990792Sgshapiro** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 20090792Sgshapiro** if it must be preserved across calls to this function. 20138032Speter*/ 20238032Speter 20338032Speterint 20490792Sgshapirogetmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) 20538032Speter char *host; 20638032Speter char **mxhosts; 20790792Sgshapiro unsigned short *mxprefs; 20838032Speter bool droplocalhost; 20938032Speter int *rcode; 21090792Sgshapiro bool tryfallback; 21190792Sgshapiro int *pttl; 21238032Speter{ 21390792Sgshapiro register unsigned char *eom, *cp; 21438032Speter register int i, j, n; 21538032Speter int nmx = 0; 21638032Speter register char *bp; 21738032Speter HEADER *hp; 21838032Speter querybuf answer; 21938032Speter int ancount, qdcount, buflen; 22090792Sgshapiro bool seenlocal = false; 22190792Sgshapiro unsigned short pref, type; 22290792Sgshapiro unsigned short localpref = 256; 223132943Sgshapiro char *fallbackMX = FallbackMX; 22490792Sgshapiro bool trycanon = false; 22590792Sgshapiro unsigned short *prefs; 226141858Sgshapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 22790792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 22838032Speter int weight[MAXMXHOSTS]; 22990792Sgshapiro int ttl = 0; 23064562Sgshapiro extern int res_query(), res_search(); 23138032Speter 23238032Speter if (tTd(8, 2)) 23390792Sgshapiro sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", 23490792Sgshapiro host, droplocalhost); 235120256Sgshapiro if (*host == '\0') 236120256Sgshapiro return 0; 23738032Speter 23890792Sgshapiro if ((fallbackMX != NULL && droplocalhost && 23990792Sgshapiro wordinclass(fallbackMX, 'w')) || !tryfallback) 24038032Speter { 24138032Speter /* don't use fallback for this pass */ 24238032Speter fallbackMX = NULL; 24338032Speter } 24438032Speter 24538032Speter *rcode = EX_OK; 24638032Speter 24764562Sgshapiro if (mxprefs != NULL) 24864562Sgshapiro prefs = mxprefs; 24964562Sgshapiro else 25064562Sgshapiro prefs = prefer; 25164562Sgshapiro 25238032Speter /* efficiency hack -- numeric or non-MX lookups */ 25338032Speter if (host[0] == '[') 25438032Speter goto punt; 25538032Speter 25638032Speter /* 25738032Speter ** If we don't have MX records in our host switch, don't 25838032Speter ** try for MX records. Note that this really isn't "right", 25938032Speter ** since we might be set up to try NIS first and then DNS; 26038032Speter ** if the host is found in NIS we really shouldn't be doing 26138032Speter ** MX lookups. However, that should be a degenerate case. 26238032Speter */ 26338032Speter 26438032Speter if (!UseNameServer) 26538032Speter goto punt; 26638032Speter if (HasWildcardMX && ConfigLevel >= 6) 26738032Speter resfunc = res_query; 26838032Speter else 26938032Speter resfunc = res_search; 27038032Speter 27138032Speter errno = 0; 27290792Sgshapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 27390792Sgshapiro sizeof(answer)); 27438032Speter if (n < 0) 27538032Speter { 27638032Speter if (tTd(8, 1)) 27790792Sgshapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 27890792Sgshapiro host == NULL ? "<NULL>" : host, errno, h_errno); 27938032Speter switch (h_errno) 28038032Speter { 28138032Speter case NO_DATA: 28290792Sgshapiro trycanon = true; 28364562Sgshapiro /* FALLTHROUGH */ 28438032Speter 28538032Speter case NO_RECOVERY: 28638032Speter /* no MX data on this host */ 28738032Speter goto punt; 28838032Speter 28938032Speter case HOST_NOT_FOUND: 29064562Sgshapiro# if BROKEN_RES_SEARCH 29138032Speter case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 29264562Sgshapiro# endif /* BROKEN_RES_SEARCH */ 29338032Speter /* host doesn't exist in DNS; might be in /etc/hosts */ 29490792Sgshapiro trycanon = true; 29538032Speter *rcode = EX_NOHOST; 29638032Speter goto punt; 29738032Speter 29838032Speter case TRY_AGAIN: 29938032Speter case -1: 30038032Speter /* couldn't connect to the name server */ 30138032Speter if (fallbackMX != NULL) 30238032Speter { 30338032Speter /* name server is hosed -- push to fallback */ 30490792Sgshapiro return fallbackmxrr(nmx, prefs, mxhosts); 30538032Speter } 30638032Speter /* it might come up later; better queue it up */ 30738032Speter *rcode = EX_TEMPFAIL; 30838032Speter break; 30938032Speter 31038032Speter default: 31190792Sgshapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 31238032Speter host, h_errno); 31338032Speter *rcode = EX_OSERR; 31438032Speter break; 31538032Speter } 31638032Speter 31738032Speter /* irreconcilable differences */ 31864562Sgshapiro return -1; 31938032Speter } 32038032Speter 32138032Speter /* avoid problems after truncation in tcp packets */ 32238032Speter if (n > sizeof(answer)) 32338032Speter n = sizeof(answer); 32438032Speter 32538032Speter /* find first satisfactory answer */ 32638032Speter hp = (HEADER *)&answer; 32790792Sgshapiro cp = (unsigned char *)&answer + HFIXEDSZ; 32890792Sgshapiro eom = (unsigned char *)&answer + n; 32990792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 33064562Sgshapiro qdcount--; 33164562Sgshapiro cp += n + QFIXEDSZ) 33264562Sgshapiro { 33338032Speter if ((n = dn_skipname(cp, eom)) < 0) 33438032Speter goto punt; 33564562Sgshapiro } 33690792Sgshapiro 33790792Sgshapiro /* NOTE: see definition of MXHostBuf! */ 33838032Speter buflen = sizeof(MXHostBuf) - 1; 33990792Sgshapiro SM_ASSERT(buflen > 0); 34038032Speter bp = MXHostBuf; 34190792Sgshapiro ancount = ntohs((unsigned short) hp->ancount); 34290792Sgshapiro 34390792Sgshapiro /* See RFC 1035 for layout of RRs. */ 344132943Sgshapiro /* XXX leave room for FallbackMX ? */ 34538032Speter while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 34638032Speter { 34790792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 34890792Sgshapiro (RES_UNC_T) bp, buflen)) < 0) 34938032Speter break; 35038032Speter cp += n; 35138032Speter GETSHORT(type, cp); 35290792Sgshapiro cp += INT16SZ; /* skip over class */ 35390792Sgshapiro GETLONG(ttl, cp); 35490792Sgshapiro GETSHORT(n, cp); /* rdlength */ 35538032Speter if (type != T_MX) 35638032Speter { 35738032Speter if (tTd(8, 8) || _res.options & RES_DEBUG) 35890792Sgshapiro sm_dprintf("unexpected answer type %d, size %d\n", 35964562Sgshapiro type, n); 36038032Speter cp += n; 36138032Speter continue; 36238032Speter } 36338032Speter GETSHORT(pref, cp); 36490792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 36538032Speter (RES_UNC_T) bp, buflen)) < 0) 36638032Speter break; 36738032Speter cp += n; 36890792Sgshapiro n = strlen(bp); 36990792Sgshapiro# if 0 37090792Sgshapiro /* Can this happen? */ 37190792Sgshapiro if (n == 0) 37290792Sgshapiro { 37390792Sgshapiro if (LogLevel > 4) 37490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 37590792Sgshapiro "MX records for %s contain empty string", 37690792Sgshapiro host); 37790792Sgshapiro continue; 37890792Sgshapiro } 37990792Sgshapiro# endif /* 0 */ 38038032Speter if (wordinclass(bp, 'w')) 38138032Speter { 38238032Speter if (tTd(8, 3)) 38390792Sgshapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 38438032Speter bp, pref); 38538032Speter if (droplocalhost) 38638032Speter { 38738032Speter if (!seenlocal || pref < localpref) 38838032Speter localpref = pref; 38990792Sgshapiro seenlocal = true; 39038032Speter continue; 39138032Speter } 39238032Speter weight[nmx] = 0; 39338032Speter } 39438032Speter else 39538032Speter weight[nmx] = mxrand(bp); 39664562Sgshapiro prefs[nmx] = pref; 39738032Speter mxhosts[nmx++] = bp; 39838032Speter bp += n; 39938032Speter if (bp[-1] != '.') 40038032Speter { 40138032Speter *bp++ = '.'; 40238032Speter n++; 40338032Speter } 40438032Speter *bp++ = '\0'; 40590792Sgshapiro if (buflen < n + 1) 40690792Sgshapiro { 40790792Sgshapiro /* don't want to wrap buflen */ 40890792Sgshapiro break; 40990792Sgshapiro } 41038032Speter buflen -= n + 1; 41138032Speter } 41238032Speter 41390792Sgshapiro /* return only one TTL entry, that should be sufficient */ 41490792Sgshapiro if (ttl > 0 && pttl != NULL) 41590792Sgshapiro *pttl = ttl; 41690792Sgshapiro 41738032Speter /* sort the records */ 41838032Speter for (i = 0; i < nmx; i++) 41938032Speter { 42038032Speter for (j = i + 1; j < nmx; j++) 42138032Speter { 42264562Sgshapiro if (prefs[i] > prefs[j] || 42364562Sgshapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 42438032Speter { 42538032Speter register int temp; 42638032Speter register char *temp1; 42738032Speter 42864562Sgshapiro temp = prefs[i]; 42964562Sgshapiro prefs[i] = prefs[j]; 43064562Sgshapiro prefs[j] = temp; 43138032Speter temp1 = mxhosts[i]; 43238032Speter mxhosts[i] = mxhosts[j]; 43338032Speter mxhosts[j] = temp1; 43438032Speter temp = weight[i]; 43538032Speter weight[i] = weight[j]; 43638032Speter weight[j] = temp; 43738032Speter } 43838032Speter } 43964562Sgshapiro if (seenlocal && prefs[i] >= localpref) 44038032Speter { 44138032Speter /* truncate higher preference part of list */ 44238032Speter nmx = i; 44338032Speter } 44438032Speter } 44538032Speter 44638032Speter /* delete duplicates from list (yes, some bozos have duplicates) */ 44738032Speter for (i = 0; i < nmx - 1; ) 44838032Speter { 44990792Sgshapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 45038032Speter i++; 45138032Speter else 45238032Speter { 45338032Speter /* compress out duplicate */ 45438032Speter for (j = i + 1; j < nmx; j++) 45564562Sgshapiro { 45638032Speter mxhosts[j] = mxhosts[j + 1]; 45764562Sgshapiro prefs[j] = prefs[j + 1]; 45864562Sgshapiro } 45938032Speter nmx--; 46038032Speter } 46138032Speter } 46238032Speter 46338032Speter if (nmx == 0) 46438032Speter { 46538032Speterpunt: 46664562Sgshapiro if (seenlocal) 46738032Speter { 46864562Sgshapiro struct hostent *h = NULL; 46964562Sgshapiro 47038032Speter /* 47138032Speter ** If we have deleted all MX entries, this is 47238032Speter ** an error -- we should NEVER send to a host that 47338032Speter ** has an MX, and this should have been caught 47438032Speter ** earlier in the config file. 47538032Speter ** 47638032Speter ** Some sites prefer to go ahead and try the 47738032Speter ** A record anyway; that case is handled by 47838032Speter ** setting TryNullMXList. I believe this is a 47938032Speter ** bad idea, but it's up to you.... 48038032Speter */ 48138032Speter 48264562Sgshapiro if (TryNullMXList) 48364562Sgshapiro { 48473188Sgshapiro SM_SET_H_ERRNO(0); 48564562Sgshapiro errno = 0; 48664562Sgshapiro h = sm_gethostbyname(host, AF_INET); 48764562Sgshapiro if (h == NULL) 48864562Sgshapiro { 48964562Sgshapiro if (errno == ETIMEDOUT || 49064562Sgshapiro h_errno == TRY_AGAIN || 49164562Sgshapiro (errno == ECONNREFUSED && 49264562Sgshapiro UseNameServer)) 49364562Sgshapiro { 49464562Sgshapiro *rcode = EX_TEMPFAIL; 49564562Sgshapiro return -1; 49664562Sgshapiro } 49764562Sgshapiro# if NETINET6 49873188Sgshapiro SM_SET_H_ERRNO(0); 49964562Sgshapiro errno = 0; 50064562Sgshapiro h = sm_gethostbyname(host, AF_INET6); 50164562Sgshapiro if (h == NULL && 50264562Sgshapiro (errno == ETIMEDOUT || 50364562Sgshapiro h_errno == TRY_AGAIN || 50464562Sgshapiro (errno == ECONNREFUSED && 50564562Sgshapiro UseNameServer))) 50664562Sgshapiro { 50764562Sgshapiro *rcode = EX_TEMPFAIL; 50864562Sgshapiro return -1; 50964562Sgshapiro } 51064562Sgshapiro# endif /* NETINET6 */ 51164562Sgshapiro } 51264562Sgshapiro } 51364562Sgshapiro 51464562Sgshapiro if (h == NULL) 51564562Sgshapiro { 51664562Sgshapiro *rcode = EX_CONFIG; 51764562Sgshapiro syserr("MX list for %s points back to %s", 51864562Sgshapiro host, MyHostName); 51964562Sgshapiro return -1; 52064562Sgshapiro } 52190792Sgshapiro# if NETINET6 52271345Sgshapiro freehostent(h); 52371345Sgshapiro hp = NULL; 52490792Sgshapiro# endif /* NETINET6 */ 52538032Speter } 52690792Sgshapiro if (strlen(host) >= sizeof MXHostBuf) 52738032Speter { 52838032Speter *rcode = EX_CONFIG; 52938032Speter syserr("Host name %s too long", 53038032Speter shortenstring(host, MAXSHORTSTR)); 53138032Speter return -1; 53238032Speter } 53390792Sgshapiro (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf); 53438032Speter mxhosts[0] = MXHostBuf; 53564562Sgshapiro prefs[0] = 0; 53638032Speter if (host[0] == '[') 53738032Speter { 53838032Speter register char *p; 53964562Sgshapiro# if NETINET6 54064562Sgshapiro struct sockaddr_in6 tmp6; 54164562Sgshapiro# endif /* NETINET6 */ 54238032Speter 54338032Speter /* this may be an MX suppression-style address */ 54438032Speter p = strchr(MXHostBuf, ']'); 54538032Speter if (p != NULL) 54638032Speter { 54738032Speter *p = '\0'; 54864562Sgshapiro 54938032Speter if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 55038032Speter { 55138032Speter nmx++; 55238032Speter *p = ']'; 55338032Speter } 55464562Sgshapiro# if NETINET6 55590792Sgshapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 55690792Sgshapiro &tmp6.sin6_addr) == 1) 55764562Sgshapiro { 55864562Sgshapiro nmx++; 55964562Sgshapiro *p = ']'; 56064562Sgshapiro } 56164562Sgshapiro# endif /* NETINET6 */ 56238032Speter else 56338032Speter { 56490792Sgshapiro trycanon = true; 56538032Speter mxhosts[0]++; 56638032Speter } 56738032Speter } 56838032Speter } 56938032Speter if (trycanon && 57090792Sgshapiro getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl)) 57138032Speter { 57290792Sgshapiro /* XXX MXHostBuf == "" ? is that possible? */ 57338032Speter bp = &MXHostBuf[strlen(MXHostBuf)]; 57438032Speter if (bp[-1] != '.') 57538032Speter { 57638032Speter *bp++ = '.'; 57738032Speter *bp = '\0'; 57838032Speter } 57938032Speter nmx = 1; 58038032Speter } 58138032Speter } 58238032Speter 58338032Speter /* if we have a default lowest preference, include that */ 58438032Speter if (fallbackMX != NULL && !seenlocal) 58564562Sgshapiro { 58690792Sgshapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 58764562Sgshapiro } 58864562Sgshapiro return nmx; 58938032Speter} 59090792Sgshapiro/* 59138032Speter** MXRAND -- create a randomizer for equal MX preferences 59238032Speter** 59338032Speter** If two MX hosts have equal preferences we want to randomize 59438032Speter** the selection. But in order for signatures to be the same, 59538032Speter** we need to randomize the same way each time. This function 59638032Speter** computes a pseudo-random hash function from the host name. 59738032Speter** 59838032Speter** Parameters: 59938032Speter** host -- the name of the host. 60038032Speter** 60138032Speter** Returns: 60238032Speter** A random but repeatable value based on the host name. 60338032Speter*/ 60438032Speter 60564562Sgshapirostatic int 60638032Spetermxrand(host) 60738032Speter register char *host; 60838032Speter{ 60938032Speter int hfunc; 61038032Speter static unsigned int seed; 61138032Speter 61238032Speter if (seed == 0) 61338032Speter { 61438032Speter seed = (int) curtime() & 0xffff; 61538032Speter if (seed == 0) 61638032Speter seed++; 61738032Speter } 61838032Speter 61938032Speter if (tTd(17, 9)) 62090792Sgshapiro sm_dprintf("mxrand(%s)", host); 62138032Speter 62238032Speter hfunc = seed; 62338032Speter while (*host != '\0') 62438032Speter { 62538032Speter int c = *host++; 62638032Speter 62738032Speter if (isascii(c) && isupper(c)) 62838032Speter c = tolower(c); 62938032Speter hfunc = ((hfunc << 1) ^ c) % 2003; 63038032Speter } 63138032Speter 63238032Speter hfunc &= 0xff; 63338032Speter hfunc++; 63438032Speter 63538032Speter if (tTd(17, 9)) 63690792Sgshapiro sm_dprintf(" = %d\n", hfunc); 63738032Speter return hfunc; 63838032Speter} 63990792Sgshapiro/* 64038032Speter** BESTMX -- find the best MX for a name 64138032Speter** 64238032Speter** This is really a hack, but I don't see any obvious way 64338032Speter** to generalize it at the moment. 64438032Speter*/ 64538032Speter 64638032Speter/* ARGSUSED3 */ 64738032Speterchar * 64838032Speterbestmx_map_lookup(map, name, av, statp) 64938032Speter MAP *map; 65038032Speter char *name; 65138032Speter char **av; 65238032Speter int *statp; 65338032Speter{ 65438032Speter int nmx; 65538032Speter int saveopts = _res.options; 65690792Sgshapiro int i; 65790792Sgshapiro ssize_t len = 0; 65890792Sgshapiro char *result; 65990792Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 66090792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 66190792Sgshapiro char *buf; 66290792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */ 66338032Speter char *p; 66442575Speter char buf[PSBUFSIZE / 2]; 66590792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 66638032Speter 66738032Speter _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 668102528Sgshapiro nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); 66938032Speter _res.options = saveopts; 67038032Speter if (nmx <= 0) 67138032Speter return NULL; 67238032Speter if (bitset(MF_MATCHONLY, map->map_mflags)) 67338032Speter return map_rewrite(map, name, strlen(name), NULL); 67438032Speter if ((map->map_coldelim == '\0') || (nmx == 1)) 67538032Speter return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 67638032Speter 67738032Speter /* 67842575Speter ** We were given a -z flag (return all MXs) and there are multiple 67942575Speter ** ones. We need to build them all into a list. 68038032Speter */ 68190792Sgshapiro 68290792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 68390792Sgshapiro for (i = 0; i < nmx; i++) 68490792Sgshapiro { 68590792Sgshapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 68690792Sgshapiro { 68790792Sgshapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 68890792Sgshapiro mxhosts[i], map->map_coldelim); 68990792Sgshapiro return NULL; 69090792Sgshapiro } 69190792Sgshapiro len += strlen(mxhosts[i]) + 1; 69290792Sgshapiro if (len < 0) 69390792Sgshapiro { 69490792Sgshapiro len -= strlen(mxhosts[i]) + 1; 69590792Sgshapiro break; 69690792Sgshapiro } 69790792Sgshapiro } 69890792Sgshapiro buf = (char *) sm_malloc(len); 69990792Sgshapiro if (buf == NULL) 70090792Sgshapiro { 70190792Sgshapiro *statp = EX_UNAVAILABLE; 70290792Sgshapiro return NULL; 70390792Sgshapiro } 70490792Sgshapiro *buf = '\0'; 70590792Sgshapiro for (i = 0; i < nmx; i++) 70690792Sgshapiro { 70790792Sgshapiro int end; 70890792Sgshapiro 70990792Sgshapiro end = sm_strlcat(buf, mxhosts[i], len); 71090792Sgshapiro if (i != nmx && end + 1 < len) 71190792Sgshapiro { 71290792Sgshapiro buf[end] = map->map_coldelim; 71390792Sgshapiro buf[end + 1] = '\0'; 71490792Sgshapiro } 71590792Sgshapiro } 71690792Sgshapiro 71790792Sgshapiro /* Cleanly truncate for rulesets */ 71890792Sgshapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 71990792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */ 72038032Speter p = buf; 72138032Speter for (i = 0; i < nmx; i++) 72238032Speter { 72390792Sgshapiro size_t slen; 72464562Sgshapiro 72538032Speter if (strchr(mxhosts[i], map->map_coldelim) != NULL) 72638032Speter { 72738032Speter syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 72838032Speter mxhosts[i], map->map_coldelim); 72938032Speter return NULL; 73038032Speter } 73138032Speter slen = strlen(mxhosts[i]); 73238032Speter if (len + slen + 2 > sizeof buf) 73338032Speter break; 73438032Speter if (i > 0) 73538032Speter { 73638032Speter *p++ = map->map_coldelim; 73738032Speter len++; 73838032Speter } 73990792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len); 74038032Speter p += slen; 74138032Speter len += slen; 74238032Speter } 74390792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 74490792Sgshapiro 74590792Sgshapiro result = map_rewrite(map, buf, len, av); 74690792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 74790792Sgshapiro sm_free(buf); 74890792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 74990792Sgshapiro return result; 75038032Speter} 75190792Sgshapiro/* 75238032Speter** DNS_GETCANONNAME -- get the canonical name for named host using DNS 75338032Speter** 75438032Speter** This algorithm tries to be smart about wildcard MX records. 75538032Speter** This is hard to do because DNS doesn't tell is if we matched 75638032Speter** against a wildcard or a specific MX. 75764562Sgshapiro** 75838032Speter** We always prefer A & CNAME records, since these are presumed 75938032Speter** to be specific. 76038032Speter** 76138032Speter** If we match an MX in one pass and lose it in the next, we use 76238032Speter** the old one. For example, consider an MX matching *.FOO.BAR.COM. 76338032Speter** A hostname bletch.foo.bar.com will match against this MX, but 76438032Speter** will stop matching when we try bletch.bar.com -- so we know 76538032Speter** that bletch.foo.bar.com must have been right. This fails if 76638032Speter** there was also an MX record matching *.BAR.COM, but there are 76738032Speter** some things that just can't be fixed. 76838032Speter** 76938032Speter** Parameters: 77038032Speter** host -- a buffer containing the name of the host. 77138032Speter** This is a value-result parameter. 77238032Speter** hbsize -- the size of the host buffer. 77338032Speter** trymx -- if set, try MX records as well as A and CNAME. 77438032Speter** statp -- pointer to place to store status. 77590792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 77638032Speter** 77738032Speter** Returns: 77890792Sgshapiro** true -- if the host matched. 77990792Sgshapiro** false -- otherwise. 78038032Speter*/ 78138032Speter 78238032Speterbool 78390792Sgshapirodns_getcanonname(host, hbsize, trymx, statp, pttl) 78438032Speter char *host; 78538032Speter int hbsize; 78638032Speter bool trymx; 78738032Speter int *statp; 78890792Sgshapiro int *pttl; 78938032Speter{ 79090792Sgshapiro register unsigned char *eom, *ap; 79138032Speter register char *cp; 79264562Sgshapiro register int n; 79338032Speter HEADER *hp; 79438032Speter querybuf answer; 79538032Speter int ancount, qdcount; 79638032Speter int ret; 79738032Speter char **domain; 79838032Speter int type; 79990792Sgshapiro int ttl = 0; 80038032Speter char **dp; 80138032Speter char *mxmatch; 80238032Speter bool amatch; 80390792Sgshapiro bool gotmx = false; 80438032Speter int qtype; 805120256Sgshapiro int initial; 80638032Speter int loopcnt; 80790792Sgshapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 80898121Sgshapiro char *searchlist[MAXDNSRCH + 2]; 80938032Speter 81038032Speter if (tTd(8, 2)) 81190792Sgshapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 81238032Speter 81338032Speter if ((_res.options & RES_INIT) == 0 && res_init() == -1) 81438032Speter { 81538032Speter *statp = EX_UNAVAILABLE; 81690792Sgshapiro return false; 81738032Speter } 81838032Speter 81971345Sgshapiro *statp = EX_OK; 82071345Sgshapiro 82138032Speter /* 82238032Speter ** Initialize domain search list. If there is at least one 82338032Speter ** dot in the name, search the unmodified name first so we 82438032Speter ** find "vse.CS" in Czechoslovakia instead of in the local 82564562Sgshapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 82664562Sgshapiro ** longer a country named Czechoslovakia but this type of problem 82764562Sgshapiro ** is still present. 82838032Speter ** 82938032Speter ** Older versions of the resolver could create this 83038032Speter ** list by tearing apart the host name. 83138032Speter */ 83238032Speter 83338032Speter loopcnt = 0; 83438032Spetercnameloop: 83538032Speter /* Check for dots in the name */ 83638032Speter for (cp = host, n = 0; *cp != '\0'; cp++) 83738032Speter if (*cp == '.') 83838032Speter n++; 83938032Speter 84038032Speter /* 84164562Sgshapiro ** Build the search list. 84238032Speter ** If there is at least one dot in name, start with a null 84338032Speter ** domain to search the unmodified name first. 84438032Speter ** If name does not end with a dot and search up local domain 84538032Speter ** tree desired, append each local domain component to the 84638032Speter ** search list; if name contains no dots and default domain 84738032Speter ** name is desired, append default domain name to search list; 84838032Speter ** else if name ends in a dot, remove that dot. 84938032Speter */ 85038032Speter 85138032Speter dp = searchlist; 85238032Speter if (n > 0) 85338032Speter *dp++ = ""; 85438032Speter if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 85538032Speter { 85664562Sgshapiro /* make sure there are less than MAXDNSRCH domains */ 85764562Sgshapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 85864562Sgshapiro *domain != NULL && ret < MAXDNSRCH; 85964562Sgshapiro ret++) 86038032Speter *dp++ = *domain++; 86138032Speter } 86238032Speter else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 86338032Speter { 86438032Speter *dp++ = _res.defdname; 86538032Speter } 86638032Speter else if (*cp == '.') 86738032Speter { 86838032Speter *cp = '\0'; 86938032Speter } 87038032Speter *dp = NULL; 87138032Speter 87238032Speter /* 87338032Speter ** Now loop through the search list, appending each domain in turn 87438032Speter ** name and searching for a match. 87538032Speter */ 87638032Speter 87738032Speter mxmatch = NULL; 878120256Sgshapiro initial = T_A; 879120256Sgshapiro# if NETINET6 880120256Sgshapiro if (InetMode == AF_INET6) 881120256Sgshapiro initial = T_AAAA; 882120256Sgshapiro# endif /* NETINET6 */ 883120256Sgshapiro qtype = initial; 88438032Speter 88538032Speter for (dp = searchlist; *dp != NULL; ) 88638032Speter { 887120256Sgshapiro if (qtype == initial) 88890792Sgshapiro gotmx = false; 88938032Speter if (tTd(8, 5)) 89090792Sgshapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 89138032Speter host, *dp, 89264562Sgshapiro# if NETINET6 89364562Sgshapiro qtype == T_AAAA ? "AAAA" : 89464562Sgshapiro# endif /* NETINET6 */ 89564562Sgshapiro qtype == T_A ? "A" : 89664562Sgshapiro qtype == T_MX ? "MX" : 89764562Sgshapiro "???"); 89871345Sgshapiro errno = 0; 89938032Speter ret = res_querydomain(host, *dp, C_IN, qtype, 90038032Speter answer.qb2, sizeof(answer.qb2)); 90138032Speter if (ret <= 0) 90238032Speter { 90390792Sgshapiro int save_errno = errno; 90490792Sgshapiro 90538032Speter if (tTd(8, 7)) 90690792Sgshapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 90790792Sgshapiro save_errno, h_errno); 90838032Speter 90990792Sgshapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 91038032Speter { 91171345Sgshapiro /* 91290792Sgshapiro ** the name server seems to be down or broken. 91371345Sgshapiro */ 91471345Sgshapiro 91573188Sgshapiro SM_SET_H_ERRNO(TRY_AGAIN); 91694334Sgshapiro if (**dp == '\0') 91794334Sgshapiro { 91894334Sgshapiro if (*statp == EX_OK) 91994334Sgshapiro *statp = EX_TEMPFAIL; 92094334Sgshapiro goto nexttype; 92194334Sgshapiro } 92238032Speter *statp = EX_TEMPFAIL; 92364562Sgshapiro 92473188Sgshapiro if (WorkAroundBrokenAAAA) 92573188Sgshapiro { 92673188Sgshapiro /* 92773188Sgshapiro ** Only return if not TRY_AGAIN as an 92873188Sgshapiro ** attempt with a different qtype may 92973188Sgshapiro ** succeed (res_querydomain() calls 93073188Sgshapiro ** res_query() calls res_send() which 93173188Sgshapiro ** sets errno to ETIMEDOUT if the 93273188Sgshapiro ** nameservers could be contacted but 93373188Sgshapiro ** didn't give an answer). 93473188Sgshapiro */ 93571345Sgshapiro 93690792Sgshapiro if (save_errno != ETIMEDOUT) 93790792Sgshapiro return false; 93873188Sgshapiro } 93990792Sgshapiro else 94090792Sgshapiro return false; 94138032Speter } 94238032Speter 94394334Sgshapironexttype: 94438032Speter if (h_errno != HOST_NOT_FOUND) 94538032Speter { 94638032Speter /* might have another type of interest */ 94764562Sgshapiro# if NETINET6 94890792Sgshapiro if (qtype == T_AAAA) 94938032Speter { 95064562Sgshapiro qtype = T_A; 95164562Sgshapiro continue; 95264562Sgshapiro } 95390792Sgshapiro else 95464562Sgshapiro# endif /* NETINET6 */ 95590792Sgshapiro if (qtype == T_A && !gotmx && 95690792Sgshapiro (trymx || **dp == '\0')) 95764562Sgshapiro { 95838032Speter qtype = T_MX; 95938032Speter continue; 96038032Speter } 96138032Speter } 96238032Speter 96338032Speter /* definite no -- try the next domain */ 96438032Speter dp++; 965120256Sgshapiro qtype = initial; 96638032Speter continue; 96738032Speter } 96838032Speter else if (tTd(8, 7)) 96990792Sgshapiro sm_dprintf("\tYES\n"); 97038032Speter 97138032Speter /* avoid problems after truncation in tcp packets */ 97238032Speter if (ret > sizeof(answer)) 97338032Speter ret = sizeof(answer); 97490792Sgshapiro if (ret < 0) 97590792Sgshapiro { 97690792Sgshapiro *statp = EX_SOFTWARE; 97790792Sgshapiro return false; 97890792Sgshapiro } 97938032Speter 98038032Speter /* 98138032Speter ** Appear to have a match. Confirm it by searching for A or 98238032Speter ** CNAME records. If we don't have a local domain 98338032Speter ** wild card MX record, we will accept MX as well. 98438032Speter */ 98538032Speter 98638032Speter hp = (HEADER *) &answer; 98790792Sgshapiro ap = (unsigned char *) &answer + HFIXEDSZ; 98890792Sgshapiro eom = (unsigned char *) &answer + ret; 98938032Speter 99038032Speter /* skip question part of response -- we know what we asked */ 99190792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 99264562Sgshapiro qdcount--; 99364562Sgshapiro ap += ret + QFIXEDSZ) 99438032Speter { 99538032Speter if ((ret = dn_skipname(ap, eom)) < 0) 99638032Speter { 99738032Speter if (tTd(8, 20)) 99890792Sgshapiro sm_dprintf("qdcount failure (%d)\n", 99990792Sgshapiro ntohs((unsigned short) hp->qdcount)); 100038032Speter *statp = EX_SOFTWARE; 100190792Sgshapiro return false; /* ???XXX??? */ 100238032Speter } 100338032Speter } 100438032Speter 100590792Sgshapiro amatch = false; 100690792Sgshapiro for (ancount = ntohs((unsigned short) hp->ancount); 100764562Sgshapiro --ancount >= 0 && ap < eom; 100864562Sgshapiro ap += n) 100938032Speter { 101090792Sgshapiro n = dn_expand((unsigned char *) &answer, eom, ap, 101138032Speter (RES_UNC_T) nbuf, sizeof nbuf); 101238032Speter if (n < 0) 101338032Speter break; 101438032Speter ap += n; 101538032Speter GETSHORT(type, ap); 101690792Sgshapiro ap += INT16SZ; /* skip over class */ 101790792Sgshapiro GETLONG(ttl, ap); 101890792Sgshapiro GETSHORT(n, ap); /* rdlength */ 101938032Speter switch (type) 102038032Speter { 102138032Speter case T_MX: 102290792Sgshapiro gotmx = true; 102338032Speter if (**dp != '\0' && HasWildcardMX) 102438032Speter { 102538032Speter /* 102638032Speter ** If we are using MX matches and have 102738032Speter ** not yet gotten one, save this one 102864562Sgshapiro ** but keep searching for an A or 102938032Speter ** CNAME match. 103038032Speter */ 103138032Speter 103238032Speter if (trymx && mxmatch == NULL) 103338032Speter mxmatch = *dp; 103438032Speter continue; 103538032Speter } 103638032Speter 103738032Speter /* 103838032Speter ** If we did not append a domain name, this 103938032Speter ** must have been a canonical name to start 104038032Speter ** with. Even if we did append a domain name, 104138032Speter ** in the absence of a wildcard MX this must 104238032Speter ** still be a real MX match. 104338032Speter ** Such MX matches are as good as an A match, 104438032Speter ** fall through. 104538032Speter */ 104664562Sgshapiro /* FALLTHROUGH */ 104738032Speter 104864562Sgshapiro# if NETINET6 104964562Sgshapiro case T_AAAA: 105064562Sgshapiro# endif /* NETINET6 */ 105138032Speter case T_A: 105238032Speter /* Flag that a good match was found */ 105390792Sgshapiro amatch = true; 105438032Speter 105538032Speter /* continue in case a CNAME also exists */ 105638032Speter continue; 105738032Speter 105838032Speter case T_CNAME: 105938032Speter if (DontExpandCnames) 106038032Speter { 106138032Speter /* got CNAME -- guaranteed canonical */ 106290792Sgshapiro amatch = true; 106338032Speter break; 106438032Speter } 106538032Speter 106638032Speter if (loopcnt++ > MAXCNAMEDEPTH) 106738032Speter { 106838032Speter /*XXX should notify postmaster XXX*/ 106938032Speter message("DNS failure: CNAME loop for %s", 107038032Speter host); 107138032Speter if (CurEnv->e_message == NULL) 107238032Speter { 107338032Speter char ebuf[MAXLINE]; 107438032Speter 107590792Sgshapiro (void) sm_snprintf(ebuf, 107690792Sgshapiro sizeof ebuf, 107738032Speter "Deferred: DNS failure: CNAME loop for %.100s", 107838032Speter host); 107990792Sgshapiro CurEnv->e_message = 108090792Sgshapiro sm_rpool_strdup_x( 108190792Sgshapiro CurEnv->e_rpool, ebuf); 108238032Speter } 108373188Sgshapiro SM_SET_H_ERRNO(NO_RECOVERY); 108438032Speter *statp = EX_CONFIG; 108590792Sgshapiro return false; 108638032Speter } 108738032Speter 108838032Speter /* value points at name */ 108990792Sgshapiro if ((ret = dn_expand((unsigned char *)&answer, 109090792Sgshapiro eom, ap, (RES_UNC_T) nbuf, 109190792Sgshapiro sizeof(nbuf))) < 0) 109238032Speter break; 109390792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 109438032Speter 109538032Speter /* 109638032Speter ** RFC 1034 section 3.6 specifies that CNAME 109738032Speter ** should point at the canonical name -- but 109838032Speter ** urges software to try again anyway. 109938032Speter */ 110038032Speter 110138032Speter goto cnameloop; 110238032Speter 110338032Speter default: 110438032Speter /* not a record of interest */ 110538032Speter continue; 110638032Speter } 110738032Speter } 110838032Speter 110938032Speter if (amatch) 111038032Speter { 111164562Sgshapiro /* 111238032Speter ** Got a good match -- either an A, CNAME, or an 111338032Speter ** exact MX record. Save it and get out of here. 111438032Speter */ 111538032Speter 111638032Speter mxmatch = *dp; 111738032Speter break; 111838032Speter } 111938032Speter 112038032Speter /* 112138032Speter ** Nothing definitive yet. 112238032Speter ** If this was a T_A query and we haven't yet found a MX 112338032Speter ** match, try T_MX if allowed to do so. 112438032Speter ** Otherwise, try the next domain. 112538032Speter */ 112638032Speter 112764562Sgshapiro# if NETINET6 112890792Sgshapiro if (qtype == T_AAAA) 112938032Speter qtype = T_A; 113090792Sgshapiro else 113164562Sgshapiro# endif /* NETINET6 */ 113290792Sgshapiro if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 113338032Speter qtype = T_MX; 113438032Speter else 113538032Speter { 1136120256Sgshapiro qtype = initial; 113738032Speter dp++; 113838032Speter } 113938032Speter } 114038032Speter 114138032Speter /* if nothing was found, we are done */ 114238032Speter if (mxmatch == NULL) 114338032Speter { 114471345Sgshapiro if (*statp == EX_OK) 114571345Sgshapiro *statp = EX_NOHOST; 114690792Sgshapiro return false; 114738032Speter } 114838032Speter 114938032Speter /* 115038032Speter ** Create canonical name and return. 115138032Speter ** If saved domain name is null, name was already canonical. 115238032Speter ** Otherwise append the saved domain name. 115338032Speter */ 115438032Speter 115590792Sgshapiro (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, 115690792Sgshapiro *mxmatch == '\0' ? "" : ".", 115790792Sgshapiro MAXDNAME, mxmatch); 115890792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 115938032Speter if (tTd(8, 5)) 116090792Sgshapiro sm_dprintf("dns_getcanonname: %s\n", host); 116138032Speter *statp = EX_OK; 116290792Sgshapiro 116390792Sgshapiro /* return only one TTL entry, that should be sufficient */ 116490792Sgshapiro if (ttl > 0 && pttl != NULL) 116590792Sgshapiro *pttl = ttl; 116690792Sgshapiro return true; 116738032Speter} 116838032Speter#endif /* NAMED_BIND */ 1169