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