1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. 3*0Sstevel@tonic-gate * All rights reserved. 4*0Sstevel@tonic-gate * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 5*0Sstevel@tonic-gate * Copyright (c) 1988, 1993 6*0Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 7*0Sstevel@tonic-gate * 8*0Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 9*0Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 10*0Sstevel@tonic-gate * the sendmail distribution. 11*0Sstevel@tonic-gate * 12*0Sstevel@tonic-gate */ 13*0Sstevel@tonic-gate 14*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate #include <sendmail.h> 17*0Sstevel@tonic-gate 18*0Sstevel@tonic-gate #if NAMED_BIND 19*0Sstevel@tonic-gate SM_RCSID("@(#)$Id: domain.c,v 8.197 2005/03/04 00:54:42 ca Exp $ (with name server)") 20*0Sstevel@tonic-gate #else /* NAMED_BIND */ 21*0Sstevel@tonic-gate SM_RCSID("@(#)$Id: domain.c,v 8.197 2005/03/04 00:54:42 ca Exp $ (without name server)") 22*0Sstevel@tonic-gate #endif /* NAMED_BIND */ 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gate #if NAMED_BIND 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate # include <arpa/inet.h> 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate ** The standard udp packet size PACKETSZ (512) is not sufficient for some 31*0Sstevel@tonic-gate ** nameserver answers containing very many resource records. The resolver 32*0Sstevel@tonic-gate ** may switch to tcp and retry if it detects udp packet overflow. 33*0Sstevel@tonic-gate ** Also note that the resolver routines res_query and res_search return 34*0Sstevel@tonic-gate ** the size of the *un*truncated answer in case the supplied answer buffer 35*0Sstevel@tonic-gate ** it not big enough to accommodate the entire answer. 36*0Sstevel@tonic-gate */ 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate # ifndef MAXPACKET 39*0Sstevel@tonic-gate # define MAXPACKET 8192 /* max packet size used internally by BIND */ 40*0Sstevel@tonic-gate # endif /* ! MAXPACKET */ 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate typedef union 43*0Sstevel@tonic-gate { 44*0Sstevel@tonic-gate HEADER qb1; 45*0Sstevel@tonic-gate unsigned char qb2[MAXPACKET]; 46*0Sstevel@tonic-gate } querybuf; 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate # ifndef MXHOSTBUFSIZE 49*0Sstevel@tonic-gate # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 50*0Sstevel@tonic-gate # endif /* ! MXHOSTBUFSIZE */ 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate static char MXHostBuf[MXHOSTBUFSIZE]; 53*0Sstevel@tonic-gate #if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 54*0Sstevel@tonic-gate ERROR: _MXHOSTBUFSIZE is out of range 55*0Sstevel@tonic-gate #endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate # ifndef MAXDNSRCH 58*0Sstevel@tonic-gate # define MAXDNSRCH 6 /* number of possible domains to search */ 59*0Sstevel@tonic-gate # endif /* ! MAXDNSRCH */ 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate # ifndef RES_DNSRCH_VARIABLE 62*0Sstevel@tonic-gate # define RES_DNSRCH_VARIABLE _res.dnsrch 63*0Sstevel@tonic-gate # endif /* ! RES_DNSRCH_VARIABLE */ 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate # ifndef NO_DATA 66*0Sstevel@tonic-gate # define NO_DATA NO_ADDRESS 67*0Sstevel@tonic-gate # endif /* ! NO_DATA */ 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate # ifndef HFIXEDSZ 70*0Sstevel@tonic-gate # define HFIXEDSZ 12 /* sizeof(HEADER) */ 71*0Sstevel@tonic-gate # endif /* ! HFIXEDSZ */ 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate # if defined(__RES) && (__RES >= 19940415) 76*0Sstevel@tonic-gate # define RES_UNC_T char * 77*0Sstevel@tonic-gate # else /* defined(__RES) && (__RES >= 19940415) */ 78*0Sstevel@tonic-gate # define RES_UNC_T unsigned char * 79*0Sstevel@tonic-gate # endif /* defined(__RES) && (__RES >= 19940415) */ 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate static int mxrand __P((char *)); 82*0Sstevel@tonic-gate static int fallbackmxrr __P((int, unsigned short *, char **)); 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate /* 85*0Sstevel@tonic-gate ** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 86*0Sstevel@tonic-gate ** 87*0Sstevel@tonic-gate ** We have to initialize this once before doing anything else. 88*0Sstevel@tonic-gate ** Moreover, we have to repeat this from time to time to avoid 89*0Sstevel@tonic-gate ** stale data, e.g., in persistent queue runners. 90*0Sstevel@tonic-gate ** This should be done in a parent process so the child 91*0Sstevel@tonic-gate ** processes have the right data. 92*0Sstevel@tonic-gate ** 93*0Sstevel@tonic-gate ** Parameters: 94*0Sstevel@tonic-gate ** host -- the name of the fallback MX host. 95*0Sstevel@tonic-gate ** 96*0Sstevel@tonic-gate ** Returns: 97*0Sstevel@tonic-gate ** number of MX records. 98*0Sstevel@tonic-gate ** 99*0Sstevel@tonic-gate ** Side Effects: 100*0Sstevel@tonic-gate ** Populates NumFallbackMXHosts and fbhosts. 101*0Sstevel@tonic-gate ** Sets renewal time (based on TTL). 102*0Sstevel@tonic-gate */ 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 105*0Sstevel@tonic-gate static char *fbhosts[MAXMXHOSTS + 1]; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate int 108*0Sstevel@tonic-gate getfallbackmxrr(host) 109*0Sstevel@tonic-gate char *host; 110*0Sstevel@tonic-gate { 111*0Sstevel@tonic-gate int i, rcode; 112*0Sstevel@tonic-gate int ttl; 113*0Sstevel@tonic-gate static time_t renew = 0; 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate #if 0 116*0Sstevel@tonic-gate /* This is currently done before this function is called. */ 117*0Sstevel@tonic-gate if (host == NULL || *host == '\0') 118*0Sstevel@tonic-gate return 0; 119*0Sstevel@tonic-gate #endif /* 0 */ 120*0Sstevel@tonic-gate if (NumFallbackMXHosts > 0 && renew > curtime()) 121*0Sstevel@tonic-gate return NumFallbackMXHosts; 122*0Sstevel@tonic-gate if (host[0] == '[') 123*0Sstevel@tonic-gate { 124*0Sstevel@tonic-gate fbhosts[0] = host; 125*0Sstevel@tonic-gate NumFallbackMXHosts = 1; 126*0Sstevel@tonic-gate } 127*0Sstevel@tonic-gate else 128*0Sstevel@tonic-gate { 129*0Sstevel@tonic-gate /* free old data */ 130*0Sstevel@tonic-gate for (i = 0; i < NumFallbackMXHosts; i++) 131*0Sstevel@tonic-gate sm_free(fbhosts[i]); 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate /* get new data */ 134*0Sstevel@tonic-gate NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false, 135*0Sstevel@tonic-gate &rcode, false, &ttl); 136*0Sstevel@tonic-gate renew = curtime() + ttl; 137*0Sstevel@tonic-gate for (i = 0; i < NumFallbackMXHosts; i++) 138*0Sstevel@tonic-gate fbhosts[i] = newstr(fbhosts[i]); 139*0Sstevel@tonic-gate } 140*0Sstevel@tonic-gate return NumFallbackMXHosts; 141*0Sstevel@tonic-gate } 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate /* 144*0Sstevel@tonic-gate ** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 145*0Sstevel@tonic-gate ** 146*0Sstevel@tonic-gate ** Parameters: 147*0Sstevel@tonic-gate ** nmx -- current number of MX records. 148*0Sstevel@tonic-gate ** prefs -- array of preferences. 149*0Sstevel@tonic-gate ** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 150*0Sstevel@tonic-gate ** 151*0Sstevel@tonic-gate ** Returns: 152*0Sstevel@tonic-gate ** new number of MX records. 153*0Sstevel@tonic-gate ** 154*0Sstevel@tonic-gate ** Side Effects: 155*0Sstevel@tonic-gate ** If FallbackMX was set, it appends the MX records for 156*0Sstevel@tonic-gate ** that host to mxhosts (and modifies prefs accordingly). 157*0Sstevel@tonic-gate */ 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate static int 160*0Sstevel@tonic-gate fallbackmxrr(nmx, prefs, mxhosts) 161*0Sstevel@tonic-gate int nmx; 162*0Sstevel@tonic-gate unsigned short *prefs; 163*0Sstevel@tonic-gate char **mxhosts; 164*0Sstevel@tonic-gate { 165*0Sstevel@tonic-gate int i; 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 168*0Sstevel@tonic-gate { 169*0Sstevel@tonic-gate if (nmx > 0) 170*0Sstevel@tonic-gate prefs[nmx] = prefs[nmx - 1] + 1; 171*0Sstevel@tonic-gate else 172*0Sstevel@tonic-gate prefs[nmx] = 0; 173*0Sstevel@tonic-gate mxhosts[nmx++] = fbhosts[i]; 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate return nmx; 176*0Sstevel@tonic-gate } 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate /* 179*0Sstevel@tonic-gate ** GETMXRR -- get MX resource records for a domain 180*0Sstevel@tonic-gate ** 181*0Sstevel@tonic-gate ** Parameters: 182*0Sstevel@tonic-gate ** host -- the name of the host to MX. 183*0Sstevel@tonic-gate ** mxhosts -- a pointer to a return buffer of MX records. 184*0Sstevel@tonic-gate ** mxprefs -- a pointer to a return buffer of MX preferences. 185*0Sstevel@tonic-gate ** If NULL, don't try to populate. 186*0Sstevel@tonic-gate ** droplocalhost -- If true, all MX records less preferred 187*0Sstevel@tonic-gate ** than the local host (as determined by $=w) will 188*0Sstevel@tonic-gate ** be discarded. 189*0Sstevel@tonic-gate ** rcode -- a pointer to an EX_ status code. 190*0Sstevel@tonic-gate ** tryfallback -- add also fallback MX host? 191*0Sstevel@tonic-gate ** pttl -- pointer to return TTL (can be NULL). 192*0Sstevel@tonic-gate ** 193*0Sstevel@tonic-gate ** Returns: 194*0Sstevel@tonic-gate ** The number of MX records found. 195*0Sstevel@tonic-gate ** -1 if there is an internal failure. 196*0Sstevel@tonic-gate ** If no MX records are found, mxhosts[0] is set to host 197*0Sstevel@tonic-gate ** and 1 is returned. 198*0Sstevel@tonic-gate ** 199*0Sstevel@tonic-gate ** Side Effects: 200*0Sstevel@tonic-gate ** The entries made for mxhosts point to a static array 201*0Sstevel@tonic-gate ** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 202*0Sstevel@tonic-gate ** if it must be preserved across calls to this function. 203*0Sstevel@tonic-gate */ 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate int 206*0Sstevel@tonic-gate getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) 207*0Sstevel@tonic-gate char *host; 208*0Sstevel@tonic-gate char **mxhosts; 209*0Sstevel@tonic-gate unsigned short *mxprefs; 210*0Sstevel@tonic-gate bool droplocalhost; 211*0Sstevel@tonic-gate int *rcode; 212*0Sstevel@tonic-gate bool tryfallback; 213*0Sstevel@tonic-gate int *pttl; 214*0Sstevel@tonic-gate { 215*0Sstevel@tonic-gate register unsigned char *eom, *cp; 216*0Sstevel@tonic-gate register int i, j, n; 217*0Sstevel@tonic-gate int nmx = 0; 218*0Sstevel@tonic-gate register char *bp; 219*0Sstevel@tonic-gate HEADER *hp; 220*0Sstevel@tonic-gate querybuf answer; 221*0Sstevel@tonic-gate int ancount, qdcount, buflen; 222*0Sstevel@tonic-gate bool seenlocal = false; 223*0Sstevel@tonic-gate unsigned short pref, type; 224*0Sstevel@tonic-gate unsigned short localpref = 256; 225*0Sstevel@tonic-gate char *fallbackMX = FallbackMX; 226*0Sstevel@tonic-gate bool trycanon = false; 227*0Sstevel@tonic-gate unsigned short *prefs; 228*0Sstevel@tonic-gate int (*resfunc) __P((const char *, int, int, u_char *, int)); 229*0Sstevel@tonic-gate unsigned short prefer[MAXMXHOSTS]; 230*0Sstevel@tonic-gate int weight[MAXMXHOSTS]; 231*0Sstevel@tonic-gate int ttl = 0; 232*0Sstevel@tonic-gate extern int res_query(), res_search(); 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate if (tTd(8, 2)) 235*0Sstevel@tonic-gate sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", 236*0Sstevel@tonic-gate host, droplocalhost); 237*0Sstevel@tonic-gate *rcode = EX_OK; 238*0Sstevel@tonic-gate if (pttl != NULL) 239*0Sstevel@tonic-gate *pttl = SM_DEFAULT_TTL; 240*0Sstevel@tonic-gate if (*host == '\0') 241*0Sstevel@tonic-gate return 0; 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate if ((fallbackMX != NULL && droplocalhost && 244*0Sstevel@tonic-gate wordinclass(fallbackMX, 'w')) || !tryfallback) 245*0Sstevel@tonic-gate { 246*0Sstevel@tonic-gate /* don't use fallback for this pass */ 247*0Sstevel@tonic-gate fallbackMX = NULL; 248*0Sstevel@tonic-gate } 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate if (mxprefs != NULL) 251*0Sstevel@tonic-gate prefs = mxprefs; 252*0Sstevel@tonic-gate else 253*0Sstevel@tonic-gate prefs = prefer; 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate /* efficiency hack -- numeric or non-MX lookups */ 256*0Sstevel@tonic-gate if (host[0] == '[') 257*0Sstevel@tonic-gate goto punt; 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate /* 260*0Sstevel@tonic-gate ** If we don't have MX records in our host switch, don't 261*0Sstevel@tonic-gate ** try for MX records. Note that this really isn't "right", 262*0Sstevel@tonic-gate ** since we might be set up to try NIS first and then DNS; 263*0Sstevel@tonic-gate ** if the host is found in NIS we really shouldn't be doing 264*0Sstevel@tonic-gate ** MX lookups. However, that should be a degenerate case. 265*0Sstevel@tonic-gate */ 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate if (!UseNameServer) 268*0Sstevel@tonic-gate goto punt; 269*0Sstevel@tonic-gate if (HasWildcardMX && ConfigLevel >= 6) 270*0Sstevel@tonic-gate resfunc = res_query; 271*0Sstevel@tonic-gate else 272*0Sstevel@tonic-gate resfunc = res_search; 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate errno = 0; 275*0Sstevel@tonic-gate n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 276*0Sstevel@tonic-gate sizeof(answer)); 277*0Sstevel@tonic-gate if (n < 0) 278*0Sstevel@tonic-gate { 279*0Sstevel@tonic-gate if (tTd(8, 1)) 280*0Sstevel@tonic-gate sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 281*0Sstevel@tonic-gate host == NULL ? "<NULL>" : host, errno, h_errno); 282*0Sstevel@tonic-gate switch (h_errno) 283*0Sstevel@tonic-gate { 284*0Sstevel@tonic-gate case NO_DATA: 285*0Sstevel@tonic-gate trycanon = true; 286*0Sstevel@tonic-gate /* FALLTHROUGH */ 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate case NO_RECOVERY: 289*0Sstevel@tonic-gate /* no MX data on this host */ 290*0Sstevel@tonic-gate goto punt; 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate case HOST_NOT_FOUND: 293*0Sstevel@tonic-gate # if BROKEN_RES_SEARCH 294*0Sstevel@tonic-gate case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 295*0Sstevel@tonic-gate # endif /* BROKEN_RES_SEARCH */ 296*0Sstevel@tonic-gate /* host doesn't exist in DNS; might be in /etc/hosts */ 297*0Sstevel@tonic-gate trycanon = true; 298*0Sstevel@tonic-gate *rcode = EX_NOHOST; 299*0Sstevel@tonic-gate goto punt; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate case TRY_AGAIN: 302*0Sstevel@tonic-gate case -1: 303*0Sstevel@tonic-gate /* couldn't connect to the name server */ 304*0Sstevel@tonic-gate if (fallbackMX != NULL) 305*0Sstevel@tonic-gate { 306*0Sstevel@tonic-gate /* name server is hosed -- push to fallback */ 307*0Sstevel@tonic-gate return fallbackmxrr(nmx, prefs, mxhosts); 308*0Sstevel@tonic-gate } 309*0Sstevel@tonic-gate /* it might come up later; better queue it up */ 310*0Sstevel@tonic-gate *rcode = EX_TEMPFAIL; 311*0Sstevel@tonic-gate break; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate default: 314*0Sstevel@tonic-gate syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 315*0Sstevel@tonic-gate host, h_errno); 316*0Sstevel@tonic-gate *rcode = EX_OSERR; 317*0Sstevel@tonic-gate break; 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate /* irreconcilable differences */ 321*0Sstevel@tonic-gate return -1; 322*0Sstevel@tonic-gate } 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate /* avoid problems after truncation in tcp packets */ 325*0Sstevel@tonic-gate if (n > sizeof(answer)) 326*0Sstevel@tonic-gate n = sizeof(answer); 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate /* find first satisfactory answer */ 329*0Sstevel@tonic-gate hp = (HEADER *)&answer; 330*0Sstevel@tonic-gate cp = (unsigned char *)&answer + HFIXEDSZ; 331*0Sstevel@tonic-gate eom = (unsigned char *)&answer + n; 332*0Sstevel@tonic-gate for (qdcount = ntohs((unsigned short) hp->qdcount); 333*0Sstevel@tonic-gate qdcount--; 334*0Sstevel@tonic-gate cp += n + QFIXEDSZ) 335*0Sstevel@tonic-gate { 336*0Sstevel@tonic-gate if ((n = dn_skipname(cp, eom)) < 0) 337*0Sstevel@tonic-gate goto punt; 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate /* NOTE: see definition of MXHostBuf! */ 341*0Sstevel@tonic-gate buflen = sizeof(MXHostBuf) - 1; 342*0Sstevel@tonic-gate SM_ASSERT(buflen > 0); 343*0Sstevel@tonic-gate bp = MXHostBuf; 344*0Sstevel@tonic-gate ancount = ntohs((unsigned short) hp->ancount); 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate /* See RFC 1035 for layout of RRs. */ 347*0Sstevel@tonic-gate /* XXX leave room for FallbackMX ? */ 348*0Sstevel@tonic-gate while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 349*0Sstevel@tonic-gate { 350*0Sstevel@tonic-gate if ((n = dn_expand((unsigned char *)&answer, eom, cp, 351*0Sstevel@tonic-gate (RES_UNC_T) bp, buflen)) < 0) 352*0Sstevel@tonic-gate break; 353*0Sstevel@tonic-gate cp += n; 354*0Sstevel@tonic-gate GETSHORT(type, cp); 355*0Sstevel@tonic-gate cp += INT16SZ; /* skip over class */ 356*0Sstevel@tonic-gate GETLONG(ttl, cp); 357*0Sstevel@tonic-gate GETSHORT(n, cp); /* rdlength */ 358*0Sstevel@tonic-gate if (type != T_MX) 359*0Sstevel@tonic-gate { 360*0Sstevel@tonic-gate if (tTd(8, 8) || _res.options & RES_DEBUG) 361*0Sstevel@tonic-gate sm_dprintf("unexpected answer type %d, size %d\n", 362*0Sstevel@tonic-gate type, n); 363*0Sstevel@tonic-gate cp += n; 364*0Sstevel@tonic-gate continue; 365*0Sstevel@tonic-gate } 366*0Sstevel@tonic-gate GETSHORT(pref, cp); 367*0Sstevel@tonic-gate if ((n = dn_expand((unsigned char *)&answer, eom, cp, 368*0Sstevel@tonic-gate (RES_UNC_T) bp, buflen)) < 0) 369*0Sstevel@tonic-gate break; 370*0Sstevel@tonic-gate cp += n; 371*0Sstevel@tonic-gate n = strlen(bp); 372*0Sstevel@tonic-gate # if 0 373*0Sstevel@tonic-gate /* Can this happen? */ 374*0Sstevel@tonic-gate if (n == 0) 375*0Sstevel@tonic-gate { 376*0Sstevel@tonic-gate if (LogLevel > 4) 377*0Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 378*0Sstevel@tonic-gate "MX records for %s contain empty string", 379*0Sstevel@tonic-gate host); 380*0Sstevel@tonic-gate continue; 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate # endif /* 0 */ 383*0Sstevel@tonic-gate if (wordinclass(bp, 'w')) 384*0Sstevel@tonic-gate { 385*0Sstevel@tonic-gate if (tTd(8, 3)) 386*0Sstevel@tonic-gate sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 387*0Sstevel@tonic-gate bp, pref); 388*0Sstevel@tonic-gate if (droplocalhost) 389*0Sstevel@tonic-gate { 390*0Sstevel@tonic-gate if (!seenlocal || pref < localpref) 391*0Sstevel@tonic-gate localpref = pref; 392*0Sstevel@tonic-gate seenlocal = true; 393*0Sstevel@tonic-gate continue; 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate weight[nmx] = 0; 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate else 398*0Sstevel@tonic-gate weight[nmx] = mxrand(bp); 399*0Sstevel@tonic-gate prefs[nmx] = pref; 400*0Sstevel@tonic-gate mxhosts[nmx++] = bp; 401*0Sstevel@tonic-gate bp += n; 402*0Sstevel@tonic-gate if (bp[-1] != '.') 403*0Sstevel@tonic-gate { 404*0Sstevel@tonic-gate *bp++ = '.'; 405*0Sstevel@tonic-gate n++; 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate *bp++ = '\0'; 408*0Sstevel@tonic-gate if (buflen < n + 1) 409*0Sstevel@tonic-gate { 410*0Sstevel@tonic-gate /* don't want to wrap buflen */ 411*0Sstevel@tonic-gate break; 412*0Sstevel@tonic-gate } 413*0Sstevel@tonic-gate buflen -= n + 1; 414*0Sstevel@tonic-gate } 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate /* return only one TTL entry, that should be sufficient */ 417*0Sstevel@tonic-gate if (ttl > 0 && pttl != NULL) 418*0Sstevel@tonic-gate *pttl = ttl; 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate /* sort the records */ 421*0Sstevel@tonic-gate for (i = 0; i < nmx; i++) 422*0Sstevel@tonic-gate { 423*0Sstevel@tonic-gate for (j = i + 1; j < nmx; j++) 424*0Sstevel@tonic-gate { 425*0Sstevel@tonic-gate if (prefs[i] > prefs[j] || 426*0Sstevel@tonic-gate (prefs[i] == prefs[j] && weight[i] > weight[j])) 427*0Sstevel@tonic-gate { 428*0Sstevel@tonic-gate register int temp; 429*0Sstevel@tonic-gate register char *temp1; 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate temp = prefs[i]; 432*0Sstevel@tonic-gate prefs[i] = prefs[j]; 433*0Sstevel@tonic-gate prefs[j] = temp; 434*0Sstevel@tonic-gate temp1 = mxhosts[i]; 435*0Sstevel@tonic-gate mxhosts[i] = mxhosts[j]; 436*0Sstevel@tonic-gate mxhosts[j] = temp1; 437*0Sstevel@tonic-gate temp = weight[i]; 438*0Sstevel@tonic-gate weight[i] = weight[j]; 439*0Sstevel@tonic-gate weight[j] = temp; 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate if (seenlocal && prefs[i] >= localpref) 443*0Sstevel@tonic-gate { 444*0Sstevel@tonic-gate /* truncate higher preference part of list */ 445*0Sstevel@tonic-gate nmx = i; 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate } 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate /* delete duplicates from list (yes, some bozos have duplicates) */ 450*0Sstevel@tonic-gate for (i = 0; i < nmx - 1; ) 451*0Sstevel@tonic-gate { 452*0Sstevel@tonic-gate if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 453*0Sstevel@tonic-gate i++; 454*0Sstevel@tonic-gate else 455*0Sstevel@tonic-gate { 456*0Sstevel@tonic-gate /* compress out duplicate */ 457*0Sstevel@tonic-gate for (j = i + 1; j < nmx; j++) 458*0Sstevel@tonic-gate { 459*0Sstevel@tonic-gate mxhosts[j] = mxhosts[j + 1]; 460*0Sstevel@tonic-gate prefs[j] = prefs[j + 1]; 461*0Sstevel@tonic-gate } 462*0Sstevel@tonic-gate nmx--; 463*0Sstevel@tonic-gate } 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate if (nmx == 0) 467*0Sstevel@tonic-gate { 468*0Sstevel@tonic-gate punt: 469*0Sstevel@tonic-gate if (seenlocal) 470*0Sstevel@tonic-gate { 471*0Sstevel@tonic-gate struct hostent *h = NULL; 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate /* 474*0Sstevel@tonic-gate ** If we have deleted all MX entries, this is 475*0Sstevel@tonic-gate ** an error -- we should NEVER send to a host that 476*0Sstevel@tonic-gate ** has an MX, and this should have been caught 477*0Sstevel@tonic-gate ** earlier in the config file. 478*0Sstevel@tonic-gate ** 479*0Sstevel@tonic-gate ** Some sites prefer to go ahead and try the 480*0Sstevel@tonic-gate ** A record anyway; that case is handled by 481*0Sstevel@tonic-gate ** setting TryNullMXList. I believe this is a 482*0Sstevel@tonic-gate ** bad idea, but it's up to you.... 483*0Sstevel@tonic-gate */ 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate if (TryNullMXList) 486*0Sstevel@tonic-gate { 487*0Sstevel@tonic-gate SM_SET_H_ERRNO(0); 488*0Sstevel@tonic-gate errno = 0; 489*0Sstevel@tonic-gate h = sm_gethostbyname(host, AF_INET); 490*0Sstevel@tonic-gate if (h == NULL) 491*0Sstevel@tonic-gate { 492*0Sstevel@tonic-gate if (errno == ETIMEDOUT || 493*0Sstevel@tonic-gate h_errno == TRY_AGAIN || 494*0Sstevel@tonic-gate (errno == ECONNREFUSED && 495*0Sstevel@tonic-gate UseNameServer)) 496*0Sstevel@tonic-gate { 497*0Sstevel@tonic-gate *rcode = EX_TEMPFAIL; 498*0Sstevel@tonic-gate return -1; 499*0Sstevel@tonic-gate } 500*0Sstevel@tonic-gate # if NETINET6 501*0Sstevel@tonic-gate SM_SET_H_ERRNO(0); 502*0Sstevel@tonic-gate errno = 0; 503*0Sstevel@tonic-gate h = sm_gethostbyname(host, AF_INET6); 504*0Sstevel@tonic-gate if (h == NULL && 505*0Sstevel@tonic-gate (errno == ETIMEDOUT || 506*0Sstevel@tonic-gate h_errno == TRY_AGAIN || 507*0Sstevel@tonic-gate (errno == ECONNREFUSED && 508*0Sstevel@tonic-gate UseNameServer))) 509*0Sstevel@tonic-gate { 510*0Sstevel@tonic-gate *rcode = EX_TEMPFAIL; 511*0Sstevel@tonic-gate return -1; 512*0Sstevel@tonic-gate } 513*0Sstevel@tonic-gate # endif /* NETINET6 */ 514*0Sstevel@tonic-gate } 515*0Sstevel@tonic-gate } 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate if (h == NULL) 518*0Sstevel@tonic-gate { 519*0Sstevel@tonic-gate *rcode = EX_CONFIG; 520*0Sstevel@tonic-gate syserr("MX list for %s points back to %s", 521*0Sstevel@tonic-gate host, MyHostName); 522*0Sstevel@tonic-gate return -1; 523*0Sstevel@tonic-gate } 524*0Sstevel@tonic-gate # if NETINET6 525*0Sstevel@tonic-gate freehostent(h); 526*0Sstevel@tonic-gate hp = NULL; 527*0Sstevel@tonic-gate # endif /* NETINET6 */ 528*0Sstevel@tonic-gate } 529*0Sstevel@tonic-gate if (strlen(host) >= sizeof MXHostBuf) 530*0Sstevel@tonic-gate { 531*0Sstevel@tonic-gate *rcode = EX_CONFIG; 532*0Sstevel@tonic-gate syserr("Host name %s too long", 533*0Sstevel@tonic-gate shortenstring(host, MAXSHORTSTR)); 534*0Sstevel@tonic-gate return -1; 535*0Sstevel@tonic-gate } 536*0Sstevel@tonic-gate (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf); 537*0Sstevel@tonic-gate mxhosts[0] = MXHostBuf; 538*0Sstevel@tonic-gate prefs[0] = 0; 539*0Sstevel@tonic-gate if (host[0] == '[') 540*0Sstevel@tonic-gate { 541*0Sstevel@tonic-gate register char *p; 542*0Sstevel@tonic-gate # if NETINET6 543*0Sstevel@tonic-gate struct sockaddr_in6 tmp6; 544*0Sstevel@tonic-gate # endif /* NETINET6 */ 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate /* this may be an MX suppression-style address */ 547*0Sstevel@tonic-gate p = strchr(MXHostBuf, ']'); 548*0Sstevel@tonic-gate if (p != NULL) 549*0Sstevel@tonic-gate { 550*0Sstevel@tonic-gate *p = '\0'; 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 553*0Sstevel@tonic-gate { 554*0Sstevel@tonic-gate nmx++; 555*0Sstevel@tonic-gate *p = ']'; 556*0Sstevel@tonic-gate } 557*0Sstevel@tonic-gate # if NETINET6 558*0Sstevel@tonic-gate else if (anynet_pton(AF_INET6, &MXHostBuf[1], 559*0Sstevel@tonic-gate &tmp6.sin6_addr) == 1) 560*0Sstevel@tonic-gate { 561*0Sstevel@tonic-gate nmx++; 562*0Sstevel@tonic-gate *p = ']'; 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate # endif /* NETINET6 */ 565*0Sstevel@tonic-gate else 566*0Sstevel@tonic-gate { 567*0Sstevel@tonic-gate trycanon = true; 568*0Sstevel@tonic-gate mxhosts[0]++; 569*0Sstevel@tonic-gate } 570*0Sstevel@tonic-gate } 571*0Sstevel@tonic-gate } 572*0Sstevel@tonic-gate if (trycanon && 573*0Sstevel@tonic-gate getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl)) 574*0Sstevel@tonic-gate { 575*0Sstevel@tonic-gate /* XXX MXHostBuf == "" ? is that possible? */ 576*0Sstevel@tonic-gate bp = &MXHostBuf[strlen(MXHostBuf)]; 577*0Sstevel@tonic-gate if (bp[-1] != '.') 578*0Sstevel@tonic-gate { 579*0Sstevel@tonic-gate *bp++ = '.'; 580*0Sstevel@tonic-gate *bp = '\0'; 581*0Sstevel@tonic-gate } 582*0Sstevel@tonic-gate nmx = 1; 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate } 585*0Sstevel@tonic-gate 586*0Sstevel@tonic-gate /* if we have a default lowest preference, include that */ 587*0Sstevel@tonic-gate if (fallbackMX != NULL && !seenlocal) 588*0Sstevel@tonic-gate { 589*0Sstevel@tonic-gate nmx = fallbackmxrr(nmx, prefs, mxhosts); 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate return nmx; 592*0Sstevel@tonic-gate } 593*0Sstevel@tonic-gate /* 594*0Sstevel@tonic-gate ** MXRAND -- create a randomizer for equal MX preferences 595*0Sstevel@tonic-gate ** 596*0Sstevel@tonic-gate ** If two MX hosts have equal preferences we want to randomize 597*0Sstevel@tonic-gate ** the selection. But in order for signatures to be the same, 598*0Sstevel@tonic-gate ** we need to randomize the same way each time. This function 599*0Sstevel@tonic-gate ** computes a pseudo-random hash function from the host name. 600*0Sstevel@tonic-gate ** 601*0Sstevel@tonic-gate ** Parameters: 602*0Sstevel@tonic-gate ** host -- the name of the host. 603*0Sstevel@tonic-gate ** 604*0Sstevel@tonic-gate ** Returns: 605*0Sstevel@tonic-gate ** A random but repeatable value based on the host name. 606*0Sstevel@tonic-gate */ 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gate static int 609*0Sstevel@tonic-gate mxrand(host) 610*0Sstevel@tonic-gate register char *host; 611*0Sstevel@tonic-gate { 612*0Sstevel@tonic-gate int hfunc; 613*0Sstevel@tonic-gate static unsigned int seed; 614*0Sstevel@tonic-gate 615*0Sstevel@tonic-gate if (seed == 0) 616*0Sstevel@tonic-gate { 617*0Sstevel@tonic-gate seed = (int) curtime() & 0xffff; 618*0Sstevel@tonic-gate if (seed == 0) 619*0Sstevel@tonic-gate seed++; 620*0Sstevel@tonic-gate } 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate if (tTd(17, 9)) 623*0Sstevel@tonic-gate sm_dprintf("mxrand(%s)", host); 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate hfunc = seed; 626*0Sstevel@tonic-gate while (*host != '\0') 627*0Sstevel@tonic-gate { 628*0Sstevel@tonic-gate int c = *host++; 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate if (isascii(c) && isupper(c)) 631*0Sstevel@tonic-gate c = tolower(c); 632*0Sstevel@tonic-gate hfunc = ((hfunc << 1) ^ c) % 2003; 633*0Sstevel@tonic-gate } 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate hfunc &= 0xff; 636*0Sstevel@tonic-gate hfunc++; 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate if (tTd(17, 9)) 639*0Sstevel@tonic-gate sm_dprintf(" = %d\n", hfunc); 640*0Sstevel@tonic-gate return hfunc; 641*0Sstevel@tonic-gate } 642*0Sstevel@tonic-gate /* 643*0Sstevel@tonic-gate ** BESTMX -- find the best MX for a name 644*0Sstevel@tonic-gate ** 645*0Sstevel@tonic-gate ** This is really a hack, but I don't see any obvious way 646*0Sstevel@tonic-gate ** to generalize it at the moment. 647*0Sstevel@tonic-gate */ 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gate /* ARGSUSED3 */ 650*0Sstevel@tonic-gate char * 651*0Sstevel@tonic-gate bestmx_map_lookup(map, name, av, statp) 652*0Sstevel@tonic-gate MAP *map; 653*0Sstevel@tonic-gate char *name; 654*0Sstevel@tonic-gate char **av; 655*0Sstevel@tonic-gate int *statp; 656*0Sstevel@tonic-gate { 657*0Sstevel@tonic-gate int nmx; 658*0Sstevel@tonic-gate int saveopts = _res.options; 659*0Sstevel@tonic-gate int i; 660*0Sstevel@tonic-gate ssize_t len = 0; 661*0Sstevel@tonic-gate char *result; 662*0Sstevel@tonic-gate char *mxhosts[MAXMXHOSTS + 1]; 663*0Sstevel@tonic-gate #if _FFR_BESTMX_BETTER_TRUNCATION 664*0Sstevel@tonic-gate char *buf; 665*0Sstevel@tonic-gate #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 666*0Sstevel@tonic-gate char *p; 667*0Sstevel@tonic-gate char buf[PSBUFSIZE / 2]; 668*0Sstevel@tonic-gate #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 671*0Sstevel@tonic-gate nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); 672*0Sstevel@tonic-gate _res.options = saveopts; 673*0Sstevel@tonic-gate if (nmx <= 0) 674*0Sstevel@tonic-gate return NULL; 675*0Sstevel@tonic-gate if (bitset(MF_MATCHONLY, map->map_mflags)) 676*0Sstevel@tonic-gate return map_rewrite(map, name, strlen(name), NULL); 677*0Sstevel@tonic-gate if ((map->map_coldelim == '\0') || (nmx == 1)) 678*0Sstevel@tonic-gate return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 679*0Sstevel@tonic-gate 680*0Sstevel@tonic-gate /* 681*0Sstevel@tonic-gate ** We were given a -z flag (return all MXs) and there are multiple 682*0Sstevel@tonic-gate ** ones. We need to build them all into a list. 683*0Sstevel@tonic-gate */ 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate #if _FFR_BESTMX_BETTER_TRUNCATION 686*0Sstevel@tonic-gate for (i = 0; i < nmx; i++) 687*0Sstevel@tonic-gate { 688*0Sstevel@tonic-gate if (strchr(mxhosts[i], map->map_coldelim) != NULL) 689*0Sstevel@tonic-gate { 690*0Sstevel@tonic-gate syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 691*0Sstevel@tonic-gate mxhosts[i], map->map_coldelim); 692*0Sstevel@tonic-gate return NULL; 693*0Sstevel@tonic-gate } 694*0Sstevel@tonic-gate len += strlen(mxhosts[i]) + 1; 695*0Sstevel@tonic-gate if (len < 0) 696*0Sstevel@tonic-gate { 697*0Sstevel@tonic-gate len -= strlen(mxhosts[i]) + 1; 698*0Sstevel@tonic-gate break; 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate buf = (char *) sm_malloc(len); 702*0Sstevel@tonic-gate if (buf == NULL) 703*0Sstevel@tonic-gate { 704*0Sstevel@tonic-gate *statp = EX_UNAVAILABLE; 705*0Sstevel@tonic-gate return NULL; 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate *buf = '\0'; 708*0Sstevel@tonic-gate for (i = 0; i < nmx; i++) 709*0Sstevel@tonic-gate { 710*0Sstevel@tonic-gate int end; 711*0Sstevel@tonic-gate 712*0Sstevel@tonic-gate end = sm_strlcat(buf, mxhosts[i], len); 713*0Sstevel@tonic-gate if (i != nmx && end + 1 < len) 714*0Sstevel@tonic-gate { 715*0Sstevel@tonic-gate buf[end] = map->map_coldelim; 716*0Sstevel@tonic-gate buf[end + 1] = '\0'; 717*0Sstevel@tonic-gate } 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate /* Cleanly truncate for rulesets */ 721*0Sstevel@tonic-gate truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 722*0Sstevel@tonic-gate #else /* _FFR_BESTMX_BETTER_TRUNCATION */ 723*0Sstevel@tonic-gate p = buf; 724*0Sstevel@tonic-gate for (i = 0; i < nmx; i++) 725*0Sstevel@tonic-gate { 726*0Sstevel@tonic-gate size_t slen; 727*0Sstevel@tonic-gate 728*0Sstevel@tonic-gate if (strchr(mxhosts[i], map->map_coldelim) != NULL) 729*0Sstevel@tonic-gate { 730*0Sstevel@tonic-gate syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 731*0Sstevel@tonic-gate mxhosts[i], map->map_coldelim); 732*0Sstevel@tonic-gate return NULL; 733*0Sstevel@tonic-gate } 734*0Sstevel@tonic-gate slen = strlen(mxhosts[i]); 735*0Sstevel@tonic-gate if (len + slen + 2 > sizeof buf) 736*0Sstevel@tonic-gate break; 737*0Sstevel@tonic-gate if (i > 0) 738*0Sstevel@tonic-gate { 739*0Sstevel@tonic-gate *p++ = map->map_coldelim; 740*0Sstevel@tonic-gate len++; 741*0Sstevel@tonic-gate } 742*0Sstevel@tonic-gate (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len); 743*0Sstevel@tonic-gate p += slen; 744*0Sstevel@tonic-gate len += slen; 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 747*0Sstevel@tonic-gate 748*0Sstevel@tonic-gate result = map_rewrite(map, buf, len, av); 749*0Sstevel@tonic-gate #if _FFR_BESTMX_BETTER_TRUNCATION 750*0Sstevel@tonic-gate sm_free(buf); 751*0Sstevel@tonic-gate #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 752*0Sstevel@tonic-gate return result; 753*0Sstevel@tonic-gate } 754*0Sstevel@tonic-gate /* 755*0Sstevel@tonic-gate ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 756*0Sstevel@tonic-gate ** 757*0Sstevel@tonic-gate ** This algorithm tries to be smart about wildcard MX records. 758*0Sstevel@tonic-gate ** This is hard to do because DNS doesn't tell is if we matched 759*0Sstevel@tonic-gate ** against a wildcard or a specific MX. 760*0Sstevel@tonic-gate ** 761*0Sstevel@tonic-gate ** We always prefer A & CNAME records, since these are presumed 762*0Sstevel@tonic-gate ** to be specific. 763*0Sstevel@tonic-gate ** 764*0Sstevel@tonic-gate ** If we match an MX in one pass and lose it in the next, we use 765*0Sstevel@tonic-gate ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 766*0Sstevel@tonic-gate ** A hostname bletch.foo.bar.com will match against this MX, but 767*0Sstevel@tonic-gate ** will stop matching when we try bletch.bar.com -- so we know 768*0Sstevel@tonic-gate ** that bletch.foo.bar.com must have been right. This fails if 769*0Sstevel@tonic-gate ** there was also an MX record matching *.BAR.COM, but there are 770*0Sstevel@tonic-gate ** some things that just can't be fixed. 771*0Sstevel@tonic-gate ** 772*0Sstevel@tonic-gate ** Parameters: 773*0Sstevel@tonic-gate ** host -- a buffer containing the name of the host. 774*0Sstevel@tonic-gate ** This is a value-result parameter. 775*0Sstevel@tonic-gate ** hbsize -- the size of the host buffer. 776*0Sstevel@tonic-gate ** trymx -- if set, try MX records as well as A and CNAME. 777*0Sstevel@tonic-gate ** statp -- pointer to place to store status. 778*0Sstevel@tonic-gate ** pttl -- pointer to return TTL (can be NULL). 779*0Sstevel@tonic-gate ** 780*0Sstevel@tonic-gate ** Returns: 781*0Sstevel@tonic-gate ** true -- if the host matched. 782*0Sstevel@tonic-gate ** false -- otherwise. 783*0Sstevel@tonic-gate */ 784*0Sstevel@tonic-gate 785*0Sstevel@tonic-gate bool 786*0Sstevel@tonic-gate dns_getcanonname(host, hbsize, trymx, statp, pttl) 787*0Sstevel@tonic-gate char *host; 788*0Sstevel@tonic-gate int hbsize; 789*0Sstevel@tonic-gate bool trymx; 790*0Sstevel@tonic-gate int *statp; 791*0Sstevel@tonic-gate int *pttl; 792*0Sstevel@tonic-gate { 793*0Sstevel@tonic-gate register unsigned char *eom, *ap; 794*0Sstevel@tonic-gate register char *cp; 795*0Sstevel@tonic-gate register int n; 796*0Sstevel@tonic-gate HEADER *hp; 797*0Sstevel@tonic-gate querybuf answer; 798*0Sstevel@tonic-gate int ancount, qdcount; 799*0Sstevel@tonic-gate int ret; 800*0Sstevel@tonic-gate char **domain; 801*0Sstevel@tonic-gate int type; 802*0Sstevel@tonic-gate int ttl = 0; 803*0Sstevel@tonic-gate char **dp; 804*0Sstevel@tonic-gate char *mxmatch; 805*0Sstevel@tonic-gate bool amatch; 806*0Sstevel@tonic-gate bool gotmx = false; 807*0Sstevel@tonic-gate int qtype; 808*0Sstevel@tonic-gate int initial; 809*0Sstevel@tonic-gate int loopcnt; 810*0Sstevel@tonic-gate char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 811*0Sstevel@tonic-gate char *searchlist[MAXDNSRCH + 2]; 812*0Sstevel@tonic-gate 813*0Sstevel@tonic-gate if (tTd(8, 2)) 814*0Sstevel@tonic-gate sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 815*0Sstevel@tonic-gate 816*0Sstevel@tonic-gate if ((_res.options & RES_INIT) == 0 && res_init() == -1) 817*0Sstevel@tonic-gate { 818*0Sstevel@tonic-gate *statp = EX_UNAVAILABLE; 819*0Sstevel@tonic-gate return false; 820*0Sstevel@tonic-gate } 821*0Sstevel@tonic-gate 822*0Sstevel@tonic-gate *statp = EX_OK; 823*0Sstevel@tonic-gate 824*0Sstevel@tonic-gate /* 825*0Sstevel@tonic-gate ** Initialize domain search list. If there is at least one 826*0Sstevel@tonic-gate ** dot in the name, search the unmodified name first so we 827*0Sstevel@tonic-gate ** find "vse.CS" in Czechoslovakia instead of in the local 828*0Sstevel@tonic-gate ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 829*0Sstevel@tonic-gate ** longer a country named Czechoslovakia but this type of problem 830*0Sstevel@tonic-gate ** is still present. 831*0Sstevel@tonic-gate ** 832*0Sstevel@tonic-gate ** Older versions of the resolver could create this 833*0Sstevel@tonic-gate ** list by tearing apart the host name. 834*0Sstevel@tonic-gate */ 835*0Sstevel@tonic-gate 836*0Sstevel@tonic-gate loopcnt = 0; 837*0Sstevel@tonic-gate cnameloop: 838*0Sstevel@tonic-gate /* Check for dots in the name */ 839*0Sstevel@tonic-gate for (cp = host, n = 0; *cp != '\0'; cp++) 840*0Sstevel@tonic-gate if (*cp == '.') 841*0Sstevel@tonic-gate n++; 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate /* 844*0Sstevel@tonic-gate ** Build the search list. 845*0Sstevel@tonic-gate ** If there is at least one dot in name, start with a null 846*0Sstevel@tonic-gate ** domain to search the unmodified name first. 847*0Sstevel@tonic-gate ** If name does not end with a dot and search up local domain 848*0Sstevel@tonic-gate ** tree desired, append each local domain component to the 849*0Sstevel@tonic-gate ** search list; if name contains no dots and default domain 850*0Sstevel@tonic-gate ** name is desired, append default domain name to search list; 851*0Sstevel@tonic-gate ** else if name ends in a dot, remove that dot. 852*0Sstevel@tonic-gate */ 853*0Sstevel@tonic-gate 854*0Sstevel@tonic-gate dp = searchlist; 855*0Sstevel@tonic-gate if (n > 0) 856*0Sstevel@tonic-gate *dp++ = ""; 857*0Sstevel@tonic-gate if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 858*0Sstevel@tonic-gate { 859*0Sstevel@tonic-gate /* make sure there are less than MAXDNSRCH domains */ 860*0Sstevel@tonic-gate for (domain = RES_DNSRCH_VARIABLE, ret = 0; 861*0Sstevel@tonic-gate *domain != NULL && ret < MAXDNSRCH; 862*0Sstevel@tonic-gate ret++) 863*0Sstevel@tonic-gate *dp++ = *domain++; 864*0Sstevel@tonic-gate } 865*0Sstevel@tonic-gate else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 866*0Sstevel@tonic-gate { 867*0Sstevel@tonic-gate *dp++ = _res.defdname; 868*0Sstevel@tonic-gate } 869*0Sstevel@tonic-gate else if (*cp == '.') 870*0Sstevel@tonic-gate { 871*0Sstevel@tonic-gate *cp = '\0'; 872*0Sstevel@tonic-gate } 873*0Sstevel@tonic-gate *dp = NULL; 874*0Sstevel@tonic-gate 875*0Sstevel@tonic-gate /* 876*0Sstevel@tonic-gate ** Now loop through the search list, appending each domain in turn 877*0Sstevel@tonic-gate ** name and searching for a match. 878*0Sstevel@tonic-gate */ 879*0Sstevel@tonic-gate 880*0Sstevel@tonic-gate mxmatch = NULL; 881*0Sstevel@tonic-gate initial = T_A; 882*0Sstevel@tonic-gate # if NETINET6 883*0Sstevel@tonic-gate if (InetMode == AF_INET6) 884*0Sstevel@tonic-gate initial = T_AAAA; 885*0Sstevel@tonic-gate # endif /* NETINET6 */ 886*0Sstevel@tonic-gate qtype = initial; 887*0Sstevel@tonic-gate 888*0Sstevel@tonic-gate for (dp = searchlist; *dp != NULL; ) 889*0Sstevel@tonic-gate { 890*0Sstevel@tonic-gate if (qtype == initial) 891*0Sstevel@tonic-gate gotmx = false; 892*0Sstevel@tonic-gate if (tTd(8, 5)) 893*0Sstevel@tonic-gate sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 894*0Sstevel@tonic-gate host, *dp, 895*0Sstevel@tonic-gate # if NETINET6 896*0Sstevel@tonic-gate qtype == T_AAAA ? "AAAA" : 897*0Sstevel@tonic-gate # endif /* NETINET6 */ 898*0Sstevel@tonic-gate qtype == T_A ? "A" : 899*0Sstevel@tonic-gate qtype == T_MX ? "MX" : 900*0Sstevel@tonic-gate "???"); 901*0Sstevel@tonic-gate errno = 0; 902*0Sstevel@tonic-gate ret = res_querydomain(host, *dp, C_IN, qtype, 903*0Sstevel@tonic-gate answer.qb2, sizeof(answer.qb2)); 904*0Sstevel@tonic-gate if (ret <= 0) 905*0Sstevel@tonic-gate { 906*0Sstevel@tonic-gate int save_errno = errno; 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate if (tTd(8, 7)) 909*0Sstevel@tonic-gate sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 910*0Sstevel@tonic-gate save_errno, h_errno); 911*0Sstevel@tonic-gate 912*0Sstevel@tonic-gate if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 913*0Sstevel@tonic-gate { 914*0Sstevel@tonic-gate /* 915*0Sstevel@tonic-gate ** the name server seems to be down or broken. 916*0Sstevel@tonic-gate */ 917*0Sstevel@tonic-gate 918*0Sstevel@tonic-gate SM_SET_H_ERRNO(TRY_AGAIN); 919*0Sstevel@tonic-gate if (**dp == '\0') 920*0Sstevel@tonic-gate { 921*0Sstevel@tonic-gate if (*statp == EX_OK) 922*0Sstevel@tonic-gate *statp = EX_TEMPFAIL; 923*0Sstevel@tonic-gate goto nexttype; 924*0Sstevel@tonic-gate } 925*0Sstevel@tonic-gate *statp = EX_TEMPFAIL; 926*0Sstevel@tonic-gate 927*0Sstevel@tonic-gate if (WorkAroundBrokenAAAA) 928*0Sstevel@tonic-gate { 929*0Sstevel@tonic-gate /* 930*0Sstevel@tonic-gate ** Only return if not TRY_AGAIN as an 931*0Sstevel@tonic-gate ** attempt with a different qtype may 932*0Sstevel@tonic-gate ** succeed (res_querydomain() calls 933*0Sstevel@tonic-gate ** res_query() calls res_send() which 934*0Sstevel@tonic-gate ** sets errno to ETIMEDOUT if the 935*0Sstevel@tonic-gate ** nameservers could be contacted but 936*0Sstevel@tonic-gate ** didn't give an answer). 937*0Sstevel@tonic-gate */ 938*0Sstevel@tonic-gate 939*0Sstevel@tonic-gate if (save_errno != ETIMEDOUT) 940*0Sstevel@tonic-gate return false; 941*0Sstevel@tonic-gate } 942*0Sstevel@tonic-gate else 943*0Sstevel@tonic-gate return false; 944*0Sstevel@tonic-gate } 945*0Sstevel@tonic-gate 946*0Sstevel@tonic-gate nexttype: 947*0Sstevel@tonic-gate if (h_errno != HOST_NOT_FOUND) 948*0Sstevel@tonic-gate { 949*0Sstevel@tonic-gate /* might have another type of interest */ 950*0Sstevel@tonic-gate # if NETINET6 951*0Sstevel@tonic-gate if (qtype == T_AAAA) 952*0Sstevel@tonic-gate { 953*0Sstevel@tonic-gate qtype = T_A; 954*0Sstevel@tonic-gate continue; 955*0Sstevel@tonic-gate } 956*0Sstevel@tonic-gate else 957*0Sstevel@tonic-gate # endif /* NETINET6 */ 958*0Sstevel@tonic-gate if (qtype == T_A && !gotmx && 959*0Sstevel@tonic-gate (trymx || **dp == '\0')) 960*0Sstevel@tonic-gate { 961*0Sstevel@tonic-gate qtype = T_MX; 962*0Sstevel@tonic-gate continue; 963*0Sstevel@tonic-gate } 964*0Sstevel@tonic-gate } 965*0Sstevel@tonic-gate 966*0Sstevel@tonic-gate /* definite no -- try the next domain */ 967*0Sstevel@tonic-gate dp++; 968*0Sstevel@tonic-gate qtype = initial; 969*0Sstevel@tonic-gate continue; 970*0Sstevel@tonic-gate } 971*0Sstevel@tonic-gate else if (tTd(8, 7)) 972*0Sstevel@tonic-gate sm_dprintf("\tYES\n"); 973*0Sstevel@tonic-gate 974*0Sstevel@tonic-gate /* avoid problems after truncation in tcp packets */ 975*0Sstevel@tonic-gate if (ret > sizeof(answer)) 976*0Sstevel@tonic-gate ret = sizeof(answer); 977*0Sstevel@tonic-gate if (ret < 0) 978*0Sstevel@tonic-gate { 979*0Sstevel@tonic-gate *statp = EX_SOFTWARE; 980*0Sstevel@tonic-gate return false; 981*0Sstevel@tonic-gate } 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate /* 984*0Sstevel@tonic-gate ** Appear to have a match. Confirm it by searching for A or 985*0Sstevel@tonic-gate ** CNAME records. If we don't have a local domain 986*0Sstevel@tonic-gate ** wild card MX record, we will accept MX as well. 987*0Sstevel@tonic-gate */ 988*0Sstevel@tonic-gate 989*0Sstevel@tonic-gate hp = (HEADER *) &answer; 990*0Sstevel@tonic-gate ap = (unsigned char *) &answer + HFIXEDSZ; 991*0Sstevel@tonic-gate eom = (unsigned char *) &answer + ret; 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate /* skip question part of response -- we know what we asked */ 994*0Sstevel@tonic-gate for (qdcount = ntohs((unsigned short) hp->qdcount); 995*0Sstevel@tonic-gate qdcount--; 996*0Sstevel@tonic-gate ap += ret + QFIXEDSZ) 997*0Sstevel@tonic-gate { 998*0Sstevel@tonic-gate if ((ret = dn_skipname(ap, eom)) < 0) 999*0Sstevel@tonic-gate { 1000*0Sstevel@tonic-gate if (tTd(8, 20)) 1001*0Sstevel@tonic-gate sm_dprintf("qdcount failure (%d)\n", 1002*0Sstevel@tonic-gate ntohs((unsigned short) hp->qdcount)); 1003*0Sstevel@tonic-gate *statp = EX_SOFTWARE; 1004*0Sstevel@tonic-gate return false; /* ???XXX??? */ 1005*0Sstevel@tonic-gate } 1006*0Sstevel@tonic-gate } 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate amatch = false; 1009*0Sstevel@tonic-gate for (ancount = ntohs((unsigned short) hp->ancount); 1010*0Sstevel@tonic-gate --ancount >= 0 && ap < eom; 1011*0Sstevel@tonic-gate ap += n) 1012*0Sstevel@tonic-gate { 1013*0Sstevel@tonic-gate n = dn_expand((unsigned char *) &answer, eom, ap, 1014*0Sstevel@tonic-gate (RES_UNC_T) nbuf, sizeof nbuf); 1015*0Sstevel@tonic-gate if (n < 0) 1016*0Sstevel@tonic-gate break; 1017*0Sstevel@tonic-gate ap += n; 1018*0Sstevel@tonic-gate GETSHORT(type, ap); 1019*0Sstevel@tonic-gate ap += INT16SZ; /* skip over class */ 1020*0Sstevel@tonic-gate GETLONG(ttl, ap); 1021*0Sstevel@tonic-gate GETSHORT(n, ap); /* rdlength */ 1022*0Sstevel@tonic-gate switch (type) 1023*0Sstevel@tonic-gate { 1024*0Sstevel@tonic-gate case T_MX: 1025*0Sstevel@tonic-gate gotmx = true; 1026*0Sstevel@tonic-gate if (**dp != '\0' && HasWildcardMX) 1027*0Sstevel@tonic-gate { 1028*0Sstevel@tonic-gate /* 1029*0Sstevel@tonic-gate ** If we are using MX matches and have 1030*0Sstevel@tonic-gate ** not yet gotten one, save this one 1031*0Sstevel@tonic-gate ** but keep searching for an A or 1032*0Sstevel@tonic-gate ** CNAME match. 1033*0Sstevel@tonic-gate */ 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate if (trymx && mxmatch == NULL) 1036*0Sstevel@tonic-gate mxmatch = *dp; 1037*0Sstevel@tonic-gate continue; 1038*0Sstevel@tonic-gate } 1039*0Sstevel@tonic-gate 1040*0Sstevel@tonic-gate /* 1041*0Sstevel@tonic-gate ** If we did not append a domain name, this 1042*0Sstevel@tonic-gate ** must have been a canonical name to start 1043*0Sstevel@tonic-gate ** with. Even if we did append a domain name, 1044*0Sstevel@tonic-gate ** in the absence of a wildcard MX this must 1045*0Sstevel@tonic-gate ** still be a real MX match. 1046*0Sstevel@tonic-gate ** Such MX matches are as good as an A match, 1047*0Sstevel@tonic-gate ** fall through. 1048*0Sstevel@tonic-gate */ 1049*0Sstevel@tonic-gate /* FALLTHROUGH */ 1050*0Sstevel@tonic-gate 1051*0Sstevel@tonic-gate # if NETINET6 1052*0Sstevel@tonic-gate case T_AAAA: 1053*0Sstevel@tonic-gate # endif /* NETINET6 */ 1054*0Sstevel@tonic-gate case T_A: 1055*0Sstevel@tonic-gate /* Flag that a good match was found */ 1056*0Sstevel@tonic-gate amatch = true; 1057*0Sstevel@tonic-gate 1058*0Sstevel@tonic-gate /* continue in case a CNAME also exists */ 1059*0Sstevel@tonic-gate continue; 1060*0Sstevel@tonic-gate 1061*0Sstevel@tonic-gate case T_CNAME: 1062*0Sstevel@tonic-gate if (DontExpandCnames) 1063*0Sstevel@tonic-gate { 1064*0Sstevel@tonic-gate /* got CNAME -- guaranteed canonical */ 1065*0Sstevel@tonic-gate amatch = true; 1066*0Sstevel@tonic-gate break; 1067*0Sstevel@tonic-gate } 1068*0Sstevel@tonic-gate 1069*0Sstevel@tonic-gate if (loopcnt++ > MAXCNAMEDEPTH) 1070*0Sstevel@tonic-gate { 1071*0Sstevel@tonic-gate /*XXX should notify postmaster XXX*/ 1072*0Sstevel@tonic-gate message("DNS failure: CNAME loop for %s", 1073*0Sstevel@tonic-gate host); 1074*0Sstevel@tonic-gate if (CurEnv->e_message == NULL) 1075*0Sstevel@tonic-gate { 1076*0Sstevel@tonic-gate char ebuf[MAXLINE]; 1077*0Sstevel@tonic-gate 1078*0Sstevel@tonic-gate (void) sm_snprintf(ebuf, 1079*0Sstevel@tonic-gate sizeof ebuf, 1080*0Sstevel@tonic-gate "Deferred: DNS failure: CNAME loop for %.100s", 1081*0Sstevel@tonic-gate host); 1082*0Sstevel@tonic-gate CurEnv->e_message = 1083*0Sstevel@tonic-gate sm_rpool_strdup_x( 1084*0Sstevel@tonic-gate CurEnv->e_rpool, ebuf); 1085*0Sstevel@tonic-gate } 1086*0Sstevel@tonic-gate SM_SET_H_ERRNO(NO_RECOVERY); 1087*0Sstevel@tonic-gate *statp = EX_CONFIG; 1088*0Sstevel@tonic-gate return false; 1089*0Sstevel@tonic-gate } 1090*0Sstevel@tonic-gate 1091*0Sstevel@tonic-gate /* value points at name */ 1092*0Sstevel@tonic-gate if ((ret = dn_expand((unsigned char *)&answer, 1093*0Sstevel@tonic-gate eom, ap, (RES_UNC_T) nbuf, 1094*0Sstevel@tonic-gate sizeof(nbuf))) < 0) 1095*0Sstevel@tonic-gate break; 1096*0Sstevel@tonic-gate (void) sm_strlcpy(host, nbuf, hbsize); 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate /* 1099*0Sstevel@tonic-gate ** RFC 1034 section 3.6 specifies that CNAME 1100*0Sstevel@tonic-gate ** should point at the canonical name -- but 1101*0Sstevel@tonic-gate ** urges software to try again anyway. 1102*0Sstevel@tonic-gate */ 1103*0Sstevel@tonic-gate 1104*0Sstevel@tonic-gate goto cnameloop; 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate default: 1107*0Sstevel@tonic-gate /* not a record of interest */ 1108*0Sstevel@tonic-gate continue; 1109*0Sstevel@tonic-gate } 1110*0Sstevel@tonic-gate } 1111*0Sstevel@tonic-gate 1112*0Sstevel@tonic-gate if (amatch) 1113*0Sstevel@tonic-gate { 1114*0Sstevel@tonic-gate /* 1115*0Sstevel@tonic-gate ** Got a good match -- either an A, CNAME, or an 1116*0Sstevel@tonic-gate ** exact MX record. Save it and get out of here. 1117*0Sstevel@tonic-gate */ 1118*0Sstevel@tonic-gate 1119*0Sstevel@tonic-gate mxmatch = *dp; 1120*0Sstevel@tonic-gate break; 1121*0Sstevel@tonic-gate } 1122*0Sstevel@tonic-gate 1123*0Sstevel@tonic-gate /* 1124*0Sstevel@tonic-gate ** Nothing definitive yet. 1125*0Sstevel@tonic-gate ** If this was a T_A query and we haven't yet found a MX 1126*0Sstevel@tonic-gate ** match, try T_MX if allowed to do so. 1127*0Sstevel@tonic-gate ** Otherwise, try the next domain. 1128*0Sstevel@tonic-gate */ 1129*0Sstevel@tonic-gate 1130*0Sstevel@tonic-gate # if NETINET6 1131*0Sstevel@tonic-gate if (qtype == T_AAAA) 1132*0Sstevel@tonic-gate qtype = T_A; 1133*0Sstevel@tonic-gate else 1134*0Sstevel@tonic-gate # endif /* NETINET6 */ 1135*0Sstevel@tonic-gate if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) 1136*0Sstevel@tonic-gate qtype = T_MX; 1137*0Sstevel@tonic-gate else 1138*0Sstevel@tonic-gate { 1139*0Sstevel@tonic-gate qtype = initial; 1140*0Sstevel@tonic-gate dp++; 1141*0Sstevel@tonic-gate } 1142*0Sstevel@tonic-gate } 1143*0Sstevel@tonic-gate 1144*0Sstevel@tonic-gate /* if nothing was found, we are done */ 1145*0Sstevel@tonic-gate if (mxmatch == NULL) 1146*0Sstevel@tonic-gate { 1147*0Sstevel@tonic-gate if (*statp == EX_OK) 1148*0Sstevel@tonic-gate *statp = EX_NOHOST; 1149*0Sstevel@tonic-gate return false; 1150*0Sstevel@tonic-gate } 1151*0Sstevel@tonic-gate 1152*0Sstevel@tonic-gate /* 1153*0Sstevel@tonic-gate ** Create canonical name and return. 1154*0Sstevel@tonic-gate ** If saved domain name is null, name was already canonical. 1155*0Sstevel@tonic-gate ** Otherwise append the saved domain name. 1156*0Sstevel@tonic-gate */ 1157*0Sstevel@tonic-gate 1158*0Sstevel@tonic-gate (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, 1159*0Sstevel@tonic-gate *mxmatch == '\0' ? "" : ".", 1160*0Sstevel@tonic-gate MAXDNAME, mxmatch); 1161*0Sstevel@tonic-gate (void) sm_strlcpy(host, nbuf, hbsize); 1162*0Sstevel@tonic-gate if (tTd(8, 5)) 1163*0Sstevel@tonic-gate sm_dprintf("dns_getcanonname: %s\n", host); 1164*0Sstevel@tonic-gate *statp = EX_OK; 1165*0Sstevel@tonic-gate 1166*0Sstevel@tonic-gate /* return only one TTL entry, that should be sufficient */ 1167*0Sstevel@tonic-gate if (ttl > 0 && pttl != NULL) 1168*0Sstevel@tonic-gate *pttl = ttl; 1169*0Sstevel@tonic-gate return true; 1170*0Sstevel@tonic-gate } 1171*0Sstevel@tonic-gate #endif /* NAMED_BIND */ 1172