138032Speter/* 2223067Sgshapiro * Copyright (c) 1998-2004, 2006, 2010 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> 15168515Sgshapiro#include "map.h" 1638032Speter 1790792Sgshapiro#if NAMED_BIND 18223067SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.204 2010/06/29 15:35:33 ca Exp $ (with name server)") 1990792Sgshapiro#else /* NAMED_BIND */ 20223067SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.204 2010/06/29 15:35:33 ca Exp $ (without name server)") 2190792Sgshapiro#endif /* NAMED_BIND */ 2238032Speter 2338032Speter#if NAMED_BIND 2438032Speter 2564562Sgshapiro# include <arpa/inet.h> 2638032Speter 2790792Sgshapiro 2864562Sgshapiro# ifndef MXHOSTBUFSIZE 2964562Sgshapiro# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 3064562Sgshapiro# endif /* ! MXHOSTBUFSIZE */ 3138032Speter 3238032Speterstatic char MXHostBuf[MXHOSTBUFSIZE]; 3390792Sgshapiro#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 3490792Sgshapiro ERROR: _MXHOSTBUFSIZE is out of range 3590792Sgshapiro#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 3638032Speter 3764562Sgshapiro# ifndef MAXDNSRCH 3864562Sgshapiro# define MAXDNSRCH 6 /* number of possible domains to search */ 3964562Sgshapiro# endif /* ! MAXDNSRCH */ 4038032Speter 4164562Sgshapiro# ifndef RES_DNSRCH_VARIABLE 4264562Sgshapiro# define RES_DNSRCH_VARIABLE _res.dnsrch 4364562Sgshapiro# endif /* ! RES_DNSRCH_VARIABLE */ 4438032Speter 4564562Sgshapiro# ifndef NO_DATA 4664562Sgshapiro# define NO_DATA NO_ADDRESS 4764562Sgshapiro# endif /* ! NO_DATA */ 4838032Speter 4964562Sgshapiro# ifndef HFIXEDSZ 5064562Sgshapiro# define HFIXEDSZ 12 /* sizeof(HEADER) */ 5164562Sgshapiro# endif /* ! HFIXEDSZ */ 5238032Speter 5364562Sgshapiro# define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 5464562Sgshapiro 5564562Sgshapiro# if defined(__RES) && (__RES >= 19940415) 5664562Sgshapiro# define RES_UNC_T char * 5764562Sgshapiro# else /* defined(__RES) && (__RES >= 19940415) */ 5890792Sgshapiro# define RES_UNC_T unsigned char * 5964562Sgshapiro# endif /* defined(__RES) && (__RES >= 19940415) */ 6064562Sgshapiro 6164562Sgshapirostatic int mxrand __P((char *)); 6290792Sgshapirostatic int fallbackmxrr __P((int, unsigned short *, char **)); 6364562Sgshapiro 6490792Sgshapiro/* 6590792Sgshapiro** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 6690792Sgshapiro** 6790792Sgshapiro** We have to initialize this once before doing anything else. 6890792Sgshapiro** Moreover, we have to repeat this from time to time to avoid 6990792Sgshapiro** stale data, e.g., in persistent queue runners. 7090792Sgshapiro** This should be done in a parent process so the child 7190792Sgshapiro** processes have the right data. 7290792Sgshapiro** 7390792Sgshapiro** Parameters: 7490792Sgshapiro** host -- the name of the fallback MX host. 7590792Sgshapiro** 7690792Sgshapiro** Returns: 7790792Sgshapiro** number of MX records. 7890792Sgshapiro** 7990792Sgshapiro** Side Effects: 80132943Sgshapiro** Populates NumFallbackMXHosts and fbhosts. 8190792Sgshapiro** Sets renewal time (based on TTL). 8290792Sgshapiro*/ 8390792Sgshapiro 84132943Sgshapiroint NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 8590792Sgshapirostatic char *fbhosts[MAXMXHOSTS + 1]; 8690792Sgshapiro 8790792Sgshapiroint 8890792Sgshapirogetfallbackmxrr(host) 8990792Sgshapiro char *host; 9090792Sgshapiro{ 9190792Sgshapiro int i, rcode; 9290792Sgshapiro int ttl; 9390792Sgshapiro static time_t renew = 0; 9490792Sgshapiro 9590792Sgshapiro#if 0 9690792Sgshapiro /* This is currently done before this function is called. */ 9790792Sgshapiro if (host == NULL || *host == '\0') 9890792Sgshapiro return 0; 9990792Sgshapiro#endif /* 0 */ 100132943Sgshapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 101132943Sgshapiro return NumFallbackMXHosts; 10290792Sgshapiro if (host[0] == '[') 10390792Sgshapiro { 10490792Sgshapiro fbhosts[0] = host; 105132943Sgshapiro NumFallbackMXHosts = 1; 10690792Sgshapiro } 10790792Sgshapiro else 10890792Sgshapiro { 10990792Sgshapiro /* free old data */ 110132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 11190792Sgshapiro sm_free(fbhosts[i]); 11290792Sgshapiro 11390792Sgshapiro /* get new data */ 114132943Sgshapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false, 11590792Sgshapiro &rcode, false, &ttl); 11690792Sgshapiro renew = curtime() + ttl; 117132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 11890792Sgshapiro fbhosts[i] = newstr(fbhosts[i]); 11990792Sgshapiro } 120132943Sgshapiro return NumFallbackMXHosts; 12190792Sgshapiro} 12290792Sgshapiro 12390792Sgshapiro/* 12490792Sgshapiro** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 12590792Sgshapiro** 12690792Sgshapiro** Parameters: 12790792Sgshapiro** nmx -- current number of MX records. 12890792Sgshapiro** prefs -- array of preferences. 12990792Sgshapiro** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 13090792Sgshapiro** 13190792Sgshapiro** Returns: 13290792Sgshapiro** new number of MX records. 13390792Sgshapiro** 13490792Sgshapiro** Side Effects: 135132943Sgshapiro** If FallbackMX was set, it appends the MX records for 13690792Sgshapiro** that host to mxhosts (and modifies prefs accordingly). 13790792Sgshapiro*/ 13890792Sgshapiro 13990792Sgshapirostatic int 14090792Sgshapirofallbackmxrr(nmx, prefs, mxhosts) 14190792Sgshapiro int nmx; 14290792Sgshapiro unsigned short *prefs; 14390792Sgshapiro char **mxhosts; 14490792Sgshapiro{ 14590792Sgshapiro int i; 14690792Sgshapiro 147132943Sgshapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 14890792Sgshapiro { 14990792Sgshapiro if (nmx > 0) 15090792Sgshapiro prefs[nmx] = prefs[nmx - 1] + 1; 15190792Sgshapiro else 15290792Sgshapiro prefs[nmx] = 0; 15390792Sgshapiro mxhosts[nmx++] = fbhosts[i]; 15490792Sgshapiro } 15590792Sgshapiro return nmx; 15690792Sgshapiro} 15790792Sgshapiro 15890792Sgshapiro/* 15938032Speter** GETMXRR -- get MX resource records for a domain 16038032Speter** 16138032Speter** Parameters: 16238032Speter** host -- the name of the host to MX. 16338032Speter** mxhosts -- a pointer to a return buffer of MX records. 16464562Sgshapiro** mxprefs -- a pointer to a return buffer of MX preferences. 16564562Sgshapiro** If NULL, don't try to populate. 16690792Sgshapiro** droplocalhost -- If true, all MX records less preferred 16738032Speter** than the local host (as determined by $=w) will 16838032Speter** be discarded. 16938032Speter** rcode -- a pointer to an EX_ status code. 17090792Sgshapiro** tryfallback -- add also fallback MX host? 17190792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 17238032Speter** 17338032Speter** Returns: 17438032Speter** The number of MX records found. 17538032Speter** -1 if there is an internal failure. 17638032Speter** If no MX records are found, mxhosts[0] is set to host 17738032Speter** and 1 is returned. 17890792Sgshapiro** 17990792Sgshapiro** Side Effects: 18090792Sgshapiro** The entries made for mxhosts point to a static array 18190792Sgshapiro** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 18290792Sgshapiro** if it must be preserved across calls to this function. 18338032Speter*/ 18438032Speter 18538032Speterint 18690792Sgshapirogetmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) 18738032Speter char *host; 18838032Speter char **mxhosts; 18990792Sgshapiro unsigned short *mxprefs; 19038032Speter bool droplocalhost; 19138032Speter int *rcode; 19290792Sgshapiro bool tryfallback; 19390792Sgshapiro int *pttl; 19438032Speter{ 19590792Sgshapiro register unsigned char *eom, *cp; 19638032Speter register int i, j, n; 19738032Speter int nmx = 0; 19838032Speter register char *bp; 19938032Speter HEADER *hp; 20038032Speter querybuf answer; 20138032Speter int ancount, qdcount, buflen; 20290792Sgshapiro bool seenlocal = false; 20390792Sgshapiro unsigned short pref, type; 20490792Sgshapiro unsigned short localpref = 256; 205132943Sgshapiro char *fallbackMX = FallbackMX; 20690792Sgshapiro bool trycanon = false; 20790792Sgshapiro unsigned short *prefs; 208141858Sgshapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 20990792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 21038032Speter int weight[MAXMXHOSTS]; 21190792Sgshapiro int ttl = 0; 21264562Sgshapiro extern int res_query(), res_search(); 21338032Speter 21438032Speter if (tTd(8, 2)) 21590792Sgshapiro sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", 21690792Sgshapiro host, droplocalhost); 217147078Sgshapiro *rcode = EX_OK; 218147078Sgshapiro if (pttl != NULL) 219147078Sgshapiro *pttl = SM_DEFAULT_TTL; 220120256Sgshapiro if (*host == '\0') 221120256Sgshapiro return 0; 22238032Speter 22390792Sgshapiro if ((fallbackMX != NULL && droplocalhost && 22490792Sgshapiro wordinclass(fallbackMX, 'w')) || !tryfallback) 22538032Speter { 22638032Speter /* don't use fallback for this pass */ 22738032Speter fallbackMX = NULL; 22838032Speter } 22938032Speter 23064562Sgshapiro if (mxprefs != NULL) 23164562Sgshapiro prefs = mxprefs; 23264562Sgshapiro else 23364562Sgshapiro prefs = prefer; 23464562Sgshapiro 23538032Speter /* efficiency hack -- numeric or non-MX lookups */ 23638032Speter if (host[0] == '[') 23738032Speter goto punt; 23838032Speter 23938032Speter /* 24038032Speter ** If we don't have MX records in our host switch, don't 24138032Speter ** try for MX records. Note that this really isn't "right", 24238032Speter ** since we might be set up to try NIS first and then DNS; 24338032Speter ** if the host is found in NIS we really shouldn't be doing 24438032Speter ** MX lookups. However, that should be a degenerate case. 24538032Speter */ 24638032Speter 24738032Speter if (!UseNameServer) 24838032Speter goto punt; 24938032Speter if (HasWildcardMX && ConfigLevel >= 6) 25038032Speter resfunc = res_query; 25138032Speter else 25238032Speter resfunc = res_search; 25338032Speter 25438032Speter errno = 0; 25590792Sgshapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 25690792Sgshapiro sizeof(answer)); 25738032Speter if (n < 0) 25838032Speter { 25938032Speter if (tTd(8, 1)) 26090792Sgshapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 261168515Sgshapiro host, errno, h_errno); 26238032Speter switch (h_errno) 26338032Speter { 26438032Speter case NO_DATA: 26590792Sgshapiro trycanon = true; 26664562Sgshapiro /* FALLTHROUGH */ 26738032Speter 26838032Speter case NO_RECOVERY: 26938032Speter /* no MX data on this host */ 27038032Speter goto punt; 27138032Speter 27238032Speter case HOST_NOT_FOUND: 27364562Sgshapiro# if BROKEN_RES_SEARCH 27438032Speter case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 27564562Sgshapiro# endif /* BROKEN_RES_SEARCH */ 27638032Speter /* host doesn't exist in DNS; might be in /etc/hosts */ 27790792Sgshapiro trycanon = true; 27838032Speter *rcode = EX_NOHOST; 27938032Speter goto punt; 28038032Speter 28138032Speter case TRY_AGAIN: 28238032Speter case -1: 28338032Speter /* couldn't connect to the name server */ 28438032Speter if (fallbackMX != NULL) 28538032Speter { 28638032Speter /* name server is hosed -- push to fallback */ 28790792Sgshapiro return fallbackmxrr(nmx, prefs, mxhosts); 28838032Speter } 28938032Speter /* it might come up later; better queue it up */ 29038032Speter *rcode = EX_TEMPFAIL; 29138032Speter break; 29238032Speter 29338032Speter default: 29490792Sgshapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 29538032Speter host, h_errno); 29638032Speter *rcode = EX_OSERR; 29738032Speter break; 29838032Speter } 29938032Speter 30038032Speter /* irreconcilable differences */ 30164562Sgshapiro return -1; 30238032Speter } 30338032Speter 30438032Speter /* avoid problems after truncation in tcp packets */ 30538032Speter if (n > sizeof(answer)) 30638032Speter n = sizeof(answer); 30738032Speter 30838032Speter /* find first satisfactory answer */ 30938032Speter hp = (HEADER *)&answer; 31090792Sgshapiro cp = (unsigned char *)&answer + HFIXEDSZ; 31190792Sgshapiro eom = (unsigned char *)&answer + n; 31290792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 31364562Sgshapiro qdcount--; 31464562Sgshapiro cp += n + QFIXEDSZ) 31564562Sgshapiro { 31638032Speter if ((n = dn_skipname(cp, eom)) < 0) 31738032Speter goto punt; 31864562Sgshapiro } 31990792Sgshapiro 32090792Sgshapiro /* NOTE: see definition of MXHostBuf! */ 32138032Speter buflen = sizeof(MXHostBuf) - 1; 32290792Sgshapiro SM_ASSERT(buflen > 0); 32338032Speter bp = MXHostBuf; 32490792Sgshapiro ancount = ntohs((unsigned short) hp->ancount); 32590792Sgshapiro 32690792Sgshapiro /* See RFC 1035 for layout of RRs. */ 327132943Sgshapiro /* XXX leave room for FallbackMX ? */ 32838032Speter while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 32938032Speter { 33090792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 33190792Sgshapiro (RES_UNC_T) bp, buflen)) < 0) 33238032Speter break; 33338032Speter cp += n; 33438032Speter GETSHORT(type, cp); 33590792Sgshapiro cp += INT16SZ; /* skip over class */ 33690792Sgshapiro GETLONG(ttl, cp); 33790792Sgshapiro GETSHORT(n, cp); /* rdlength */ 33838032Speter if (type != T_MX) 33938032Speter { 34038032Speter if (tTd(8, 8) || _res.options & RES_DEBUG) 34190792Sgshapiro sm_dprintf("unexpected answer type %d, size %d\n", 34264562Sgshapiro type, n); 34338032Speter cp += n; 34438032Speter continue; 34538032Speter } 34638032Speter GETSHORT(pref, cp); 34790792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 34838032Speter (RES_UNC_T) bp, buflen)) < 0) 34938032Speter break; 35038032Speter cp += n; 35190792Sgshapiro n = strlen(bp); 35290792Sgshapiro# if 0 35390792Sgshapiro /* Can this happen? */ 35490792Sgshapiro if (n == 0) 35590792Sgshapiro { 35690792Sgshapiro if (LogLevel > 4) 35790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 35890792Sgshapiro "MX records for %s contain empty string", 35990792Sgshapiro host); 36090792Sgshapiro continue; 36190792Sgshapiro } 36290792Sgshapiro# endif /* 0 */ 36338032Speter if (wordinclass(bp, 'w')) 36438032Speter { 36538032Speter if (tTd(8, 3)) 36690792Sgshapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 36738032Speter bp, pref); 36838032Speter if (droplocalhost) 36938032Speter { 37038032Speter if (!seenlocal || pref < localpref) 37138032Speter localpref = pref; 37290792Sgshapiro seenlocal = true; 37338032Speter continue; 37438032Speter } 37538032Speter weight[nmx] = 0; 37638032Speter } 37738032Speter else 37838032Speter weight[nmx] = mxrand(bp); 37964562Sgshapiro prefs[nmx] = pref; 38038032Speter mxhosts[nmx++] = bp; 38138032Speter bp += n; 38238032Speter if (bp[-1] != '.') 38338032Speter { 38438032Speter *bp++ = '.'; 38538032Speter n++; 38638032Speter } 38738032Speter *bp++ = '\0'; 38890792Sgshapiro if (buflen < n + 1) 38990792Sgshapiro { 39090792Sgshapiro /* don't want to wrap buflen */ 39190792Sgshapiro break; 39290792Sgshapiro } 39338032Speter buflen -= n + 1; 39438032Speter } 39538032Speter 39690792Sgshapiro /* return only one TTL entry, that should be sufficient */ 39790792Sgshapiro if (ttl > 0 && pttl != NULL) 39890792Sgshapiro *pttl = ttl; 39990792Sgshapiro 40038032Speter /* sort the records */ 40138032Speter for (i = 0; i < nmx; i++) 40238032Speter { 40338032Speter for (j = i + 1; j < nmx; j++) 40438032Speter { 40564562Sgshapiro if (prefs[i] > prefs[j] || 40664562Sgshapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 40738032Speter { 40838032Speter register int temp; 40938032Speter register char *temp1; 41038032Speter 41164562Sgshapiro temp = prefs[i]; 41264562Sgshapiro prefs[i] = prefs[j]; 41364562Sgshapiro prefs[j] = temp; 41438032Speter temp1 = mxhosts[i]; 41538032Speter mxhosts[i] = mxhosts[j]; 41638032Speter mxhosts[j] = temp1; 41738032Speter temp = weight[i]; 41838032Speter weight[i] = weight[j]; 41938032Speter weight[j] = temp; 42038032Speter } 42138032Speter } 42264562Sgshapiro if (seenlocal && prefs[i] >= localpref) 42338032Speter { 42438032Speter /* truncate higher preference part of list */ 42538032Speter nmx = i; 42638032Speter } 42738032Speter } 42838032Speter 42938032Speter /* delete duplicates from list (yes, some bozos have duplicates) */ 43038032Speter for (i = 0; i < nmx - 1; ) 43138032Speter { 43290792Sgshapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 43338032Speter i++; 43438032Speter else 43538032Speter { 43638032Speter /* compress out duplicate */ 43738032Speter for (j = i + 1; j < nmx; j++) 43864562Sgshapiro { 43938032Speter mxhosts[j] = mxhosts[j + 1]; 44064562Sgshapiro prefs[j] = prefs[j + 1]; 44164562Sgshapiro } 44238032Speter nmx--; 44338032Speter } 44438032Speter } 44538032Speter 44638032Speter if (nmx == 0) 44738032Speter { 44838032Speterpunt: 44964562Sgshapiro if (seenlocal) 45038032Speter { 45164562Sgshapiro struct hostent *h = NULL; 45264562Sgshapiro 45338032Speter /* 45438032Speter ** If we have deleted all MX entries, this is 45538032Speter ** an error -- we should NEVER send to a host that 45638032Speter ** has an MX, and this should have been caught 45738032Speter ** earlier in the config file. 45838032Speter ** 45938032Speter ** Some sites prefer to go ahead and try the 46038032Speter ** A record anyway; that case is handled by 46138032Speter ** setting TryNullMXList. I believe this is a 46238032Speter ** bad idea, but it's up to you.... 46338032Speter */ 46438032Speter 46564562Sgshapiro if (TryNullMXList) 46664562Sgshapiro { 46773188Sgshapiro SM_SET_H_ERRNO(0); 46864562Sgshapiro errno = 0; 46964562Sgshapiro h = sm_gethostbyname(host, AF_INET); 47064562Sgshapiro if (h == NULL) 47164562Sgshapiro { 47264562Sgshapiro if (errno == ETIMEDOUT || 47364562Sgshapiro h_errno == TRY_AGAIN || 47464562Sgshapiro (errno == ECONNREFUSED && 47564562Sgshapiro UseNameServer)) 47664562Sgshapiro { 47764562Sgshapiro *rcode = EX_TEMPFAIL; 47864562Sgshapiro return -1; 47964562Sgshapiro } 48064562Sgshapiro# if NETINET6 48173188Sgshapiro SM_SET_H_ERRNO(0); 48264562Sgshapiro errno = 0; 48364562Sgshapiro h = sm_gethostbyname(host, AF_INET6); 48464562Sgshapiro if (h == NULL && 48564562Sgshapiro (errno == ETIMEDOUT || 48664562Sgshapiro h_errno == TRY_AGAIN || 48764562Sgshapiro (errno == ECONNREFUSED && 48864562Sgshapiro UseNameServer))) 48964562Sgshapiro { 49064562Sgshapiro *rcode = EX_TEMPFAIL; 49164562Sgshapiro return -1; 49264562Sgshapiro } 49364562Sgshapiro# endif /* NETINET6 */ 49464562Sgshapiro } 49564562Sgshapiro } 49664562Sgshapiro 49764562Sgshapiro if (h == NULL) 49864562Sgshapiro { 49964562Sgshapiro *rcode = EX_CONFIG; 50064562Sgshapiro syserr("MX list for %s points back to %s", 50164562Sgshapiro host, MyHostName); 50264562Sgshapiro return -1; 50364562Sgshapiro } 50490792Sgshapiro# if NETINET6 50571345Sgshapiro freehostent(h); 506159609Sgshapiro h = NULL; 50790792Sgshapiro# endif /* NETINET6 */ 50838032Speter } 509168515Sgshapiro if (strlen(host) >= sizeof(MXHostBuf)) 51038032Speter { 51138032Speter *rcode = EX_CONFIG; 51238032Speter syserr("Host name %s too long", 51338032Speter shortenstring(host, MAXSHORTSTR)); 51438032Speter return -1; 51538032Speter } 516168515Sgshapiro (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf)); 51738032Speter mxhosts[0] = MXHostBuf; 51864562Sgshapiro prefs[0] = 0; 51938032Speter if (host[0] == '[') 52038032Speter { 52138032Speter register char *p; 52264562Sgshapiro# if NETINET6 52364562Sgshapiro struct sockaddr_in6 tmp6; 52464562Sgshapiro# endif /* NETINET6 */ 52538032Speter 52638032Speter /* this may be an MX suppression-style address */ 52738032Speter p = strchr(MXHostBuf, ']'); 52838032Speter if (p != NULL) 52938032Speter { 53038032Speter *p = '\0'; 53164562Sgshapiro 53238032Speter if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 53338032Speter { 53438032Speter nmx++; 53538032Speter *p = ']'; 53638032Speter } 53764562Sgshapiro# if NETINET6 53890792Sgshapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 53990792Sgshapiro &tmp6.sin6_addr) == 1) 54064562Sgshapiro { 54164562Sgshapiro nmx++; 54264562Sgshapiro *p = ']'; 54364562Sgshapiro } 54464562Sgshapiro# endif /* NETINET6 */ 54538032Speter else 54638032Speter { 54790792Sgshapiro trycanon = true; 54838032Speter mxhosts[0]++; 54938032Speter } 55038032Speter } 55138032Speter } 55238032Speter if (trycanon && 553168515Sgshapiro getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, pttl)) 55438032Speter { 55590792Sgshapiro /* XXX MXHostBuf == "" ? is that possible? */ 55638032Speter bp = &MXHostBuf[strlen(MXHostBuf)]; 55738032Speter if (bp[-1] != '.') 55838032Speter { 55938032Speter *bp++ = '.'; 56038032Speter *bp = '\0'; 56138032Speter } 56238032Speter nmx = 1; 56338032Speter } 56438032Speter } 56538032Speter 56638032Speter /* if we have a default lowest preference, include that */ 56738032Speter if (fallbackMX != NULL && !seenlocal) 56864562Sgshapiro { 56990792Sgshapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 57064562Sgshapiro } 57164562Sgshapiro return nmx; 57238032Speter} 57390792Sgshapiro/* 57438032Speter** MXRAND -- create a randomizer for equal MX preferences 57538032Speter** 57638032Speter** If two MX hosts have equal preferences we want to randomize 57738032Speter** the selection. But in order for signatures to be the same, 57838032Speter** we need to randomize the same way each time. This function 57938032Speter** computes a pseudo-random hash function from the host name. 58038032Speter** 58138032Speter** Parameters: 58238032Speter** host -- the name of the host. 58338032Speter** 58438032Speter** Returns: 58538032Speter** A random but repeatable value based on the host name. 58638032Speter*/ 58738032Speter 58864562Sgshapirostatic int 58938032Spetermxrand(host) 59038032Speter register char *host; 59138032Speter{ 59238032Speter int hfunc; 59338032Speter static unsigned int seed; 59438032Speter 59538032Speter if (seed == 0) 59638032Speter { 59738032Speter seed = (int) curtime() & 0xffff; 59838032Speter if (seed == 0) 59938032Speter seed++; 60038032Speter } 60138032Speter 60238032Speter if (tTd(17, 9)) 60390792Sgshapiro sm_dprintf("mxrand(%s)", host); 60438032Speter 60538032Speter hfunc = seed; 60638032Speter while (*host != '\0') 60738032Speter { 60838032Speter int c = *host++; 60938032Speter 61038032Speter if (isascii(c) && isupper(c)) 61138032Speter c = tolower(c); 61238032Speter hfunc = ((hfunc << 1) ^ c) % 2003; 61338032Speter } 61438032Speter 61538032Speter hfunc &= 0xff; 61638032Speter hfunc++; 61738032Speter 61838032Speter if (tTd(17, 9)) 61990792Sgshapiro sm_dprintf(" = %d\n", hfunc); 62038032Speter return hfunc; 62138032Speter} 62290792Sgshapiro/* 62338032Speter** BESTMX -- find the best MX for a name 62438032Speter** 62538032Speter** This is really a hack, but I don't see any obvious way 62638032Speter** to generalize it at the moment. 62738032Speter*/ 62838032Speter 62938032Speter/* ARGSUSED3 */ 63038032Speterchar * 63138032Speterbestmx_map_lookup(map, name, av, statp) 63238032Speter MAP *map; 63338032Speter char *name; 63438032Speter char **av; 63538032Speter int *statp; 63638032Speter{ 63738032Speter int nmx; 63838032Speter int saveopts = _res.options; 63990792Sgshapiro int i; 64090792Sgshapiro ssize_t len = 0; 64190792Sgshapiro char *result; 64290792Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 64390792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 64490792Sgshapiro char *buf; 64590792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */ 64638032Speter char *p; 64742575Speter char buf[PSBUFSIZE / 2]; 64890792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 64938032Speter 65038032Speter _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 651102528Sgshapiro nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); 65238032Speter _res.options = saveopts; 65338032Speter if (nmx <= 0) 65438032Speter return NULL; 65538032Speter if (bitset(MF_MATCHONLY, map->map_mflags)) 65638032Speter return map_rewrite(map, name, strlen(name), NULL); 65738032Speter if ((map->map_coldelim == '\0') || (nmx == 1)) 65838032Speter return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 65938032Speter 66038032Speter /* 66142575Speter ** We were given a -z flag (return all MXs) and there are multiple 66242575Speter ** ones. We need to build them all into a list. 66338032Speter */ 66490792Sgshapiro 66590792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 66690792Sgshapiro for (i = 0; i < nmx; i++) 66790792Sgshapiro { 66890792Sgshapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 66990792Sgshapiro { 67090792Sgshapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 67190792Sgshapiro mxhosts[i], map->map_coldelim); 67290792Sgshapiro return NULL; 67390792Sgshapiro } 67490792Sgshapiro len += strlen(mxhosts[i]) + 1; 67590792Sgshapiro if (len < 0) 67690792Sgshapiro { 67790792Sgshapiro len -= strlen(mxhosts[i]) + 1; 67890792Sgshapiro break; 67990792Sgshapiro } 68090792Sgshapiro } 68190792Sgshapiro buf = (char *) sm_malloc(len); 68290792Sgshapiro if (buf == NULL) 68390792Sgshapiro { 68490792Sgshapiro *statp = EX_UNAVAILABLE; 68590792Sgshapiro return NULL; 68690792Sgshapiro } 68790792Sgshapiro *buf = '\0'; 68890792Sgshapiro for (i = 0; i < nmx; i++) 68990792Sgshapiro { 69090792Sgshapiro int end; 69190792Sgshapiro 69290792Sgshapiro end = sm_strlcat(buf, mxhosts[i], len); 69390792Sgshapiro if (i != nmx && end + 1 < len) 69490792Sgshapiro { 69590792Sgshapiro buf[end] = map->map_coldelim; 69690792Sgshapiro buf[end + 1] = '\0'; 69790792Sgshapiro } 69890792Sgshapiro } 69990792Sgshapiro 70090792Sgshapiro /* Cleanly truncate for rulesets */ 70190792Sgshapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 70290792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */ 70338032Speter p = buf; 70438032Speter for (i = 0; i < nmx; i++) 70538032Speter { 70690792Sgshapiro size_t slen; 70764562Sgshapiro 70838032Speter if (strchr(mxhosts[i], map->map_coldelim) != NULL) 70938032Speter { 71038032Speter syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 71138032Speter mxhosts[i], map->map_coldelim); 71238032Speter return NULL; 71338032Speter } 71438032Speter slen = strlen(mxhosts[i]); 715168515Sgshapiro if (len + slen + 2 > sizeof(buf)) 71638032Speter break; 71738032Speter if (i > 0) 71838032Speter { 71938032Speter *p++ = map->map_coldelim; 72038032Speter len++; 72138032Speter } 722168515Sgshapiro (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len); 72338032Speter p += slen; 72438032Speter len += slen; 72538032Speter } 72690792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 72790792Sgshapiro 72890792Sgshapiro result = map_rewrite(map, buf, len, av); 72990792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION 73090792Sgshapiro sm_free(buf); 73190792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 73290792Sgshapiro return result; 73338032Speter} 73490792Sgshapiro/* 73538032Speter** DNS_GETCANONNAME -- get the canonical name for named host using DNS 73638032Speter** 73738032Speter** This algorithm tries to be smart about wildcard MX records. 73838032Speter** This is hard to do because DNS doesn't tell is if we matched 73938032Speter** against a wildcard or a specific MX. 74064562Sgshapiro** 74138032Speter** We always prefer A & CNAME records, since these are presumed 74238032Speter** to be specific. 74338032Speter** 74438032Speter** If we match an MX in one pass and lose it in the next, we use 74538032Speter** the old one. For example, consider an MX matching *.FOO.BAR.COM. 74638032Speter** A hostname bletch.foo.bar.com will match against this MX, but 74738032Speter** will stop matching when we try bletch.bar.com -- so we know 74838032Speter** that bletch.foo.bar.com must have been right. This fails if 74938032Speter** there was also an MX record matching *.BAR.COM, but there are 75038032Speter** some things that just can't be fixed. 75138032Speter** 75238032Speter** Parameters: 75338032Speter** host -- a buffer containing the name of the host. 75438032Speter** This is a value-result parameter. 75538032Speter** hbsize -- the size of the host buffer. 75638032Speter** trymx -- if set, try MX records as well as A and CNAME. 75738032Speter** statp -- pointer to place to store status. 75890792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 75938032Speter** 76038032Speter** Returns: 76190792Sgshapiro** true -- if the host matched. 76290792Sgshapiro** false -- otherwise. 76338032Speter*/ 76438032Speter 76538032Speterbool 76690792Sgshapirodns_getcanonname(host, hbsize, trymx, statp, pttl) 76738032Speter char *host; 76838032Speter int hbsize; 76938032Speter bool trymx; 77038032Speter int *statp; 77190792Sgshapiro int *pttl; 77238032Speter{ 77390792Sgshapiro register unsigned char *eom, *ap; 77438032Speter register char *cp; 77564562Sgshapiro register int n; 77638032Speter HEADER *hp; 77738032Speter querybuf answer; 77838032Speter int ancount, qdcount; 77938032Speter int ret; 78038032Speter char **domain; 78138032Speter int type; 78290792Sgshapiro int ttl = 0; 78338032Speter char **dp; 78438032Speter char *mxmatch; 78538032Speter bool amatch; 78690792Sgshapiro bool gotmx = false; 78738032Speter int qtype; 788120256Sgshapiro int initial; 78938032Speter int loopcnt; 79090792Sgshapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 79198121Sgshapiro char *searchlist[MAXDNSRCH + 2]; 79238032Speter 79338032Speter if (tTd(8, 2)) 79490792Sgshapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 79538032Speter 79638032Speter if ((_res.options & RES_INIT) == 0 && res_init() == -1) 79738032Speter { 79838032Speter *statp = EX_UNAVAILABLE; 79990792Sgshapiro return false; 80038032Speter } 80138032Speter 80271345Sgshapiro *statp = EX_OK; 80371345Sgshapiro 80438032Speter /* 80538032Speter ** Initialize domain search list. If there is at least one 80638032Speter ** dot in the name, search the unmodified name first so we 80738032Speter ** find "vse.CS" in Czechoslovakia instead of in the local 80864562Sgshapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 80964562Sgshapiro ** longer a country named Czechoslovakia but this type of problem 81064562Sgshapiro ** is still present. 81138032Speter ** 81238032Speter ** Older versions of the resolver could create this 81338032Speter ** list by tearing apart the host name. 81438032Speter */ 81538032Speter 81638032Speter loopcnt = 0; 81738032Spetercnameloop: 81838032Speter /* Check for dots in the name */ 81938032Speter for (cp = host, n = 0; *cp != '\0'; cp++) 82038032Speter if (*cp == '.') 82138032Speter n++; 82238032Speter 82338032Speter /* 82464562Sgshapiro ** Build the search list. 82538032Speter ** If there is at least one dot in name, start with a null 82638032Speter ** domain to search the unmodified name first. 82738032Speter ** If name does not end with a dot and search up local domain 82838032Speter ** tree desired, append each local domain component to the 82938032Speter ** search list; if name contains no dots and default domain 83038032Speter ** name is desired, append default domain name to search list; 83138032Speter ** else if name ends in a dot, remove that dot. 83238032Speter */ 83338032Speter 83438032Speter dp = searchlist; 83538032Speter if (n > 0) 83638032Speter *dp++ = ""; 83738032Speter if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 83838032Speter { 83964562Sgshapiro /* make sure there are less than MAXDNSRCH domains */ 84064562Sgshapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 84164562Sgshapiro *domain != NULL && ret < MAXDNSRCH; 84264562Sgshapiro ret++) 84338032Speter *dp++ = *domain++; 84438032Speter } 84538032Speter else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 84638032Speter { 84738032Speter *dp++ = _res.defdname; 84838032Speter } 84938032Speter else if (*cp == '.') 85038032Speter { 85138032Speter *cp = '\0'; 85238032Speter } 85338032Speter *dp = NULL; 85438032Speter 85538032Speter /* 85638032Speter ** Now loop through the search list, appending each domain in turn 85738032Speter ** name and searching for a match. 85838032Speter */ 85938032Speter 86038032Speter mxmatch = NULL; 861120256Sgshapiro initial = T_A; 862120256Sgshapiro# if NETINET6 863120256Sgshapiro if (InetMode == AF_INET6) 864120256Sgshapiro initial = T_AAAA; 865120256Sgshapiro# endif /* NETINET6 */ 866120256Sgshapiro qtype = initial; 86738032Speter 86838032Speter for (dp = searchlist; *dp != NULL; ) 86938032Speter { 870120256Sgshapiro if (qtype == initial) 87190792Sgshapiro gotmx = false; 87238032Speter if (tTd(8, 5)) 87390792Sgshapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 87438032Speter host, *dp, 87564562Sgshapiro# if NETINET6 87664562Sgshapiro qtype == T_AAAA ? "AAAA" : 87764562Sgshapiro# endif /* NETINET6 */ 87864562Sgshapiro qtype == T_A ? "A" : 87964562Sgshapiro qtype == T_MX ? "MX" : 88064562Sgshapiro "???"); 88171345Sgshapiro errno = 0; 88238032Speter ret = res_querydomain(host, *dp, C_IN, qtype, 88338032Speter answer.qb2, sizeof(answer.qb2)); 88438032Speter if (ret <= 0) 88538032Speter { 88690792Sgshapiro int save_errno = errno; 88790792Sgshapiro 88838032Speter if (tTd(8, 7)) 88990792Sgshapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 89090792Sgshapiro save_errno, h_errno); 89138032Speter 89290792Sgshapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 89338032Speter { 89471345Sgshapiro /* 89590792Sgshapiro ** the name server seems to be down or broken. 89671345Sgshapiro */ 89771345Sgshapiro 89873188Sgshapiro SM_SET_H_ERRNO(TRY_AGAIN); 89994334Sgshapiro if (**dp == '\0') 90094334Sgshapiro { 90194334Sgshapiro if (*statp == EX_OK) 90294334Sgshapiro *statp = EX_TEMPFAIL; 90394334Sgshapiro goto nexttype; 90494334Sgshapiro } 90538032Speter *statp = EX_TEMPFAIL; 90664562Sgshapiro 90773188Sgshapiro if (WorkAroundBrokenAAAA) 90873188Sgshapiro { 90973188Sgshapiro /* 91073188Sgshapiro ** Only return if not TRY_AGAIN as an 91173188Sgshapiro ** attempt with a different qtype may 91273188Sgshapiro ** succeed (res_querydomain() calls 91373188Sgshapiro ** res_query() calls res_send() which 91473188Sgshapiro ** sets errno to ETIMEDOUT if the 91573188Sgshapiro ** nameservers could be contacted but 91673188Sgshapiro ** didn't give an answer). 91773188Sgshapiro */ 91871345Sgshapiro 91990792Sgshapiro if (save_errno != ETIMEDOUT) 92090792Sgshapiro return false; 92173188Sgshapiro } 92290792Sgshapiro else 92390792Sgshapiro return false; 92438032Speter } 92538032Speter 92694334Sgshapironexttype: 92738032Speter if (h_errno != HOST_NOT_FOUND) 92838032Speter { 92938032Speter /* might have another type of interest */ 93064562Sgshapiro# if NETINET6 93190792Sgshapiro if (qtype == T_AAAA) 93238032Speter { 93364562Sgshapiro qtype = T_A; 93464562Sgshapiro continue; 93564562Sgshapiro } 93690792Sgshapiro else 93764562Sgshapiro# endif /* NETINET6 */ 93890792Sgshapiro if (qtype == T_A && !gotmx && 93990792Sgshapiro (trymx || **dp == '\0')) 94064562Sgshapiro { 94138032Speter qtype = T_MX; 94238032Speter continue; 94338032Speter } 94438032Speter } 94538032Speter 94638032Speter /* definite no -- try the next domain */ 94738032Speter dp++; 948120256Sgshapiro qtype = initial; 94938032Speter continue; 95038032Speter } 95138032Speter else if (tTd(8, 7)) 95290792Sgshapiro sm_dprintf("\tYES\n"); 95338032Speter 95438032Speter /* avoid problems after truncation in tcp packets */ 95538032Speter if (ret > sizeof(answer)) 95638032Speter ret = sizeof(answer); 957159609Sgshapiro SM_ASSERT(ret >= 0); 95838032Speter 95938032Speter /* 96038032Speter ** Appear to have a match. Confirm it by searching for A or 96138032Speter ** CNAME records. If we don't have a local domain 96238032Speter ** wild card MX record, we will accept MX as well. 96338032Speter */ 96438032Speter 96538032Speter hp = (HEADER *) &answer; 96690792Sgshapiro ap = (unsigned char *) &answer + HFIXEDSZ; 96790792Sgshapiro eom = (unsigned char *) &answer + ret; 96838032Speter 96938032Speter /* skip question part of response -- we know what we asked */ 97090792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 97164562Sgshapiro qdcount--; 97264562Sgshapiro ap += ret + QFIXEDSZ) 97338032Speter { 97438032Speter if ((ret = dn_skipname(ap, eom)) < 0) 97538032Speter { 97638032Speter if (tTd(8, 20)) 97790792Sgshapiro sm_dprintf("qdcount failure (%d)\n", 97890792Sgshapiro ntohs((unsigned short) hp->qdcount)); 97938032Speter *statp = EX_SOFTWARE; 98090792Sgshapiro return false; /* ???XXX??? */ 98138032Speter } 98238032Speter } 98338032Speter 98490792Sgshapiro amatch = false; 98590792Sgshapiro for (ancount = ntohs((unsigned short) hp->ancount); 98664562Sgshapiro --ancount >= 0 && ap < eom; 98764562Sgshapiro ap += n) 98838032Speter { 98990792Sgshapiro n = dn_expand((unsigned char *) &answer, eom, ap, 990168515Sgshapiro (RES_UNC_T) nbuf, sizeof(nbuf)); 99138032Speter if (n < 0) 99238032Speter break; 99338032Speter ap += n; 99438032Speter GETSHORT(type, ap); 99590792Sgshapiro ap += INT16SZ; /* skip over class */ 99690792Sgshapiro GETLONG(ttl, ap); 99790792Sgshapiro GETSHORT(n, ap); /* rdlength */ 99838032Speter switch (type) 99938032Speter { 100038032Speter case T_MX: 100190792Sgshapiro gotmx = true; 100238032Speter if (**dp != '\0' && HasWildcardMX) 100338032Speter { 100438032Speter /* 100538032Speter ** If we are using MX matches and have 100638032Speter ** not yet gotten one, save this one 100764562Sgshapiro ** but keep searching for an A or 100838032Speter ** CNAME match. 100938032Speter */ 101038032Speter 101138032Speter if (trymx && mxmatch == NULL) 101238032Speter mxmatch = *dp; 101338032Speter continue; 101438032Speter } 101538032Speter 101638032Speter /* 101738032Speter ** If we did not append a domain name, this 101838032Speter ** must have been a canonical name to start 101938032Speter ** with. Even if we did append a domain name, 102038032Speter ** in the absence of a wildcard MX this must 102138032Speter ** still be a real MX match. 102238032Speter ** Such MX matches are as good as an A match, 102338032Speter ** fall through. 102438032Speter */ 102564562Sgshapiro /* FALLTHROUGH */ 102638032Speter 102764562Sgshapiro# if NETINET6 102864562Sgshapiro case T_AAAA: 102964562Sgshapiro# endif /* NETINET6 */ 103038032Speter case T_A: 103138032Speter /* Flag that a good match was found */ 103290792Sgshapiro amatch = true; 103338032Speter 103438032Speter /* continue in case a CNAME also exists */ 103538032Speter continue; 103638032Speter 103738032Speter case T_CNAME: 103838032Speter if (DontExpandCnames) 103938032Speter { 104038032Speter /* got CNAME -- guaranteed canonical */ 104190792Sgshapiro amatch = true; 104238032Speter break; 104338032Speter } 104438032Speter 104538032Speter if (loopcnt++ > MAXCNAMEDEPTH) 104638032Speter { 104738032Speter /*XXX should notify postmaster XXX*/ 104838032Speter message("DNS failure: CNAME loop for %s", 104938032Speter host); 105038032Speter if (CurEnv->e_message == NULL) 105138032Speter { 105238032Speter char ebuf[MAXLINE]; 105338032Speter 105490792Sgshapiro (void) sm_snprintf(ebuf, 1055168515Sgshapiro sizeof(ebuf), 105638032Speter "Deferred: DNS failure: CNAME loop for %.100s", 105738032Speter host); 105890792Sgshapiro CurEnv->e_message = 105990792Sgshapiro sm_rpool_strdup_x( 106090792Sgshapiro CurEnv->e_rpool, ebuf); 106138032Speter } 106273188Sgshapiro SM_SET_H_ERRNO(NO_RECOVERY); 106338032Speter *statp = EX_CONFIG; 106490792Sgshapiro return false; 106538032Speter } 106638032Speter 106738032Speter /* value points at name */ 106890792Sgshapiro if ((ret = dn_expand((unsigned char *)&answer, 106990792Sgshapiro eom, ap, (RES_UNC_T) nbuf, 107090792Sgshapiro sizeof(nbuf))) < 0) 107138032Speter break; 107290792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 107338032Speter 107438032Speter /* 107538032Speter ** RFC 1034 section 3.6 specifies that CNAME 107638032Speter ** should point at the canonical name -- but 107738032Speter ** urges software to try again anyway. 107838032Speter */ 107938032Speter 108038032Speter goto cnameloop; 108138032Speter 108238032Speter default: 108338032Speter /* not a record of interest */ 108438032Speter continue; 108538032Speter } 108638032Speter } 108738032Speter 108838032Speter if (amatch) 108938032Speter { 109064562Sgshapiro /* 109138032Speter ** Got a good match -- either an A, CNAME, or an 109238032Speter ** exact MX record. Save it and get out of here. 109338032Speter */ 109438032Speter 109538032Speter mxmatch = *dp; 109638032Speter break; 109738032Speter } 109838032Speter 109938032Speter /* 110038032Speter ** Nothing definitive yet. 110138032Speter ** If this was a T_A query and we haven't yet found a MX 110238032Speter ** match, try T_MX if allowed to do so. 110338032Speter ** Otherwise, try the next domain. 110438032Speter */ 110538032Speter 110664562Sgshapiro# if NETINET6 110790792Sgshapiro if (qtype == T_AAAA) 110838032Speter qtype = T_A; 110990792Sgshapiro else 111064562Sgshapiro# endif /* NETINET6 */ 111190792Sgshapiro if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 111238032Speter qtype = T_MX; 111338032Speter else 111438032Speter { 1115120256Sgshapiro qtype = initial; 111638032Speter dp++; 111738032Speter } 111838032Speter } 111938032Speter 112038032Speter /* if nothing was found, we are done */ 112138032Speter if (mxmatch == NULL) 112238032Speter { 112371345Sgshapiro if (*statp == EX_OK) 112471345Sgshapiro *statp = EX_NOHOST; 112590792Sgshapiro return false; 112638032Speter } 112738032Speter 112838032Speter /* 112938032Speter ** Create canonical name and return. 113038032Speter ** If saved domain name is null, name was already canonical. 113138032Speter ** Otherwise append the saved domain name. 113238032Speter */ 113338032Speter 1134168515Sgshapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host, 113590792Sgshapiro *mxmatch == '\0' ? "" : ".", 113690792Sgshapiro MAXDNAME, mxmatch); 113790792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 113838032Speter if (tTd(8, 5)) 113990792Sgshapiro sm_dprintf("dns_getcanonname: %s\n", host); 114038032Speter *statp = EX_OK; 114190792Sgshapiro 114290792Sgshapiro /* return only one TTL entry, that should be sufficient */ 114390792Sgshapiro if (ttl > 0 && pttl != NULL) 114490792Sgshapiro *pttl = ttl; 114590792Sgshapiro return true; 114638032Speter} 114738032Speter#endif /* NAMED_BIND */ 1148