1c2aa98e2SPeter Wemm /* 25dd76dd0SGregory Neil Shapiro * Copyright (c) 1998-2004, 2006, 2010 Proofpoint, Inc. and its suppliers. 306f25ae9SGregory Neil Shapiro * All rights reserved. 4c2aa98e2SPeter Wemm * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 5c2aa98e2SPeter Wemm * Copyright (c) 1988, 1993 6c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 7c2aa98e2SPeter Wemm * 8c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set 9c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of 10c2aa98e2SPeter Wemm * the sendmail distribution. 11c2aa98e2SPeter Wemm * 12c2aa98e2SPeter Wemm */ 13c2aa98e2SPeter Wemm 1406f25ae9SGregory Neil Shapiro #include <sendmail.h> 15d0cef73dSGregory Neil Shapiro #include "map.h" 16*5b0945b5SGregory Neil Shapiro #if _FFR_EAI 17*5b0945b5SGregory Neil Shapiro #include <unicode/uidna.h> 18*5b0945b5SGregory Neil Shapiro #endif 19c2aa98e2SPeter Wemm 20c2aa98e2SPeter Wemm #if NAMED_BIND 214313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name server)") 22*5b0945b5SGregory Neil Shapiro #else 234313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)") 24*5b0945b5SGregory Neil Shapiro #endif 25c2aa98e2SPeter Wemm 26c2aa98e2SPeter Wemm #if NAMED_BIND 27c2aa98e2SPeter Wemm 28c2aa98e2SPeter Wemm # include <arpa/inet.h> 29*5b0945b5SGregory Neil Shapiro # include <sm_resolve.h> 30*5b0945b5SGregory Neil Shapiro # if DANE 31*5b0945b5SGregory Neil Shapiro # include <tls.h> 32*5b0945b5SGregory Neil Shapiro # ifndef SM_NEG_TTL 33*5b0945b5SGregory Neil Shapiro # define SM_NEG_TTL 60 /* "negative" TTL */ 34*5b0945b5SGregory Neil Shapiro # endif 35*5b0945b5SGregory Neil Shapiro # endif 36c2aa98e2SPeter Wemm 3740266059SGregory Neil Shapiro 38c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE 39c2aa98e2SPeter Wemm # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 40*5b0945b5SGregory Neil Shapiro # endif 41c2aa98e2SPeter Wemm 42c2aa98e2SPeter Wemm static char MXHostBuf[MXHOSTBUFSIZE]; 4340266059SGregory Neil Shapiro # if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 4440266059SGregory Neil Shapiro ERROR: _MXHOSTBUFSIZE is out of range 45*5b0945b5SGregory Neil Shapiro # endif 46c2aa98e2SPeter Wemm 47c2aa98e2SPeter Wemm # ifndef MAXDNSRCH 48c2aa98e2SPeter Wemm # define MAXDNSRCH 6 /* number of possible domains to search */ 49*5b0945b5SGregory Neil Shapiro # endif 5006f25ae9SGregory Neil Shapiro 5106f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE 5206f25ae9SGregory Neil Shapiro # define RES_DNSRCH_VARIABLE _res.dnsrch 53*5b0945b5SGregory Neil Shapiro # endif 54c2aa98e2SPeter Wemm 55c2aa98e2SPeter Wemm # ifndef NO_DATA 56c2aa98e2SPeter Wemm # define NO_DATA NO_ADDRESS 57*5b0945b5SGregory Neil Shapiro # endif 58c2aa98e2SPeter Wemm 59c2aa98e2SPeter Wemm # ifndef HFIXEDSZ 60c2aa98e2SPeter Wemm # define HFIXEDSZ 12 /* sizeof(HEADER) */ 61*5b0945b5SGregory Neil Shapiro # endif 62c2aa98e2SPeter Wemm 63c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 64c2aa98e2SPeter Wemm 65c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415) 66c2aa98e2SPeter Wemm # define RES_UNC_T char * 67*5b0945b5SGregory Neil Shapiro # else 6840266059SGregory Neil Shapiro # define RES_UNC_T unsigned char * 69*5b0945b5SGregory Neil Shapiro # endif 7006f25ae9SGregory Neil Shapiro 7106f25ae9SGregory Neil Shapiro static int mxrand __P((char *)); 7240266059SGregory Neil Shapiro static int fallbackmxrr __P((int, unsigned short *, char **)); 7306f25ae9SGregory Neil Shapiro 74*5b0945b5SGregory Neil Shapiro # if DANE 75*5b0945b5SGregory Neil Shapiro 76*5b0945b5SGregory Neil Shapiro /* 77*5b0945b5SGregory Neil Shapiro ** TLSAADD -- add TLSA records to dane_tlsa entry 78*5b0945b5SGregory Neil Shapiro ** 79*5b0945b5SGregory Neil Shapiro ** Parameters: 80*5b0945b5SGregory Neil Shapiro ** name -- key for stab entry (for debugging output) 81*5b0945b5SGregory Neil Shapiro ** dr -- DNS reply 82*5b0945b5SGregory Neil Shapiro ** dane_tlsa -- dane_tlsa entry 83*5b0945b5SGregory Neil Shapiro ** dnsrc -- DNS lookup return code (h_errno) 84*5b0945b5SGregory Neil Shapiro ** n -- current number of TLSA records in dane_tlsa entry 85*5b0945b5SGregory Neil Shapiro ** pttl -- (pointer to) TTL (in/out) 86*5b0945b5SGregory Neil Shapiro ** level -- recursion level (CNAMEs) 87*5b0945b5SGregory Neil Shapiro ** 88*5b0945b5SGregory Neil Shapiro ** Returns: 89*5b0945b5SGregory Neil Shapiro ** new number of TLSA records 90*5b0945b5SGregory Neil Shapiro */ 91*5b0945b5SGregory Neil Shapiro 92*5b0945b5SGregory Neil Shapiro static int tlsaadd __P((const char *, DNS_REPLY_T *, dane_tlsa_P, int, int, 93*5b0945b5SGregory Neil Shapiro unsigned int *, int)); 94*5b0945b5SGregory Neil Shapiro 95*5b0945b5SGregory Neil Shapiro static int 96*5b0945b5SGregory Neil Shapiro tlsaadd(name, dr, dane_tlsa, dnsrc, n, pttl, level) 97*5b0945b5SGregory Neil Shapiro const char *name; 98*5b0945b5SGregory Neil Shapiro DNS_REPLY_T *dr; 99*5b0945b5SGregory Neil Shapiro dane_tlsa_P dane_tlsa; 100*5b0945b5SGregory Neil Shapiro int dnsrc; 101*5b0945b5SGregory Neil Shapiro int n; 102*5b0945b5SGregory Neil Shapiro unsigned int *pttl; 103*5b0945b5SGregory Neil Shapiro int level; 104*5b0945b5SGregory Neil Shapiro { 105*5b0945b5SGregory Neil Shapiro RESOURCE_RECORD_T *rr; 106*5b0945b5SGregory Neil Shapiro unsigned int ttl; 107*5b0945b5SGregory Neil Shapiro int nprev; 108*5b0945b5SGregory Neil Shapiro 109*5b0945b5SGregory Neil Shapiro if (dnsrc != 0) 110*5b0945b5SGregory Neil Shapiro { 111*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 112*5b0945b5SGregory Neil Shapiro sm_dprintf("tlsaadd(%s), prev=%d, dnsrc=%d\n", 113*5b0945b5SGregory Neil Shapiro name, dane_tlsa->dane_tlsa_dnsrc, dnsrc); 114*5b0945b5SGregory Neil Shapiro 115*5b0945b5SGregory Neil Shapiro /* check previous error and keep the "most important" one? */ 116*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_dnsrc = dnsrc; 117*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 118*5b0945b5SGregory Neil Shapiro if (tTd(8, 110)) 119*5b0945b5SGregory Neil Shapiro *pttl = tTdlevel(8)-110; /* how to make this an option? */ 120*5b0945b5SGregory Neil Shapiro else 121*5b0945b5SGregory Neil Shapiro # else 122*5b0945b5SGregory Neil Shapiro *pttl = SM_NEG_TTL; 123*5b0945b5SGregory Neil Shapiro # endif 124*5b0945b5SGregory Neil Shapiro 125*5b0945b5SGregory Neil Shapiro return n; 126*5b0945b5SGregory Neil Shapiro } 127*5b0945b5SGregory Neil Shapiro if (dr == NULL) 128*5b0945b5SGregory Neil Shapiro return n; 129*5b0945b5SGregory Neil Shapiro if (dr->dns_r_h.ad != 1 && Dane == DANE_SECURE) /* not secure? */ 130*5b0945b5SGregory Neil Shapiro return n; 131*5b0945b5SGregory Neil Shapiro ttl = *pttl; 132*5b0945b5SGregory Neil Shapiro 133*5b0945b5SGregory Neil Shapiro /* first: try to find TLSA records */ 134*5b0945b5SGregory Neil Shapiro nprev = n; 135*5b0945b5SGregory Neil Shapiro for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR; 136*5b0945b5SGregory Neil Shapiro rr = rr->rr_next) 137*5b0945b5SGregory Neil Shapiro { 138*5b0945b5SGregory Neil Shapiro int tlsa_chk; 139*5b0945b5SGregory Neil Shapiro 140*5b0945b5SGregory Neil Shapiro if (rr->rr_type != T_TLSA) 141*5b0945b5SGregory Neil Shapiro { 142*5b0945b5SGregory Neil Shapiro if (rr->rr_type != T_CNAME && tTd(8, 8)) 143*5b0945b5SGregory Neil Shapiro sm_dprintf("tlsaadd(%s), type=%s\n", name, 144*5b0945b5SGregory Neil Shapiro dns_type_to_string(rr->rr_type)); 145*5b0945b5SGregory Neil Shapiro continue; 146*5b0945b5SGregory Neil Shapiro } 147*5b0945b5SGregory Neil Shapiro tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data, rr->rr_size, name, 148*5b0945b5SGregory Neil Shapiro true); 149*5b0945b5SGregory Neil Shapiro if (!TLSA_IS_VALID(tlsa_chk)) 150*5b0945b5SGregory Neil Shapiro continue; 151*5b0945b5SGregory Neil Shapiro 152*5b0945b5SGregory Neil Shapiro /* 153*5b0945b5SGregory Neil Shapiro ** To do: the RRs should be sorted (by "complexity") -- 154*5b0945b5SGregory Neil Shapiro ** when more than one type is supported. 155*5b0945b5SGregory Neil Shapiro */ 156*5b0945b5SGregory Neil Shapiro 157*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_rr[n] = rr->rr_u.rr_data; 158*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[n] = rr->rr_size; 159*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 160*5b0945b5SGregory Neil Shapiro { 161*5b0945b5SGregory Neil Shapiro unsigned char *p; 162*5b0945b5SGregory Neil Shapiro 163*5b0945b5SGregory Neil Shapiro p = rr->rr_u.rr_data; 164*5b0945b5SGregory Neil Shapiro sm_dprintf("tlsaadd(%s), n=%d, %d-%d-%d:%02x\n", name, 165*5b0945b5SGregory Neil Shapiro n, (int)p[0], (int)p[1], (int)p[2], (int)p[3]); 166*5b0945b5SGregory Neil Shapiro } 167*5b0945b5SGregory Neil Shapiro 168*5b0945b5SGregory Neil Shapiro /* require some minimum TTL? */ 169*5b0945b5SGregory Neil Shapiro if (ttl > rr->rr_ttl && rr->rr_ttl > 0) 170*5b0945b5SGregory Neil Shapiro ttl = rr->rr_ttl; 171*5b0945b5SGregory Neil Shapiro 172*5b0945b5SGregory Neil Shapiro /* hack: instead of copying the data, just "take it over" */ 173*5b0945b5SGregory Neil Shapiro rr->rr_u.rr_data = NULL; 174*5b0945b5SGregory Neil Shapiro ++n; 175*5b0945b5SGregory Neil Shapiro } 176*5b0945b5SGregory Neil Shapiro 177*5b0945b5SGregory Neil Shapiro /* second: check for CNAME records, but only if no TLSA RR was added */ 178*5b0945b5SGregory Neil Shapiro for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR && nprev == n; 179*5b0945b5SGregory Neil Shapiro rr = rr->rr_next) 180*5b0945b5SGregory Neil Shapiro { 181*5b0945b5SGregory Neil Shapiro DNS_REPLY_T *drc; 182*5b0945b5SGregory Neil Shapiro int err, herr; 183*5b0945b5SGregory Neil Shapiro 184*5b0945b5SGregory Neil Shapiro if (rr->rr_type != T_CNAME) 185*5b0945b5SGregory Neil Shapiro continue; 186*5b0945b5SGregory Neil Shapiro if (level > 1) 187*5b0945b5SGregory Neil Shapiro { 188*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 189*5b0945b5SGregory Neil Shapiro sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d\n", 190*5b0945b5SGregory Neil Shapiro name, rr->rr_u.rr_txt, level); 191*5b0945b5SGregory Neil Shapiro continue; 192*5b0945b5SGregory Neil Shapiro } 193*5b0945b5SGregory Neil Shapiro 194*5b0945b5SGregory Neil Shapiro drc = dns_lookup_int(rr->rr_u.rr_txt, C_IN, T_TLSA, 0, 0, 195*5b0945b5SGregory Neil Shapiro (Dane == DANE_SECURE && 196*5b0945b5SGregory Neil Shapiro !TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX)) 197*5b0945b5SGregory Neil Shapiro ? SM_RES_DNSSEC : 0, 198*5b0945b5SGregory Neil Shapiro RR_RAW, &err, &herr); 199*5b0945b5SGregory Neil Shapiro 200*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 201*5b0945b5SGregory Neil Shapiro sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d, dr=%p, ad=%d, err=%d, herr=%d\n", 202*5b0945b5SGregory Neil Shapiro name, rr->rr_u.rr_txt, level, 203*5b0945b5SGregory Neil Shapiro (void *)drc, drc != NULL ? drc->dns_r_h.ad : -1, 204*5b0945b5SGregory Neil Shapiro err, herr); 205*5b0945b5SGregory Neil Shapiro nprev = n = tlsaadd(name, drc, dane_tlsa, herr, n, pttl, 206*5b0945b5SGregory Neil Shapiro level + 1); 207*5b0945b5SGregory Neil Shapiro dns_free_data(drc); 208*5b0945b5SGregory Neil Shapiro drc = NULL; 209*5b0945b5SGregory Neil Shapiro } 210*5b0945b5SGregory Neil Shapiro 211*5b0945b5SGregory Neil Shapiro *pttl = ttl; 212*5b0945b5SGregory Neil Shapiro return n; 213*5b0945b5SGregory Neil Shapiro } 214*5b0945b5SGregory Neil Shapiro 215*5b0945b5SGregory Neil Shapiro /* 216*5b0945b5SGregory Neil Shapiro ** GETTLSA -- get TLSA records for named host using DNS 217*5b0945b5SGregory Neil Shapiro ** 218*5b0945b5SGregory Neil Shapiro ** Parameters: 219*5b0945b5SGregory Neil Shapiro ** host -- host 220*5b0945b5SGregory Neil Shapiro ** name -- name for stab entry key (if NULL: host) 221*5b0945b5SGregory Neil Shapiro ** pste -- (pointer to) stab entry (output) 222*5b0945b5SGregory Neil Shapiro ** flags -- TLSAFL* 223*5b0945b5SGregory Neil Shapiro ** mxttl -- TTL of MX (or host) 224*5b0945b5SGregory Neil Shapiro ** port -- port 225*5b0945b5SGregory Neil Shapiro ** 226*5b0945b5SGregory Neil Shapiro ** Returns: 227*5b0945b5SGregory Neil Shapiro ** The number of TLSA records found. 228*5b0945b5SGregory Neil Shapiro ** <0 if there is an internal failure. 229*5b0945b5SGregory Neil Shapiro ** 230*5b0945b5SGregory Neil Shapiro ** Side effects: 231*5b0945b5SGregory Neil Shapiro ** Enters TLSA RRs into stab(). 232*5b0945b5SGregory Neil Shapiro ** If the DNS lookup fails temporarily, an "empty" entry is 233*5b0945b5SGregory Neil Shapiro ** created with that DNS error code. 234*5b0945b5SGregory Neil Shapiro */ 235*5b0945b5SGregory Neil Shapiro 236*5b0945b5SGregory Neil Shapiro int 237*5b0945b5SGregory Neil Shapiro gettlsa(host, name, pste, flags, mxttl, port) 238*5b0945b5SGregory Neil Shapiro char *host; 239*5b0945b5SGregory Neil Shapiro char *name; 240*5b0945b5SGregory Neil Shapiro STAB **pste; 241*5b0945b5SGregory Neil Shapiro unsigned long flags; 242*5b0945b5SGregory Neil Shapiro unsigned int mxttl; 243*5b0945b5SGregory Neil Shapiro unsigned int port; 244*5b0945b5SGregory Neil Shapiro { 245*5b0945b5SGregory Neil Shapiro DNS_REPLY_T *dr; 246*5b0945b5SGregory Neil Shapiro dane_tlsa_P dane_tlsa; 247*5b0945b5SGregory Neil Shapiro STAB *ste; 248*5b0945b5SGregory Neil Shapiro time_t now; 249*5b0945b5SGregory Neil Shapiro unsigned int ttl; 250*5b0945b5SGregory Neil Shapiro int n_rrs, len, err, herr; 251*5b0945b5SGregory Neil Shapiro bool isrname; 252*5b0945b5SGregory Neil Shapiro char nbuf[MAXDNAME]; 253*5b0945b5SGregory Neil Shapiro char key[MAXDNAME]; 254*5b0945b5SGregory Neil Shapiro 255*5b0945b5SGregory Neil Shapiro SM_REQUIRE(host != NULL); 256*5b0945b5SGregory Neil Shapiro if (pste != NULL) 257*5b0945b5SGregory Neil Shapiro *pste = NULL; 258*5b0945b5SGregory Neil Shapiro if ('\0' == *host) 259*5b0945b5SGregory Neil Shapiro return 0; 260*5b0945b5SGregory Neil Shapiro 261*5b0945b5SGregory Neil Shapiro isrname = NULL == name; 262*5b0945b5SGregory Neil Shapiro if (isrname) 263*5b0945b5SGregory Neil Shapiro name = host; 264*5b0945b5SGregory Neil Shapiro now = 0; 265*5b0945b5SGregory Neil Shapiro n_rrs = 0; 266*5b0945b5SGregory Neil Shapiro dr = NULL; 267*5b0945b5SGregory Neil Shapiro dane_tlsa = NULL; 268*5b0945b5SGregory Neil Shapiro len = strlen(name); 269*5b0945b5SGregory Neil Shapiro if (len > 1 && name[len - 1] == '.') 270*5b0945b5SGregory Neil Shapiro { 271*5b0945b5SGregory Neil Shapiro len--; 272*5b0945b5SGregory Neil Shapiro name[len] = '\0'; 273*5b0945b5SGregory Neil Shapiro } 274*5b0945b5SGregory Neil Shapiro else 275*5b0945b5SGregory Neil Shapiro len = -1; 276*5b0945b5SGregory Neil Shapiro if (0 == port || tTd(66, 10)) 277*5b0945b5SGregory Neil Shapiro port = 25; 278*5b0945b5SGregory Neil Shapiro (void) sm_snprintf(key, sizeof(key), "_%u..%s", port, name); 279*5b0945b5SGregory Neil Shapiro ste = stab(key, ST_TLSA_RR, ST_FIND); 280*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 281*5b0945b5SGregory Neil Shapiro sm_dprintf("gettlsa(%s, %s, ste=%p, pste=%p, flags=%lX, port=%d)\n", 282*5b0945b5SGregory Neil Shapiro host, isrname ? "" : name, (void *)ste, (void *)pste, 283*5b0945b5SGregory Neil Shapiro flags, port); 284*5b0945b5SGregory Neil Shapiro 285*5b0945b5SGregory Neil Shapiro if (ste != NULL) 286*5b0945b5SGregory Neil Shapiro { 287*5b0945b5SGregory Neil Shapiro dane_tlsa = ste->s_tlsa; 288*5b0945b5SGregory Neil Shapiro if ((TLSAFLADMX & flags) != 0) 289*5b0945b5SGregory Neil Shapiro TLSA_CLR_FL(ste->s_tlsa, TLSAFLNOADMX); 290*5b0945b5SGregory Neil Shapiro } 291*5b0945b5SGregory Neil Shapiro 292*5b0945b5SGregory Neil Shapiro /* Do not reload TLSA RRs if the MX RRs were not securely retrieved. */ 293*5b0945b5SGregory Neil Shapiro if (pste != NULL 294*5b0945b5SGregory Neil Shapiro && dane_tlsa != NULL && TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX) 295*5b0945b5SGregory Neil Shapiro && DANE_SECURE == Dane) 296*5b0945b5SGregory Neil Shapiro goto end; 297*5b0945b5SGregory Neil Shapiro 298*5b0945b5SGregory Neil Shapiro if (ste != NULL) 299*5b0945b5SGregory Neil Shapiro { 300*5b0945b5SGregory Neil Shapiro SM_ASSERT(dane_tlsa != NULL); 301*5b0945b5SGregory Neil Shapiro now = curtime(); 302*5b0945b5SGregory Neil Shapiro if (dane_tlsa->dane_tlsa_exp <= now 303*5b0945b5SGregory Neil Shapiro && 0 == (TLSAFLNOEXP & flags)) 304*5b0945b5SGregory Neil Shapiro dane_tlsa_clr(dane_tlsa); 305*5b0945b5SGregory Neil Shapiro else 306*5b0945b5SGregory Neil Shapiro { 307*5b0945b5SGregory Neil Shapiro n_rrs = dane_tlsa->dane_tlsa_n; 308*5b0945b5SGregory Neil Shapiro goto end; 309*5b0945b5SGregory Neil Shapiro } 310*5b0945b5SGregory Neil Shapiro } 311*5b0945b5SGregory Neil Shapiro 312*5b0945b5SGregory Neil Shapiro if (dane_tlsa == NULL) 313*5b0945b5SGregory Neil Shapiro { 314*5b0945b5SGregory Neil Shapiro dane_tlsa = (dane_tlsa_P) sm_malloc(sizeof(*dane_tlsa)); 315*5b0945b5SGregory Neil Shapiro if (dane_tlsa == NULL) 316*5b0945b5SGregory Neil Shapiro { 317*5b0945b5SGregory Neil Shapiro n_rrs = -ENOMEM; 318*5b0945b5SGregory Neil Shapiro goto end; 319*5b0945b5SGregory Neil Shapiro } 320*5b0945b5SGregory Neil Shapiro memset(dane_tlsa, '\0', sizeof(*dane_tlsa)); 321*5b0945b5SGregory Neil Shapiro } 322*5b0945b5SGregory Neil Shapiro 323*5b0945b5SGregory Neil Shapiro /* There are flags to store -- just set those, do nothing else. */ 324*5b0945b5SGregory Neil Shapiro if (TLSA_STORE_FL(flags)) 325*5b0945b5SGregory Neil Shapiro { 326*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_flags = flags; 327*5b0945b5SGregory Neil Shapiro ttl = mxttl > 0 ? mxttl: SM_DEFAULT_TTL; 328*5b0945b5SGregory Neil Shapiro goto done; 329*5b0945b5SGregory Neil Shapiro } 330*5b0945b5SGregory Neil Shapiro 331*5b0945b5SGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "_%u._tcp.%s", port, host); 332*5b0945b5SGregory Neil Shapiro dr = dns_lookup_int(nbuf, C_IN, T_TLSA, 0, 0, 333*5b0945b5SGregory Neil Shapiro TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX) ? 0 : SM_RES_DNSSEC, 334*5b0945b5SGregory Neil Shapiro RR_RAW, &err, &herr); 335*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 336*5b0945b5SGregory Neil Shapiro sm_dprintf("gettlsa(%s), dr=%p, ad=%d, err=%d, herr=%d\n", host, 337*5b0945b5SGregory Neil Shapiro (void *)dr, dr != NULL ? dr->dns_r_h.ad : -1, err, herr); 338*5b0945b5SGregory Neil Shapiro ttl = UINT_MAX; 339*5b0945b5SGregory Neil Shapiro n_rrs = tlsaadd(key, dr, dane_tlsa, herr, n_rrs, &ttl, 0); 340*5b0945b5SGregory Neil Shapiro 341*5b0945b5SGregory Neil Shapiro /* no valid entries found? */ 342*5b0945b5SGregory Neil Shapiro if (n_rrs == 0 && !TLSA_RR_TEMPFAIL(dane_tlsa)) 343*5b0945b5SGregory Neil Shapiro { 344*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 345*5b0945b5SGregory Neil Shapiro sm_dprintf("gettlsa(%s), n_rrs=%d, herr=%d, status=NOT_ADDED\n", 346*5b0945b5SGregory Neil Shapiro host, n_rrs, dane_tlsa->dane_tlsa_dnsrc); 347*5b0945b5SGregory Neil Shapiro goto cleanup; 348*5b0945b5SGregory Neil Shapiro } 349*5b0945b5SGregory Neil Shapiro 350*5b0945b5SGregory Neil Shapiro done: 351*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_n = n_rrs; 352*5b0945b5SGregory Neil Shapiro if (!isrname) 353*5b0945b5SGregory Neil Shapiro { 354*5b0945b5SGregory Neil Shapiro SM_FREE(dane_tlsa->dane_tlsa_sni); 355*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_sni = sm_strdup(host); 356*5b0945b5SGregory Neil Shapiro } 357*5b0945b5SGregory Neil Shapiro if (NULL == ste) 358*5b0945b5SGregory Neil Shapiro { 359*5b0945b5SGregory Neil Shapiro ste = stab(key, ST_TLSA_RR, ST_ENTER); 360*5b0945b5SGregory Neil Shapiro if (NULL == ste) 361*5b0945b5SGregory Neil Shapiro goto error; 362*5b0945b5SGregory Neil Shapiro } 363*5b0945b5SGregory Neil Shapiro ste->s_tlsa = dane_tlsa; 364*5b0945b5SGregory Neil Shapiro if (now == 0) 365*5b0945b5SGregory Neil Shapiro now = curtime(); 366*5b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 367*5b0945b5SGregory Neil Shapiro dns_free_data(dr); 368*5b0945b5SGregory Neil Shapiro dr = NULL; 369*5b0945b5SGregory Neil Shapiro goto end; 370*5b0945b5SGregory Neil Shapiro 371*5b0945b5SGregory Neil Shapiro error: 372*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 373*5b0945b5SGregory Neil Shapiro sm_dprintf("gettlsa(%s, %s), status=error\n", host, key); 374*5b0945b5SGregory Neil Shapiro n_rrs = -1; 375*5b0945b5SGregory Neil Shapiro cleanup: 376*5b0945b5SGregory Neil Shapiro if (NULL == ste) 377*5b0945b5SGregory Neil Shapiro dane_tlsa_free(dane_tlsa); 378*5b0945b5SGregory Neil Shapiro dns_free_data(dr); 379*5b0945b5SGregory Neil Shapiro dr = NULL; 380*5b0945b5SGregory Neil Shapiro 381*5b0945b5SGregory Neil Shapiro end: 382*5b0945b5SGregory Neil Shapiro if (pste != NULL && ste != NULL) 383*5b0945b5SGregory Neil Shapiro *pste = ste; 384*5b0945b5SGregory Neil Shapiro if (len > 0) 385*5b0945b5SGregory Neil Shapiro host[len] = '.'; 386*5b0945b5SGregory Neil Shapiro return n_rrs; 387*5b0945b5SGregory Neil Shapiro } 388*5b0945b5SGregory Neil Shapiro # endif /* DANE */ 389*5b0945b5SGregory Neil Shapiro 39040266059SGregory Neil Shapiro /* 39140266059SGregory Neil Shapiro ** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 39240266059SGregory Neil Shapiro ** 39340266059SGregory Neil Shapiro ** We have to initialize this once before doing anything else. 39440266059SGregory Neil Shapiro ** Moreover, we have to repeat this from time to time to avoid 39540266059SGregory Neil Shapiro ** stale data, e.g., in persistent queue runners. 39640266059SGregory Neil Shapiro ** This should be done in a parent process so the child 39740266059SGregory Neil Shapiro ** processes have the right data. 39840266059SGregory Neil Shapiro ** 39940266059SGregory Neil Shapiro ** Parameters: 40040266059SGregory Neil Shapiro ** host -- the name of the fallback MX host. 40140266059SGregory Neil Shapiro ** 40240266059SGregory Neil Shapiro ** Returns: 40340266059SGregory Neil Shapiro ** number of MX records. 40440266059SGregory Neil Shapiro ** 40540266059SGregory Neil Shapiro ** Side Effects: 406e92d3f3fSGregory Neil Shapiro ** Populates NumFallbackMXHosts and fbhosts. 40740266059SGregory Neil Shapiro ** Sets renewal time (based on TTL). 40840266059SGregory Neil Shapiro */ 40940266059SGregory Neil Shapiro 410e92d3f3fSGregory Neil Shapiro int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 41140266059SGregory Neil Shapiro static char *fbhosts[MAXMXHOSTS + 1]; 41240266059SGregory Neil Shapiro 41340266059SGregory Neil Shapiro int 41440266059SGregory Neil Shapiro getfallbackmxrr(host) 41540266059SGregory Neil Shapiro char *host; 41640266059SGregory Neil Shapiro { 41740266059SGregory Neil Shapiro int i, rcode; 41840266059SGregory Neil Shapiro int ttl; 41940266059SGregory Neil Shapiro static time_t renew = 0; 42040266059SGregory Neil Shapiro 42140266059SGregory Neil Shapiro #if 0 42240266059SGregory Neil Shapiro /* This is currently done before this function is called. */ 42340266059SGregory Neil Shapiro if (host == NULL || *host == '\0') 42440266059SGregory Neil Shapiro return 0; 42540266059SGregory Neil Shapiro #endif /* 0 */ 426e92d3f3fSGregory Neil Shapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 427e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 428*5b0945b5SGregory Neil Shapiro 429*5b0945b5SGregory Neil Shapiro /* for DANE we need to invoke getmxrr() to get the TLSA RRs. */ 430*5b0945b5SGregory Neil Shapiro #if !DANE 43140266059SGregory Neil Shapiro if (host[0] == '[') 43240266059SGregory Neil Shapiro { 43340266059SGregory Neil Shapiro fbhosts[0] = host; 434e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = 1; 43540266059SGregory Neil Shapiro } 43640266059SGregory Neil Shapiro else 437*5b0945b5SGregory Neil Shapiro #endif 43840266059SGregory Neil Shapiro { 43940266059SGregory Neil Shapiro /* free old data */ 440e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 44140266059SGregory Neil Shapiro sm_free(fbhosts[i]); 44240266059SGregory Neil Shapiro 443*5b0945b5SGregory Neil Shapiro /* 444*5b0945b5SGregory Neil Shapiro ** Get new data. 445*5b0945b5SGregory Neil Shapiro ** Note: passing 0 as port is not correct but we cannot 446*5b0945b5SGregory Neil Shapiro ** determine the port number as there is no mailer. 447*5b0945b5SGregory Neil Shapiro */ 448*5b0945b5SGregory Neil Shapiro 449*5b0945b5SGregory Neil Shapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, 450*5b0945b5SGregory Neil Shapiro #if DANE 451*5b0945b5SGregory Neil Shapiro (DANE_SECURE == Dane) ? ISAD : 452*5b0945b5SGregory Neil Shapiro #endif 453*5b0945b5SGregory Neil Shapiro 0, 454*5b0945b5SGregory Neil Shapiro &rcode, &ttl, 0); 45540266059SGregory Neil Shapiro renew = curtime() + ttl; 456e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++) 45740266059SGregory Neil Shapiro fbhosts[i] = newstr(fbhosts[i]); 45840266059SGregory Neil Shapiro } 459*5b0945b5SGregory Neil Shapiro if (NumFallbackMXHosts == NULLMX) 460*5b0945b5SGregory Neil Shapiro NumFallbackMXHosts = 0; 461e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts; 46240266059SGregory Neil Shapiro } 46340266059SGregory Neil Shapiro 46440266059SGregory Neil Shapiro /* 46540266059SGregory Neil Shapiro ** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 46640266059SGregory Neil Shapiro ** 46740266059SGregory Neil Shapiro ** Parameters: 46840266059SGregory Neil Shapiro ** nmx -- current number of MX records. 46940266059SGregory Neil Shapiro ** prefs -- array of preferences. 47040266059SGregory Neil Shapiro ** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 47140266059SGregory Neil Shapiro ** 47240266059SGregory Neil Shapiro ** Returns: 47340266059SGregory Neil Shapiro ** new number of MX records. 47440266059SGregory Neil Shapiro ** 47540266059SGregory Neil Shapiro ** Side Effects: 476e92d3f3fSGregory Neil Shapiro ** If FallbackMX was set, it appends the MX records for 47740266059SGregory Neil Shapiro ** that host to mxhosts (and modifies prefs accordingly). 47840266059SGregory Neil Shapiro */ 47940266059SGregory Neil Shapiro 48040266059SGregory Neil Shapiro static int 48140266059SGregory Neil Shapiro fallbackmxrr(nmx, prefs, mxhosts) 48240266059SGregory Neil Shapiro int nmx; 48340266059SGregory Neil Shapiro unsigned short *prefs; 48440266059SGregory Neil Shapiro char **mxhosts; 48540266059SGregory Neil Shapiro { 48640266059SGregory Neil Shapiro int i; 48740266059SGregory Neil Shapiro 488e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 48940266059SGregory Neil Shapiro { 49040266059SGregory Neil Shapiro if (nmx > 0) 49140266059SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1; 49240266059SGregory Neil Shapiro else 49340266059SGregory Neil Shapiro prefs[nmx] = 0; 49440266059SGregory Neil Shapiro mxhosts[nmx++] = fbhosts[i]; 49540266059SGregory Neil Shapiro } 49640266059SGregory Neil Shapiro return nmx; 49740266059SGregory Neil Shapiro } 49840266059SGregory Neil Shapiro 49940266059SGregory Neil Shapiro /* 500c2aa98e2SPeter Wemm ** GETMXRR -- get MX resource records for a domain 501c2aa98e2SPeter Wemm ** 502c2aa98e2SPeter Wemm ** Parameters: 503c2aa98e2SPeter Wemm ** host -- the name of the host to MX. 504c2aa98e2SPeter Wemm ** mxhosts -- a pointer to a return buffer of MX records. 50506f25ae9SGregory Neil Shapiro ** mxprefs -- a pointer to a return buffer of MX preferences. 50606f25ae9SGregory Neil Shapiro ** If NULL, don't try to populate. 507*5b0945b5SGregory Neil Shapiro ** flags -- flags: 508*5b0945b5SGregory Neil Shapiro ** DROPLOCALHOSt -- If true, all MX records less preferred 509c2aa98e2SPeter Wemm ** than the local host (as determined by $=w) will 510c2aa98e2SPeter Wemm ** be discarded. 511*5b0945b5SGregory Neil Shapiro ** TRYFALLBACK -- add also fallback MX host? 512*5b0945b5SGregory Neil Shapiro ** ISAD -- host lookup was secure? 513c2aa98e2SPeter Wemm ** rcode -- a pointer to an EX_ status code. 51440266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 515c2aa98e2SPeter Wemm ** 516c2aa98e2SPeter Wemm ** Returns: 517c2aa98e2SPeter Wemm ** The number of MX records found. 518c2aa98e2SPeter Wemm ** -1 if there is an internal failure. 519c2aa98e2SPeter Wemm ** If no MX records are found, mxhosts[0] is set to host 520c2aa98e2SPeter Wemm ** and 1 is returned. 52140266059SGregory Neil Shapiro ** 52240266059SGregory Neil Shapiro ** Side Effects: 52340266059SGregory Neil Shapiro ** The entries made for mxhosts point to a static array 52440266059SGregory Neil Shapiro ** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 52540266059SGregory Neil Shapiro ** if it must be preserved across calls to this function. 526c2aa98e2SPeter Wemm */ 527c2aa98e2SPeter Wemm 528c2aa98e2SPeter Wemm int 529*5b0945b5SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port) 530c2aa98e2SPeter Wemm char *host; 531c2aa98e2SPeter Wemm char **mxhosts; 53240266059SGregory Neil Shapiro unsigned short *mxprefs; 533*5b0945b5SGregory Neil Shapiro unsigned int flags; 534c2aa98e2SPeter Wemm int *rcode; 53540266059SGregory Neil Shapiro int *pttl; 536*5b0945b5SGregory Neil Shapiro int port; 537c2aa98e2SPeter Wemm { 53840266059SGregory Neil Shapiro register unsigned char *eom, *cp; 539c2aa98e2SPeter Wemm register int i, j, n; 540c2aa98e2SPeter Wemm int nmx = 0; 541c2aa98e2SPeter Wemm register char *bp; 542c2aa98e2SPeter Wemm HEADER *hp; 543c2aa98e2SPeter Wemm querybuf answer; 544c2aa98e2SPeter Wemm int ancount, qdcount, buflen; 54540266059SGregory Neil Shapiro bool seenlocal = false; 54640266059SGregory Neil Shapiro unsigned short pref, type; 54740266059SGregory Neil Shapiro unsigned short localpref = 256; 548e92d3f3fSGregory Neil Shapiro char *fallbackMX = FallbackMX; 54940266059SGregory Neil Shapiro bool trycanon = false; 55040266059SGregory Neil Shapiro unsigned short *prefs; 551b6bacd31SGregory Neil Shapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 55240266059SGregory Neil Shapiro unsigned short prefer[MAXMXHOSTS]; 553c2aa98e2SPeter Wemm int weight[MAXMXHOSTS]; 55440266059SGregory Neil Shapiro int ttl = 0; 555*5b0945b5SGregory Neil Shapiro bool ad; 556*5b0945b5SGregory Neil Shapiro bool seennullmx = false; 55706f25ae9SGregory Neil Shapiro extern int res_query(), res_search(); 558*5b0945b5SGregory Neil Shapiro # if DANE 559*5b0945b5SGregory Neil Shapiro bool cname2mx; 560*5b0945b5SGregory Neil Shapiro char qname[MAXNAME]; 561*5b0945b5SGregory Neil Shapiro unsigned long old_options = 0; 562*5b0945b5SGregory Neil Shapiro # endif 563c2aa98e2SPeter Wemm 564c2aa98e2SPeter Wemm if (tTd(8, 2)) 565*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr(%s, droplocalhost=%d, flags=%X, port=%d)\n", 566*5b0945b5SGregory Neil Shapiro host, (flags & DROPLOCALHOST) != 0, flags, port); 567*5b0945b5SGregory Neil Shapiro ad = (flags & ISAD) != 0; 56813d88268SGregory Neil Shapiro *rcode = EX_OK; 56913d88268SGregory Neil Shapiro if (pttl != NULL) 57013d88268SGregory Neil Shapiro *pttl = SM_DEFAULT_TTL; 571a7ec597cSGregory Neil Shapiro if (*host == '\0') 572a7ec597cSGregory Neil Shapiro return 0; 573*5b0945b5SGregory Neil Shapiro # if DANE 574*5b0945b5SGregory Neil Shapiro cname2mx = false; 575*5b0945b5SGregory Neil Shapiro qname[0] = '\0'; 576*5b0945b5SGregory Neil Shapiro old_options = _res.options; 577*5b0945b5SGregory Neil Shapiro if (ad) 578*5b0945b5SGregory Neil Shapiro _res.options |= SM_RES_DNSSEC; 579*5b0945b5SGregory Neil Shapiro # endif 580c2aa98e2SPeter Wemm 581*5b0945b5SGregory Neil Shapiro if ((fallbackMX != NULL && (flags & DROPLOCALHOST) != 0 && 582*5b0945b5SGregory Neil Shapiro wordinclass(fallbackMX, 'w')) || (flags & TRYFALLBACK) == 0) 583c2aa98e2SPeter Wemm { 584c2aa98e2SPeter Wemm /* don't use fallback for this pass */ 585c2aa98e2SPeter Wemm fallbackMX = NULL; 586c2aa98e2SPeter Wemm } 587c2aa98e2SPeter Wemm 58806f25ae9SGregory Neil Shapiro if (mxprefs != NULL) 58906f25ae9SGregory Neil Shapiro prefs = mxprefs; 59006f25ae9SGregory Neil Shapiro else 59106f25ae9SGregory Neil Shapiro prefs = prefer; 59206f25ae9SGregory Neil Shapiro 593c2aa98e2SPeter Wemm /* efficiency hack -- numeric or non-MX lookups */ 594c2aa98e2SPeter Wemm if (host[0] == '[') 595c2aa98e2SPeter Wemm goto punt; 596c2aa98e2SPeter Wemm 597*5b0945b5SGregory Neil Shapiro # if DANE 598*5b0945b5SGregory Neil Shapiro /* 599*5b0945b5SGregory Neil Shapiro ** NOTE: This only works if nocanonify is used, 600*5b0945b5SGregory Neil Shapiro ** otherwise the name is already rewritten. 601*5b0945b5SGregory Neil Shapiro */ 602*5b0945b5SGregory Neil Shapiro 603*5b0945b5SGregory Neil Shapiro /* always or only when "needed"? */ 604*5b0945b5SGregory Neil Shapiro if (DANE_ALWAYS == Dane || (ad && DANE_SECURE == Dane)) 605*5b0945b5SGregory Neil Shapiro (void) sm_strlcpy(qname, host, sizeof(qname)); 606*5b0945b5SGregory Neil Shapiro # endif /* DANE */ 607*5b0945b5SGregory Neil Shapiro 608*5b0945b5SGregory Neil Shapiro # if _FFR_EAI 609*5b0945b5SGregory Neil Shapiro if (!addr_is_ascii(host)) 610*5b0945b5SGregory Neil Shapiro { 611*5b0945b5SGregory Neil Shapiro char buf[1024]; 612*5b0945b5SGregory Neil Shapiro UErrorCode error = U_ZERO_ERROR; 613*5b0945b5SGregory Neil Shapiro UIDNAInfo info = UIDNA_INFO_INITIALIZER; 614*5b0945b5SGregory Neil Shapiro UIDNA *idna; 615*5b0945b5SGregory Neil Shapiro 616*5b0945b5SGregory Neil Shapiro idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error); 617*5b0945b5SGregory Neil Shapiro (void) uidna_nameToASCII_UTF8(idna, host, strlen(host), 618*5b0945b5SGregory Neil Shapiro buf, sizeof(buf) - 1, 619*5b0945b5SGregory Neil Shapiro &info, &error); 620*5b0945b5SGregory Neil Shapiro uidna_close(idna); 621*5b0945b5SGregory Neil Shapiro host = sm_rpool_strdup_x(CurEnv->e_rpool, buf); 622*5b0945b5SGregory Neil Shapiro } 623*5b0945b5SGregory Neil Shapiro # endif /* _FFR_EAI */ 624*5b0945b5SGregory Neil Shapiro 625c2aa98e2SPeter Wemm /* 626c2aa98e2SPeter Wemm ** If we don't have MX records in our host switch, don't 627c2aa98e2SPeter Wemm ** try for MX records. Note that this really isn't "right", 628c2aa98e2SPeter Wemm ** since we might be set up to try NIS first and then DNS; 629c2aa98e2SPeter Wemm ** if the host is found in NIS we really shouldn't be doing 630c2aa98e2SPeter Wemm ** MX lookups. However, that should be a degenerate case. 631c2aa98e2SPeter Wemm */ 632c2aa98e2SPeter Wemm 633c2aa98e2SPeter Wemm if (!UseNameServer) 634c2aa98e2SPeter Wemm goto punt; 635c2aa98e2SPeter Wemm if (HasWildcardMX && ConfigLevel >= 6) 636c2aa98e2SPeter Wemm resfunc = res_query; 637c2aa98e2SPeter Wemm else 638c2aa98e2SPeter Wemm resfunc = res_search; 639*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 640*5b0945b5SGregory Neil Shapiro if (tTd(8, 110)) 641*5b0945b5SGregory Neil Shapiro resfunc = tstdns_search; 642*5b0945b5SGregory Neil Shapiro # endif 643c2aa98e2SPeter Wemm 644c2aa98e2SPeter Wemm errno = 0; 645*5b0945b5SGregory Neil Shapiro hp = (HEADER *)&answer; 64640266059SGregory Neil Shapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 64740266059SGregory Neil Shapiro sizeof(answer)); 648c2aa98e2SPeter Wemm if (n < 0) 649c2aa98e2SPeter Wemm { 650c2aa98e2SPeter Wemm if (tTd(8, 1)) 651*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 652*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d (%s), h_errno=%d (%s))\n", 653*5b0945b5SGregory Neil Shapiro host, errno, strerror(errno), 654*5b0945b5SGregory Neil Shapiro h_errno, herrno2txt(h_errno)); 655*5b0945b5SGregory Neil Shapiro # else 656*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed, h_errno=%d\n", 657*5b0945b5SGregory Neil Shapiro host, h_errno); 658*5b0945b5SGregory Neil Shapiro # endif 659c2aa98e2SPeter Wemm switch (h_errno) 660c2aa98e2SPeter Wemm { 661c2aa98e2SPeter Wemm case NO_DATA: 66240266059SGregory Neil Shapiro trycanon = true; 66306f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 664c2aa98e2SPeter Wemm 665c2aa98e2SPeter Wemm case NO_RECOVERY: 666c2aa98e2SPeter Wemm /* no MX data on this host */ 667c2aa98e2SPeter Wemm goto punt; 668c2aa98e2SPeter Wemm 669c2aa98e2SPeter Wemm case HOST_NOT_FOUND: 670c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH 671*5b0945b5SGregory Neil Shapiro case 0: /* Ultrix resolver returns failure w/ h_errno=0 */ 672*5b0945b5SGregory Neil Shapiro # endif 673c2aa98e2SPeter Wemm /* host doesn't exist in DNS; might be in /etc/hosts */ 67440266059SGregory Neil Shapiro trycanon = true; 675c2aa98e2SPeter Wemm *rcode = EX_NOHOST; 676c2aa98e2SPeter Wemm goto punt; 677c2aa98e2SPeter Wemm 678c2aa98e2SPeter Wemm case TRY_AGAIN: 679c2aa98e2SPeter Wemm case -1: 680c2aa98e2SPeter Wemm /* couldn't connect to the name server */ 681c2aa98e2SPeter Wemm if (fallbackMX != NULL) 682c2aa98e2SPeter Wemm { 683c2aa98e2SPeter Wemm /* name server is hosed -- push to fallback */ 684*5b0945b5SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 685*5b0945b5SGregory Neil Shapiro goto done; 686c2aa98e2SPeter Wemm } 687c2aa98e2SPeter Wemm /* it might come up later; better queue it up */ 688c2aa98e2SPeter Wemm *rcode = EX_TEMPFAIL; 689c2aa98e2SPeter Wemm break; 690c2aa98e2SPeter Wemm 691c2aa98e2SPeter Wemm default: 69240266059SGregory Neil Shapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 693c2aa98e2SPeter Wemm host, h_errno); 694c2aa98e2SPeter Wemm *rcode = EX_OSERR; 695c2aa98e2SPeter Wemm break; 696c2aa98e2SPeter Wemm } 697c2aa98e2SPeter Wemm 698c2aa98e2SPeter Wemm /* irreconcilable differences */ 699*5b0945b5SGregory Neil Shapiro goto error; 700c2aa98e2SPeter Wemm } 701c2aa98e2SPeter Wemm 702*5b0945b5SGregory Neil Shapiro ad = ad && hp->ad; 703*5b0945b5SGregory Neil Shapiro if (tTd(8, 2)) 704*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr(%s), hp=%p, ad=%d\n", host, (void*)hp, ad); 705*5b0945b5SGregory Neil Shapiro 706c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 707c2aa98e2SPeter Wemm if (n > sizeof(answer)) 708c2aa98e2SPeter Wemm n = sizeof(answer); 709c2aa98e2SPeter Wemm 710c2aa98e2SPeter Wemm /* find first satisfactory answer */ 71140266059SGregory Neil Shapiro cp = (unsigned char *)&answer + HFIXEDSZ; 71240266059SGregory Neil Shapiro eom = (unsigned char *)&answer + n; 713*5b0945b5SGregory Neil Shapiro 71440266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 71506f25ae9SGregory Neil Shapiro qdcount--; 71606f25ae9SGregory Neil Shapiro cp += n + QFIXEDSZ) 71706f25ae9SGregory Neil Shapiro { 718c2aa98e2SPeter Wemm if ((n = dn_skipname(cp, eom)) < 0) 719c2aa98e2SPeter Wemm goto punt; 72006f25ae9SGregory Neil Shapiro } 72140266059SGregory Neil Shapiro 72240266059SGregory Neil Shapiro /* NOTE: see definition of MXHostBuf! */ 723c2aa98e2SPeter Wemm buflen = sizeof(MXHostBuf) - 1; 72440266059SGregory Neil Shapiro SM_ASSERT(buflen > 0); 725c2aa98e2SPeter Wemm bp = MXHostBuf; 72640266059SGregory Neil Shapiro ancount = ntohs((unsigned short) hp->ancount); 72740266059SGregory Neil Shapiro 72840266059SGregory Neil Shapiro /* See RFC 1035 for layout of RRs. */ 729e92d3f3fSGregory Neil Shapiro /* XXX leave room for FallbackMX ? */ 730c2aa98e2SPeter Wemm while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 731c2aa98e2SPeter Wemm { 73240266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 73340266059SGregory Neil Shapiro (RES_UNC_T) bp, buflen)) < 0) 734c2aa98e2SPeter Wemm break; 735c2aa98e2SPeter Wemm cp += n; 736c2aa98e2SPeter Wemm GETSHORT(type, cp); 73740266059SGregory Neil Shapiro cp += INT16SZ; /* skip over class */ 73840266059SGregory Neil Shapiro GETLONG(ttl, cp); 73940266059SGregory Neil Shapiro GETSHORT(n, cp); /* rdlength */ 740*5b0945b5SGregory Neil Shapiro # if DANE 741*5b0945b5SGregory Neil Shapiro if (type == T_CNAME) 742*5b0945b5SGregory Neil Shapiro cname2mx = true; 743*5b0945b5SGregory Neil Shapiro # endif 744c2aa98e2SPeter Wemm if (type != T_MX) 745c2aa98e2SPeter Wemm { 746*5b0945b5SGregory Neil Shapiro if ((tTd(8, 8) || _res.options & RES_DEBUG) 747*5b0945b5SGregory Neil Shapiro # if DANE 748*5b0945b5SGregory Neil Shapiro && type != T_RRSIG 749*5b0945b5SGregory Neil Shapiro # endif 750*5b0945b5SGregory Neil Shapiro ) 751*5b0945b5SGregory Neil Shapiro sm_dprintf("unexpected answer type %s, size %d\n", 752*5b0945b5SGregory Neil Shapiro dns_type_to_string(type), n); 753c2aa98e2SPeter Wemm cp += n; 754c2aa98e2SPeter Wemm continue; 755c2aa98e2SPeter Wemm } 756c2aa98e2SPeter Wemm GETSHORT(pref, cp); 75740266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 758c2aa98e2SPeter Wemm (RES_UNC_T) bp, buflen)) < 0) 759c2aa98e2SPeter Wemm break; 760c2aa98e2SPeter Wemm cp += n; 76140266059SGregory Neil Shapiro n = strlen(bp); 762*5b0945b5SGregory Neil Shapiro 763*5b0945b5SGregory Neil Shapiro /* Support for RFC7505 "MX 0 ." */ 764*5b0945b5SGregory Neil Shapiro if (pref == 0 && *bp == '\0') 765*5b0945b5SGregory Neil Shapiro seennullmx = true; 766*5b0945b5SGregory Neil Shapiro 767c2aa98e2SPeter Wemm if (wordinclass(bp, 'w')) 768c2aa98e2SPeter Wemm { 769c2aa98e2SPeter Wemm if (tTd(8, 3)) 77040266059SGregory Neil Shapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 771c2aa98e2SPeter Wemm bp, pref); 772*5b0945b5SGregory Neil Shapiro if ((flags & DROPLOCALHOST) != 0) 773c2aa98e2SPeter Wemm { 774c2aa98e2SPeter Wemm if (!seenlocal || pref < localpref) 775c2aa98e2SPeter Wemm localpref = pref; 77640266059SGregory Neil Shapiro seenlocal = true; 777c2aa98e2SPeter Wemm continue; 778c2aa98e2SPeter Wemm } 779c2aa98e2SPeter Wemm weight[nmx] = 0; 780c2aa98e2SPeter Wemm } 781c2aa98e2SPeter Wemm else 782c2aa98e2SPeter Wemm weight[nmx] = mxrand(bp); 78306f25ae9SGregory Neil Shapiro prefs[nmx] = pref; 784c2aa98e2SPeter Wemm mxhosts[nmx++] = bp; 785*5b0945b5SGregory Neil Shapiro # if DANE 786*5b0945b5SGregory Neil Shapiro if (CHK_DANE(Dane) && port >= 0) 787*5b0945b5SGregory Neil Shapiro { 788*5b0945b5SGregory Neil Shapiro int nrr; 789*5b0945b5SGregory Neil Shapiro unsigned long flags; 790*5b0945b5SGregory Neil Shapiro 791*5b0945b5SGregory Neil Shapiro flags = ad ? TLSAFLADMX : TLSAFLNOADMX; 792*5b0945b5SGregory Neil Shapiro nrr = gettlsa(bp, NULL, NULL, flags, ttl, port); 793*5b0945b5SGregory Neil Shapiro 794*5b0945b5SGregory Neil Shapiro /* Only check qname if no TLSA RRs were found */ 795*5b0945b5SGregory Neil Shapiro if (0 == nrr && cname2mx && '\0' != qname[0] && 796*5b0945b5SGregory Neil Shapiro strcmp(qname, bp)) 797*5b0945b5SGregory Neil Shapiro gettlsa(qname, bp, NULL, flags, ttl, port); 798*5b0945b5SGregory Neil Shapiro /* XXX is this the right ad flag? */ 799*5b0945b5SGregory Neil Shapiro } 800*5b0945b5SGregory Neil Shapiro # endif 801*5b0945b5SGregory Neil Shapiro 802*5b0945b5SGregory Neil Shapiro /* 803*5b0945b5SGregory Neil Shapiro ** Note: n can be 0 for something like: 804*5b0945b5SGregory Neil Shapiro ** host MX 0 . 805*5b0945b5SGregory Neil Shapiro ** See RFC 7505 806*5b0945b5SGregory Neil Shapiro */ 807*5b0945b5SGregory Neil Shapiro 808c2aa98e2SPeter Wemm bp += n; 809*5b0945b5SGregory Neil Shapiro if (0 == n || bp[-1] != '.') 810c2aa98e2SPeter Wemm { 811c2aa98e2SPeter Wemm *bp++ = '.'; 812c2aa98e2SPeter Wemm n++; 813c2aa98e2SPeter Wemm } 814c2aa98e2SPeter Wemm *bp++ = '\0'; 81540266059SGregory Neil Shapiro if (buflen < n + 1) 81640266059SGregory Neil Shapiro { 81740266059SGregory Neil Shapiro /* don't want to wrap buflen */ 81840266059SGregory Neil Shapiro break; 81940266059SGregory Neil Shapiro } 820c2aa98e2SPeter Wemm buflen -= n + 1; 821c2aa98e2SPeter Wemm } 822c2aa98e2SPeter Wemm 823*5b0945b5SGregory Neil Shapiro /* Support for RFC7505 "MX 0 ." */ 824*5b0945b5SGregory Neil Shapiro if (seennullmx && nmx == 1) 825*5b0945b5SGregory Neil Shapiro { 826*5b0945b5SGregory Neil Shapiro if (tTd(8, 4)) 827*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: Null MX record found, domain doesn't accept mail (RFC7505)\n"); 828*5b0945b5SGregory Neil Shapiro *rcode = EX_UNAVAILABLE; 829*5b0945b5SGregory Neil Shapiro return NULLMX; 830*5b0945b5SGregory Neil Shapiro } 831*5b0945b5SGregory Neil Shapiro 83240266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 83340266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 83440266059SGregory Neil Shapiro *pttl = ttl; 83540266059SGregory Neil Shapiro 836c2aa98e2SPeter Wemm /* sort the records */ 837c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 838c2aa98e2SPeter Wemm { 839c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 840c2aa98e2SPeter Wemm { 84106f25ae9SGregory Neil Shapiro if (prefs[i] > prefs[j] || 84206f25ae9SGregory Neil Shapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 843c2aa98e2SPeter Wemm { 844c2aa98e2SPeter Wemm register int temp; 845c2aa98e2SPeter Wemm register char *temp1; 846c2aa98e2SPeter Wemm 84706f25ae9SGregory Neil Shapiro temp = prefs[i]; 84806f25ae9SGregory Neil Shapiro prefs[i] = prefs[j]; 84906f25ae9SGregory Neil Shapiro prefs[j] = temp; 850c2aa98e2SPeter Wemm temp1 = mxhosts[i]; 851c2aa98e2SPeter Wemm mxhosts[i] = mxhosts[j]; 852c2aa98e2SPeter Wemm mxhosts[j] = temp1; 853c2aa98e2SPeter Wemm temp = weight[i]; 854c2aa98e2SPeter Wemm weight[i] = weight[j]; 855c2aa98e2SPeter Wemm weight[j] = temp; 856c2aa98e2SPeter Wemm } 857c2aa98e2SPeter Wemm } 85806f25ae9SGregory Neil Shapiro if (seenlocal && prefs[i] >= localpref) 859c2aa98e2SPeter Wemm { 860c2aa98e2SPeter Wemm /* truncate higher preference part of list */ 861c2aa98e2SPeter Wemm nmx = i; 862c2aa98e2SPeter Wemm } 863c2aa98e2SPeter Wemm } 864c2aa98e2SPeter Wemm 865c2aa98e2SPeter Wemm /* delete duplicates from list (yes, some bozos have duplicates) */ 866c2aa98e2SPeter Wemm for (i = 0; i < nmx - 1; ) 867c2aa98e2SPeter Wemm { 86840266059SGregory Neil Shapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 869c2aa98e2SPeter Wemm i++; 870c2aa98e2SPeter Wemm else 871c2aa98e2SPeter Wemm { 872c2aa98e2SPeter Wemm /* compress out duplicate */ 873c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++) 87406f25ae9SGregory Neil Shapiro { 875c2aa98e2SPeter Wemm mxhosts[j] = mxhosts[j + 1]; 87606f25ae9SGregory Neil Shapiro prefs[j] = prefs[j + 1]; 87706f25ae9SGregory Neil Shapiro } 878c2aa98e2SPeter Wemm nmx--; 879c2aa98e2SPeter Wemm } 880c2aa98e2SPeter Wemm } 881c2aa98e2SPeter Wemm 882c2aa98e2SPeter Wemm if (nmx == 0) 883c2aa98e2SPeter Wemm { 884c2aa98e2SPeter Wemm punt: 88506f25ae9SGregory Neil Shapiro if (seenlocal) 886c2aa98e2SPeter Wemm { 88706f25ae9SGregory Neil Shapiro struct hostent *h = NULL; 88806f25ae9SGregory Neil Shapiro 889c2aa98e2SPeter Wemm /* 890c2aa98e2SPeter Wemm ** If we have deleted all MX entries, this is 891c2aa98e2SPeter Wemm ** an error -- we should NEVER send to a host that 892c2aa98e2SPeter Wemm ** has an MX, and this should have been caught 893c2aa98e2SPeter Wemm ** earlier in the config file. 894c2aa98e2SPeter Wemm ** 895c2aa98e2SPeter Wemm ** Some sites prefer to go ahead and try the 896c2aa98e2SPeter Wemm ** A record anyway; that case is handled by 897c2aa98e2SPeter Wemm ** setting TryNullMXList. I believe this is a 898c2aa98e2SPeter Wemm ** bad idea, but it's up to you.... 899c2aa98e2SPeter Wemm */ 900c2aa98e2SPeter Wemm 90106f25ae9SGregory Neil Shapiro if (TryNullMXList) 90206f25ae9SGregory Neil Shapiro { 903602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 90406f25ae9SGregory Neil Shapiro errno = 0; 90506f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET); 90606f25ae9SGregory Neil Shapiro if (h == NULL) 90706f25ae9SGregory Neil Shapiro { 90806f25ae9SGregory Neil Shapiro if (errno == ETIMEDOUT || 90906f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 91006f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 91106f25ae9SGregory Neil Shapiro UseNameServer)) 91206f25ae9SGregory Neil Shapiro { 91306f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 914*5b0945b5SGregory Neil Shapiro goto error; 91506f25ae9SGregory Neil Shapiro } 91606f25ae9SGregory Neil Shapiro # if NETINET6 917602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0); 91806f25ae9SGregory Neil Shapiro errno = 0; 91906f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET6); 92006f25ae9SGregory Neil Shapiro if (h == NULL && 92106f25ae9SGregory Neil Shapiro (errno == ETIMEDOUT || 92206f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN || 92306f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED && 92406f25ae9SGregory Neil Shapiro UseNameServer))) 92506f25ae9SGregory Neil Shapiro { 92606f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL; 927*5b0945b5SGregory Neil Shapiro goto error; 92806f25ae9SGregory Neil Shapiro } 92906f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 93006f25ae9SGregory Neil Shapiro } 93106f25ae9SGregory Neil Shapiro } 93206f25ae9SGregory Neil Shapiro 93306f25ae9SGregory Neil Shapiro if (h == NULL) 93406f25ae9SGregory Neil Shapiro { 935c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 936c2aa98e2SPeter Wemm syserr("MX list for %s points back to %s", 937c2aa98e2SPeter Wemm host, MyHostName); 938*5b0945b5SGregory Neil Shapiro goto error; 939c2aa98e2SPeter Wemm } 94040266059SGregory Neil Shapiro # if NETINET6 941193538b7SGregory Neil Shapiro freehostent(h); 942af9557fdSGregory Neil Shapiro h = NULL; 943*5b0945b5SGregory Neil Shapiro # endif 94406f25ae9SGregory Neil Shapiro } 945d0cef73dSGregory Neil Shapiro if (strlen(host) >= sizeof(MXHostBuf)) 946c2aa98e2SPeter Wemm { 947c2aa98e2SPeter Wemm *rcode = EX_CONFIG; 948c2aa98e2SPeter Wemm syserr("Host name %s too long", 949c2aa98e2SPeter Wemm shortenstring(host, MAXSHORTSTR)); 950*5b0945b5SGregory Neil Shapiro goto error; 951c2aa98e2SPeter Wemm } 952d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf)); 953c2aa98e2SPeter Wemm mxhosts[0] = MXHostBuf; 95406f25ae9SGregory Neil Shapiro prefs[0] = 0; 955c2aa98e2SPeter Wemm if (host[0] == '[') 956c2aa98e2SPeter Wemm { 957c2aa98e2SPeter Wemm register char *p; 95806f25ae9SGregory Neil Shapiro # if NETINET6 95906f25ae9SGregory Neil Shapiro struct sockaddr_in6 tmp6; 960*5b0945b5SGregory Neil Shapiro # endif 961c2aa98e2SPeter Wemm 962c2aa98e2SPeter Wemm /* this may be an MX suppression-style address */ 963c2aa98e2SPeter Wemm p = strchr(MXHostBuf, ']'); 964c2aa98e2SPeter Wemm if (p != NULL) 965c2aa98e2SPeter Wemm { 966c2aa98e2SPeter Wemm *p = '\0'; 96706f25ae9SGregory Neil Shapiro 968c2aa98e2SPeter Wemm if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 969c2aa98e2SPeter Wemm { 970c2aa98e2SPeter Wemm nmx++; 971c2aa98e2SPeter Wemm *p = ']'; 972c2aa98e2SPeter Wemm } 97306f25ae9SGregory Neil Shapiro # if NETINET6 97440266059SGregory Neil Shapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 97506f25ae9SGregory Neil Shapiro &tmp6.sin6_addr) == 1) 97606f25ae9SGregory Neil Shapiro { 97706f25ae9SGregory Neil Shapiro nmx++; 97806f25ae9SGregory Neil Shapiro *p = ']'; 97906f25ae9SGregory Neil Shapiro } 98006f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 981c2aa98e2SPeter Wemm else 982c2aa98e2SPeter Wemm { 98340266059SGregory Neil Shapiro trycanon = true; 984c2aa98e2SPeter Wemm mxhosts[0]++; 985c2aa98e2SPeter Wemm } 986c2aa98e2SPeter Wemm } 987c2aa98e2SPeter Wemm } 988c2aa98e2SPeter Wemm if (trycanon && 989*5b0945b5SGregory Neil Shapiro (n = getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, 990*5b0945b5SGregory Neil Shapiro pttl)) != HOST_NOTFOUND) 991c2aa98e2SPeter Wemm { 99240266059SGregory Neil Shapiro /* XXX MXHostBuf == "" ? is that possible? */ 993c2aa98e2SPeter Wemm bp = &MXHostBuf[strlen(MXHostBuf)]; 994c2aa98e2SPeter Wemm if (bp[-1] != '.') 995c2aa98e2SPeter Wemm { 996c2aa98e2SPeter Wemm *bp++ = '.'; 997c2aa98e2SPeter Wemm *bp = '\0'; 998c2aa98e2SPeter Wemm } 999c2aa98e2SPeter Wemm nmx = 1; 1000*5b0945b5SGregory Neil Shapiro # if DANE 1001*5b0945b5SGregory Neil Shapiro if (tTd(8, 3)) 1002*5b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr=%s, getcanonname=%d\n", 1003*5b0945b5SGregory Neil Shapiro mxhosts[0], n); 1004*5b0945b5SGregory Neil Shapiro if (CHK_DANE(Dane) && port >= 0) 1005*5b0945b5SGregory Neil Shapiro { 1006*5b0945b5SGregory Neil Shapiro int nrr; 1007*5b0945b5SGregory Neil Shapiro unsigned long flags; 1008*5b0945b5SGregory Neil Shapiro unsigned int cttl; 1009*5b0945b5SGregory Neil Shapiro 1010*5b0945b5SGregory Neil Shapiro if (pttl != NULL) 1011*5b0945b5SGregory Neil Shapiro cttl = *pttl; 1012*5b0945b5SGregory Neil Shapiro else if (ttl > 0) 1013*5b0945b5SGregory Neil Shapiro cttl = ttl; 1014*5b0945b5SGregory Neil Shapiro else 1015*5b0945b5SGregory Neil Shapiro cttl = SM_DEFAULT_TTL; 1016*5b0945b5SGregory Neil Shapiro 1017*5b0945b5SGregory Neil Shapiro flags = (ad && n == HOST_SECURE) 1018*5b0945b5SGregory Neil Shapiro ? TLSAFLADMX : TLSAFLNOADMX; 1019*5b0945b5SGregory Neil Shapiro nrr = gettlsa(mxhosts[0], NULL, NULL, flags, 1020*5b0945b5SGregory Neil Shapiro cttl, port); 1021*5b0945b5SGregory Neil Shapiro 1022*5b0945b5SGregory Neil Shapiro /* 1023*5b0945b5SGregory Neil Shapiro ** Only check qname if no TLSA RRs were found 1024*5b0945b5SGregory Neil Shapiro ** XXX: what about (temp) DNS errors? 1025*5b0945b5SGregory Neil Shapiro */ 1026*5b0945b5SGregory Neil Shapiro 1027*5b0945b5SGregory Neil Shapiro if (0 == nrr && '\0' != qname[0] && 1028*5b0945b5SGregory Neil Shapiro strcmp(qname, mxhosts[0])) 1029*5b0945b5SGregory Neil Shapiro gettlsa(qname, mxhosts[0], NULL, flags, 1030*5b0945b5SGregory Neil Shapiro cttl, port); 1031*5b0945b5SGregory Neil Shapiro /* XXX is this the right ad flag? */ 1032*5b0945b5SGregory Neil Shapiro } 1033*5b0945b5SGregory Neil Shapiro # endif 1034c2aa98e2SPeter Wemm } 1035c2aa98e2SPeter Wemm } 1036c2aa98e2SPeter Wemm 1037c2aa98e2SPeter Wemm /* if we have a default lowest preference, include that */ 1038c2aa98e2SPeter Wemm if (fallbackMX != NULL && !seenlocal) 103906f25ae9SGregory Neil Shapiro { 1040*5b0945b5SGregory Neil Shapiro /* TODO: DNSsec status of fallbacks */ 104140266059SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 104206f25ae9SGregory Neil Shapiro } 1043*5b0945b5SGregory Neil Shapiro done: 1044*5b0945b5SGregory Neil Shapiro # if DANE 1045*5b0945b5SGregory Neil Shapiro _res.options = old_options; 1046*5b0945b5SGregory Neil Shapiro # endif 104706f25ae9SGregory Neil Shapiro return nmx; 1048*5b0945b5SGregory Neil Shapiro 1049*5b0945b5SGregory Neil Shapiro error: 1050*5b0945b5SGregory Neil Shapiro # if DANE 1051*5b0945b5SGregory Neil Shapiro _res.options = old_options; 1052*5b0945b5SGregory Neil Shapiro # endif 1053*5b0945b5SGregory Neil Shapiro return -1; 1054c2aa98e2SPeter Wemm } 1055*5b0945b5SGregory Neil Shapiro 105640266059SGregory Neil Shapiro /* 1057c2aa98e2SPeter Wemm ** MXRAND -- create a randomizer for equal MX preferences 1058c2aa98e2SPeter Wemm ** 1059c2aa98e2SPeter Wemm ** If two MX hosts have equal preferences we want to randomize 1060c2aa98e2SPeter Wemm ** the selection. But in order for signatures to be the same, 1061c2aa98e2SPeter Wemm ** we need to randomize the same way each time. This function 1062c2aa98e2SPeter Wemm ** computes a pseudo-random hash function from the host name. 1063c2aa98e2SPeter Wemm ** 1064c2aa98e2SPeter Wemm ** Parameters: 1065c2aa98e2SPeter Wemm ** host -- the name of the host. 1066c2aa98e2SPeter Wemm ** 1067c2aa98e2SPeter Wemm ** Returns: 1068c2aa98e2SPeter Wemm ** A random but repeatable value based on the host name. 1069c2aa98e2SPeter Wemm */ 1070c2aa98e2SPeter Wemm 107106f25ae9SGregory Neil Shapiro static int 1072c2aa98e2SPeter Wemm mxrand(host) 1073c2aa98e2SPeter Wemm register char *host; 1074c2aa98e2SPeter Wemm { 1075c2aa98e2SPeter Wemm int hfunc; 1076c2aa98e2SPeter Wemm static unsigned int seed; 1077c2aa98e2SPeter Wemm 1078c2aa98e2SPeter Wemm if (seed == 0) 1079c2aa98e2SPeter Wemm { 1080c2aa98e2SPeter Wemm seed = (int) curtime() & 0xffff; 1081c2aa98e2SPeter Wemm if (seed == 0) 1082c2aa98e2SPeter Wemm seed++; 1083c2aa98e2SPeter Wemm } 1084c2aa98e2SPeter Wemm 1085c2aa98e2SPeter Wemm if (tTd(17, 9)) 108640266059SGregory Neil Shapiro sm_dprintf("mxrand(%s)", host); 1087c2aa98e2SPeter Wemm 1088c2aa98e2SPeter Wemm hfunc = seed; 1089c2aa98e2SPeter Wemm while (*host != '\0') 1090c2aa98e2SPeter Wemm { 1091c2aa98e2SPeter Wemm int c = *host++; 1092c2aa98e2SPeter Wemm 1093c2aa98e2SPeter Wemm if (isascii(c) && isupper(c)) 1094c2aa98e2SPeter Wemm c = tolower(c); 1095c2aa98e2SPeter Wemm hfunc = ((hfunc << 1) ^ c) % 2003; 1096c2aa98e2SPeter Wemm } 1097c2aa98e2SPeter Wemm 1098c2aa98e2SPeter Wemm hfunc &= 0xff; 1099c2aa98e2SPeter Wemm hfunc++; 1100c2aa98e2SPeter Wemm 1101c2aa98e2SPeter Wemm if (tTd(17, 9)) 110240266059SGregory Neil Shapiro sm_dprintf(" = %d\n", hfunc); 1103c2aa98e2SPeter Wemm return hfunc; 1104c2aa98e2SPeter Wemm } 110540266059SGregory Neil Shapiro /* 1106c2aa98e2SPeter Wemm ** BESTMX -- find the best MX for a name 1107c2aa98e2SPeter Wemm ** 1108c2aa98e2SPeter Wemm ** This is really a hack, but I don't see any obvious way 1109c2aa98e2SPeter Wemm ** to generalize it at the moment. 1110c2aa98e2SPeter Wemm */ 1111c2aa98e2SPeter Wemm 1112c2aa98e2SPeter Wemm /* ARGSUSED3 */ 1113c2aa98e2SPeter Wemm char * 1114c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp) 1115c2aa98e2SPeter Wemm MAP *map; 1116c2aa98e2SPeter Wemm char *name; 1117c2aa98e2SPeter Wemm char **av; 1118c2aa98e2SPeter Wemm int *statp; 1119c2aa98e2SPeter Wemm { 1120c2aa98e2SPeter Wemm int nmx; 1121c2aa98e2SPeter Wemm int saveopts = _res.options; 112240266059SGregory Neil Shapiro int i; 112340266059SGregory Neil Shapiro ssize_t len = 0; 112440266059SGregory Neil Shapiro char *result; 1125c2aa98e2SPeter Wemm char *mxhosts[MAXMXHOSTS + 1]; 112640266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION 112740266059SGregory Neil Shapiro char *buf; 1128*5b0945b5SGregory Neil Shapiro # else 112940266059SGregory Neil Shapiro char *p; 1130065a643dSPeter Wemm char buf[PSBUFSIZE / 2]; 1131*5b0945b5SGregory Neil Shapiro # endif 1132c2aa98e2SPeter Wemm 1133c2aa98e2SPeter Wemm _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 1134*5b0945b5SGregory Neil Shapiro nmx = getmxrr(name, mxhosts, NULL, 0, statp, NULL, -1); 1135c2aa98e2SPeter Wemm _res.options = saveopts; 1136c2aa98e2SPeter Wemm if (nmx <= 0) 1137c2aa98e2SPeter Wemm return NULL; 1138c2aa98e2SPeter Wemm if (bitset(MF_MATCHONLY, map->map_mflags)) 1139c2aa98e2SPeter Wemm return map_rewrite(map, name, strlen(name), NULL); 1140c2aa98e2SPeter Wemm if ((map->map_coldelim == '\0') || (nmx == 1)) 1141c2aa98e2SPeter Wemm return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 1142c2aa98e2SPeter Wemm 1143c2aa98e2SPeter Wemm /* 1144c2aa98e2SPeter Wemm ** We were given a -z flag (return all MXs) and there are multiple 1145c2aa98e2SPeter Wemm ** ones. We need to build them all into a list. 1146c2aa98e2SPeter Wemm */ 114740266059SGregory Neil Shapiro 114840266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION 114940266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 115040266059SGregory Neil Shapiro { 115140266059SGregory Neil Shapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 115240266059SGregory Neil Shapiro { 115340266059SGregory Neil Shapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 115440266059SGregory Neil Shapiro mxhosts[i], map->map_coldelim); 115540266059SGregory Neil Shapiro return NULL; 115640266059SGregory Neil Shapiro } 115740266059SGregory Neil Shapiro len += strlen(mxhosts[i]) + 1; 115840266059SGregory Neil Shapiro if (len < 0) 115940266059SGregory Neil Shapiro { 116040266059SGregory Neil Shapiro len -= strlen(mxhosts[i]) + 1; 116140266059SGregory Neil Shapiro break; 116240266059SGregory Neil Shapiro } 116340266059SGregory Neil Shapiro } 116440266059SGregory Neil Shapiro buf = (char *) sm_malloc(len); 116540266059SGregory Neil Shapiro if (buf == NULL) 116640266059SGregory Neil Shapiro { 116740266059SGregory Neil Shapiro *statp = EX_UNAVAILABLE; 116840266059SGregory Neil Shapiro return NULL; 116940266059SGregory Neil Shapiro } 117040266059SGregory Neil Shapiro *buf = '\0'; 117140266059SGregory Neil Shapiro for (i = 0; i < nmx; i++) 117240266059SGregory Neil Shapiro { 117340266059SGregory Neil Shapiro int end; 117440266059SGregory Neil Shapiro 117540266059SGregory Neil Shapiro end = sm_strlcat(buf, mxhosts[i], len); 117640266059SGregory Neil Shapiro if (i != nmx && end + 1 < len) 117740266059SGregory Neil Shapiro { 117840266059SGregory Neil Shapiro buf[end] = map->map_coldelim; 117940266059SGregory Neil Shapiro buf[end + 1] = '\0'; 118040266059SGregory Neil Shapiro } 118140266059SGregory Neil Shapiro } 118240266059SGregory Neil Shapiro 118340266059SGregory Neil Shapiro /* Cleanly truncate for rulesets */ 118440266059SGregory Neil Shapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 118540266059SGregory Neil Shapiro # else /* _FFR_BESTMX_BETTER_TRUNCATION */ 1186c2aa98e2SPeter Wemm p = buf; 1187c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++) 1188c2aa98e2SPeter Wemm { 118940266059SGregory Neil Shapiro size_t slen; 1190c2aa98e2SPeter Wemm 1191c2aa98e2SPeter Wemm if (strchr(mxhosts[i], map->map_coldelim) != NULL) 1192c2aa98e2SPeter Wemm { 1193c2aa98e2SPeter Wemm syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 1194c2aa98e2SPeter Wemm mxhosts[i], map->map_coldelim); 1195c2aa98e2SPeter Wemm return NULL; 1196c2aa98e2SPeter Wemm } 1197c2aa98e2SPeter Wemm slen = strlen(mxhosts[i]); 1198d0cef73dSGregory Neil Shapiro if (len + slen + 2 > sizeof(buf)) 1199c2aa98e2SPeter Wemm break; 1200c2aa98e2SPeter Wemm if (i > 0) 1201c2aa98e2SPeter Wemm { 1202c2aa98e2SPeter Wemm *p++ = map->map_coldelim; 1203c2aa98e2SPeter Wemm len++; 1204c2aa98e2SPeter Wemm } 1205d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len); 1206c2aa98e2SPeter Wemm p += slen; 1207c2aa98e2SPeter Wemm len += slen; 1208c2aa98e2SPeter Wemm } 120940266059SGregory Neil Shapiro # endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 121040266059SGregory Neil Shapiro 121140266059SGregory Neil Shapiro result = map_rewrite(map, buf, len, av); 121240266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION 121340266059SGregory Neil Shapiro sm_free(buf); 1214*5b0945b5SGregory Neil Shapiro # endif 121540266059SGregory Neil Shapiro return result; 1216c2aa98e2SPeter Wemm } 121740266059SGregory Neil Shapiro /* 1218c2aa98e2SPeter Wemm ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 1219c2aa98e2SPeter Wemm ** 1220c2aa98e2SPeter Wemm ** This algorithm tries to be smart about wildcard MX records. 1221c2aa98e2SPeter Wemm ** This is hard to do because DNS doesn't tell is if we matched 1222c2aa98e2SPeter Wemm ** against a wildcard or a specific MX. 1223c2aa98e2SPeter Wemm ** 1224c2aa98e2SPeter Wemm ** We always prefer A & CNAME records, since these are presumed 1225c2aa98e2SPeter Wemm ** to be specific. 1226c2aa98e2SPeter Wemm ** 1227c2aa98e2SPeter Wemm ** If we match an MX in one pass and lose it in the next, we use 1228c2aa98e2SPeter Wemm ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 1229c2aa98e2SPeter Wemm ** A hostname bletch.foo.bar.com will match against this MX, but 1230c2aa98e2SPeter Wemm ** will stop matching when we try bletch.bar.com -- so we know 1231c2aa98e2SPeter Wemm ** that bletch.foo.bar.com must have been right. This fails if 1232c2aa98e2SPeter Wemm ** there was also an MX record matching *.BAR.COM, but there are 1233c2aa98e2SPeter Wemm ** some things that just can't be fixed. 1234c2aa98e2SPeter Wemm ** 1235c2aa98e2SPeter Wemm ** Parameters: 1236c2aa98e2SPeter Wemm ** host -- a buffer containing the name of the host. 1237c2aa98e2SPeter Wemm ** This is a value-result parameter. 1238c2aa98e2SPeter Wemm ** hbsize -- the size of the host buffer. 1239c2aa98e2SPeter Wemm ** trymx -- if set, try MX records as well as A and CNAME. 1240c2aa98e2SPeter Wemm ** statp -- pointer to place to store status. 124140266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL). 1242c2aa98e2SPeter Wemm ** 1243c2aa98e2SPeter Wemm ** Returns: 1244*5b0945b5SGregory Neil Shapiro ** >0 -- if the host was found. 1245*5b0945b5SGregory Neil Shapiro ** 0 -- otherwise. 1246c2aa98e2SPeter Wemm */ 1247c2aa98e2SPeter Wemm 1248*5b0945b5SGregory Neil Shapiro int 124940266059SGregory Neil Shapiro dns_getcanonname(host, hbsize, trymx, statp, pttl) 1250c2aa98e2SPeter Wemm char *host; 1251c2aa98e2SPeter Wemm int hbsize; 1252c2aa98e2SPeter Wemm bool trymx; 1253c2aa98e2SPeter Wemm int *statp; 125440266059SGregory Neil Shapiro int *pttl; 1255c2aa98e2SPeter Wemm { 125640266059SGregory Neil Shapiro register unsigned char *eom, *ap; 1257c2aa98e2SPeter Wemm register char *cp; 1258c2aa98e2SPeter Wemm register int n; 1259c2aa98e2SPeter Wemm HEADER *hp; 1260c2aa98e2SPeter Wemm querybuf answer; 1261*5b0945b5SGregory Neil Shapiro int ancount, qdcount, ret, type, qtype, initial, loopcnt, ttl, sli; 1262c2aa98e2SPeter Wemm char **domain; 1263*5b0945b5SGregory Neil Shapiro char *dp; 1264c2aa98e2SPeter Wemm char *mxmatch; 1265*5b0945b5SGregory Neil Shapiro bool amatch, gotmx, ad; 126640266059SGregory Neil Shapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 1267*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 1268*5b0945b5SGregory Neil Shapiro # define ADDSL 1 /* NameSearchList may add another entry to searchlist! */ 1269*5b0945b5SGregory Neil Shapiro # else 1270*5b0945b5SGregory Neil Shapiro # define ADDSL 0 1271*5b0945b5SGregory Neil Shapiro # endif 1272*5b0945b5SGregory Neil Shapiro char *searchlist[MAXDNSRCH + 2 + ADDSL]; 1273*5b0945b5SGregory Neil Shapiro # define SLSIZE SM_ARRAY_SIZE(searchlist) 1274*5b0945b5SGregory Neil Shapiro int (*resqdomain) __P((const char *, const char *, int, int, unsigned char *, int)); 1275*5b0945b5SGregory Neil Shapiro # if DANE 1276*5b0945b5SGregory Neil Shapiro unsigned long old_options = 0; 1277*5b0945b5SGregory Neil Shapiro # endif 1278c2aa98e2SPeter Wemm 1279*5b0945b5SGregory Neil Shapiro ttl = 0; 1280*5b0945b5SGregory Neil Shapiro gotmx = false; 1281*5b0945b5SGregory Neil Shapiro ad = true; 1282c2aa98e2SPeter Wemm if (tTd(8, 2)) 128340266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 1284c2aa98e2SPeter Wemm 1285c2aa98e2SPeter Wemm if ((_res.options & RES_INIT) == 0 && res_init() == -1) 1286c2aa98e2SPeter Wemm { 1287c2aa98e2SPeter Wemm *statp = EX_UNAVAILABLE; 1288*5b0945b5SGregory Neil Shapiro return HOST_NOTFOUND; 1289c2aa98e2SPeter Wemm } 1290c2aa98e2SPeter Wemm 1291*5b0945b5SGregory Neil Shapiro # if DANE 1292*5b0945b5SGregory Neil Shapiro old_options = _res.options; 1293*5b0945b5SGregory Neil Shapiro if (DANE_SECURE == Dane) 1294*5b0945b5SGregory Neil Shapiro _res.options |= SM_RES_DNSSEC; 1295*5b0945b5SGregory Neil Shapiro # endif 1296*5b0945b5SGregory Neil Shapiro 1297193538b7SGregory Neil Shapiro *statp = EX_OK; 1298*5b0945b5SGregory Neil Shapiro resqdomain = res_querydomain; 1299*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 1300*5b0945b5SGregory Neil Shapiro if (tTd(8, 110)) 1301*5b0945b5SGregory Neil Shapiro resqdomain = tstdns_querydomain; 1302*5b0945b5SGregory Neil Shapiro # endif 1303193538b7SGregory Neil Shapiro 1304c2aa98e2SPeter Wemm /* 1305c2aa98e2SPeter Wemm ** Initialize domain search list. If there is at least one 1306c2aa98e2SPeter Wemm ** dot in the name, search the unmodified name first so we 1307c2aa98e2SPeter Wemm ** find "vse.CS" in Czechoslovakia instead of in the local 130806f25ae9SGregory Neil Shapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 130906f25ae9SGregory Neil Shapiro ** longer a country named Czechoslovakia but this type of problem 131006f25ae9SGregory Neil Shapiro ** is still present. 1311c2aa98e2SPeter Wemm ** 1312c2aa98e2SPeter Wemm ** Older versions of the resolver could create this 1313c2aa98e2SPeter Wemm ** list by tearing apart the host name. 1314c2aa98e2SPeter Wemm */ 1315c2aa98e2SPeter Wemm 1316c2aa98e2SPeter Wemm loopcnt = 0; 1317c2aa98e2SPeter Wemm cnameloop: 1318c2aa98e2SPeter Wemm /* Check for dots in the name */ 1319c2aa98e2SPeter Wemm for (cp = host, n = 0; *cp != '\0'; cp++) 1320c2aa98e2SPeter Wemm if (*cp == '.') 1321c2aa98e2SPeter Wemm n++; 1322c2aa98e2SPeter Wemm 1323c2aa98e2SPeter Wemm /* 1324c2aa98e2SPeter Wemm ** Build the search list. 1325c2aa98e2SPeter Wemm ** If there is at least one dot in name, start with a null 1326c2aa98e2SPeter Wemm ** domain to search the unmodified name first. 1327c2aa98e2SPeter Wemm ** If name does not end with a dot and search up local domain 1328c2aa98e2SPeter Wemm ** tree desired, append each local domain component to the 1329c2aa98e2SPeter Wemm ** search list; if name contains no dots and default domain 1330c2aa98e2SPeter Wemm ** name is desired, append default domain name to search list; 1331c2aa98e2SPeter Wemm ** else if name ends in a dot, remove that dot. 1332c2aa98e2SPeter Wemm */ 1333c2aa98e2SPeter Wemm 1334*5b0945b5SGregory Neil Shapiro sli = 0; 1335c2aa98e2SPeter Wemm if (n > 0) 1336*5b0945b5SGregory Neil Shapiro searchlist[sli++] = ""; 1337*5b0945b5SGregory Neil Shapiro # if DNSSEC_TEST 1338*5b0945b5SGregory Neil Shapiro if (NameSearchList != NULL) 1339*5b0945b5SGregory Neil Shapiro { 1340*5b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE); 1341*5b0945b5SGregory Neil Shapiro searchlist[sli++] = NameSearchList; 1342*5b0945b5SGregory Neil Shapiro } 1343*5b0945b5SGregory Neil Shapiro # endif 1344c2aa98e2SPeter Wemm if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 1345c2aa98e2SPeter Wemm { 134606f25ae9SGregory Neil Shapiro /* make sure there are less than MAXDNSRCH domains */ 134706f25ae9SGregory Neil Shapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 1348*5b0945b5SGregory Neil Shapiro *domain != NULL && ret < MAXDNSRCH && sli < SLSIZE; 134906f25ae9SGregory Neil Shapiro ret++) 1350*5b0945b5SGregory Neil Shapiro searchlist[sli++] = *domain++; 1351c2aa98e2SPeter Wemm } 1352c2aa98e2SPeter Wemm else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 1353c2aa98e2SPeter Wemm { 1354*5b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE); 1355*5b0945b5SGregory Neil Shapiro searchlist[sli++] = _res.defdname; 1356c2aa98e2SPeter Wemm } 1357c2aa98e2SPeter Wemm else if (*cp == '.') 1358c2aa98e2SPeter Wemm { 1359c2aa98e2SPeter Wemm *cp = '\0'; 1360c2aa98e2SPeter Wemm } 1361*5b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE); 1362*5b0945b5SGregory Neil Shapiro searchlist[sli] = NULL; 1363c2aa98e2SPeter Wemm 1364c2aa98e2SPeter Wemm /* 1365c2aa98e2SPeter Wemm ** Now loop through the search list, appending each domain in turn 1366c2aa98e2SPeter Wemm ** name and searching for a match. 1367c2aa98e2SPeter Wemm */ 1368c2aa98e2SPeter Wemm 1369c2aa98e2SPeter Wemm mxmatch = NULL; 1370a7ec597cSGregory Neil Shapiro initial = T_A; 1371a7ec597cSGregory Neil Shapiro # if NETINET6 1372a7ec597cSGregory Neil Shapiro if (InetMode == AF_INET6) 1373a7ec597cSGregory Neil Shapiro initial = T_AAAA; 1374*5b0945b5SGregory Neil Shapiro # endif 1375a7ec597cSGregory Neil Shapiro qtype = initial; 1376c2aa98e2SPeter Wemm 1377*5b0945b5SGregory Neil Shapiro for (sli = 0; sli < SLSIZE; ) 1378c2aa98e2SPeter Wemm { 1379*5b0945b5SGregory Neil Shapiro dp = searchlist[sli]; 1380*5b0945b5SGregory Neil Shapiro if (NULL == dp) 1381*5b0945b5SGregory Neil Shapiro break; 1382a7ec597cSGregory Neil Shapiro if (qtype == initial) 138340266059SGregory Neil Shapiro gotmx = false; 1384c2aa98e2SPeter Wemm if (tTd(8, 5)) 138540266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 1386*5b0945b5SGregory Neil Shapiro host, dp, 138706f25ae9SGregory Neil Shapiro # if NETINET6 138806f25ae9SGregory Neil Shapiro qtype == T_AAAA ? "AAAA" : 1389*5b0945b5SGregory Neil Shapiro # endif 139006f25ae9SGregory Neil Shapiro qtype == T_A ? "A" : 139106f25ae9SGregory Neil Shapiro qtype == T_MX ? "MX" : 139206f25ae9SGregory Neil Shapiro "???"); 1393193538b7SGregory Neil Shapiro errno = 0; 1394*5b0945b5SGregory Neil Shapiro hp = (HEADER *) &answer; 1395*5b0945b5SGregory Neil Shapiro ret = (*resqdomain)(host, dp, C_IN, qtype, 1396c2aa98e2SPeter Wemm answer.qb2, sizeof(answer.qb2)); 1397c2aa98e2SPeter Wemm if (ret <= 0) 1398c2aa98e2SPeter Wemm { 139940266059SGregory Neil Shapiro int save_errno = errno; 1400c2aa98e2SPeter Wemm 140140266059SGregory Neil Shapiro if (tTd(8, 7)) 140240266059SGregory Neil Shapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 140340266059SGregory Neil Shapiro save_errno, h_errno); 140440266059SGregory Neil Shapiro 140540266059SGregory Neil Shapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 1406c2aa98e2SPeter Wemm { 1407193538b7SGregory Neil Shapiro /* 140840266059SGregory Neil Shapiro ** the name server seems to be down or broken. 1409193538b7SGregory Neil Shapiro */ 1410193538b7SGregory Neil Shapiro 1411602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(TRY_AGAIN); 1412*5b0945b5SGregory Neil Shapiro if (*dp == '\0') 1413605302a5SGregory Neil Shapiro { 1414605302a5SGregory Neil Shapiro if (*statp == EX_OK) 1415605302a5SGregory Neil Shapiro *statp = EX_TEMPFAIL; 1416605302a5SGregory Neil Shapiro goto nexttype; 1417605302a5SGregory Neil Shapiro } 1418c2aa98e2SPeter Wemm *statp = EX_TEMPFAIL; 141906f25ae9SGregory Neil Shapiro 1420602a2b1bSGregory Neil Shapiro if (WorkAroundBrokenAAAA) 1421602a2b1bSGregory Neil Shapiro { 1422193538b7SGregory Neil Shapiro /* 1423193538b7SGregory Neil Shapiro ** Only return if not TRY_AGAIN as an 1424193538b7SGregory Neil Shapiro ** attempt with a different qtype may 1425193538b7SGregory Neil Shapiro ** succeed (res_querydomain() calls 1426193538b7SGregory Neil Shapiro ** res_query() calls res_send() which 1427193538b7SGregory Neil Shapiro ** sets errno to ETIMEDOUT if the 1428193538b7SGregory Neil Shapiro ** nameservers could be contacted but 1429193538b7SGregory Neil Shapiro ** didn't give an answer). 1430193538b7SGregory Neil Shapiro */ 1431193538b7SGregory Neil Shapiro 143240266059SGregory Neil Shapiro if (save_errno != ETIMEDOUT) 1433*5b0945b5SGregory Neil Shapiro goto error; 1434602a2b1bSGregory Neil Shapiro } 143540266059SGregory Neil Shapiro else 1436*5b0945b5SGregory Neil Shapiro goto error; 1437c2aa98e2SPeter Wemm } 1438c2aa98e2SPeter Wemm 1439605302a5SGregory Neil Shapiro nexttype: 1440c2aa98e2SPeter Wemm if (h_errno != HOST_NOT_FOUND) 1441c2aa98e2SPeter Wemm { 1442c2aa98e2SPeter Wemm /* might have another type of interest */ 144306f25ae9SGregory Neil Shapiro # if NETINET6 144440266059SGregory Neil Shapiro if (qtype == T_AAAA) 144506f25ae9SGregory Neil Shapiro { 1446c2aa98e2SPeter Wemm qtype = T_A; 1447c2aa98e2SPeter Wemm continue; 1448c2aa98e2SPeter Wemm } 144940266059SGregory Neil Shapiro else 145006f25ae9SGregory Neil Shapiro # endif /* NETINET6 */ 145140266059SGregory Neil Shapiro if (qtype == T_A && !gotmx && 1452*5b0945b5SGregory Neil Shapiro (trymx || *dp == '\0')) 1453c2aa98e2SPeter Wemm { 1454c2aa98e2SPeter Wemm qtype = T_MX; 1455c2aa98e2SPeter Wemm continue; 1456c2aa98e2SPeter Wemm } 1457c2aa98e2SPeter Wemm } 1458c2aa98e2SPeter Wemm 1459c2aa98e2SPeter Wemm /* definite no -- try the next domain */ 1460*5b0945b5SGregory Neil Shapiro sli++; 1461a7ec597cSGregory Neil Shapiro qtype = initial; 1462c2aa98e2SPeter Wemm continue; 1463c2aa98e2SPeter Wemm } 1464c2aa98e2SPeter Wemm else if (tTd(8, 7)) 146540266059SGregory Neil Shapiro sm_dprintf("\tYES\n"); 1466c2aa98e2SPeter Wemm 1467c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */ 1468c2aa98e2SPeter Wemm if (ret > sizeof(answer)) 1469c2aa98e2SPeter Wemm ret = sizeof(answer); 1470af9557fdSGregory Neil Shapiro SM_ASSERT(ret >= 0); 1471c2aa98e2SPeter Wemm 1472c2aa98e2SPeter Wemm /* 1473c2aa98e2SPeter Wemm ** Appear to have a match. Confirm it by searching for A or 1474c2aa98e2SPeter Wemm ** CNAME records. If we don't have a local domain 1475c2aa98e2SPeter Wemm ** wild card MX record, we will accept MX as well. 1476c2aa98e2SPeter Wemm */ 1477c2aa98e2SPeter Wemm 147840266059SGregory Neil Shapiro ap = (unsigned char *) &answer + HFIXEDSZ; 147940266059SGregory Neil Shapiro eom = (unsigned char *) &answer + ret; 1480c2aa98e2SPeter Wemm 1481*5b0945b5SGregory Neil Shapiro if (0 == hp->ad) 1482*5b0945b5SGregory Neil Shapiro ad = false; 1483*5b0945b5SGregory Neil Shapiro 1484c2aa98e2SPeter Wemm /* skip question part of response -- we know what we asked */ 148540266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 148606f25ae9SGregory Neil Shapiro qdcount--; 148706f25ae9SGregory Neil Shapiro ap += ret + QFIXEDSZ) 1488c2aa98e2SPeter Wemm { 1489c2aa98e2SPeter Wemm if ((ret = dn_skipname(ap, eom)) < 0) 1490c2aa98e2SPeter Wemm { 1491c2aa98e2SPeter Wemm if (tTd(8, 20)) 149240266059SGregory Neil Shapiro sm_dprintf("qdcount failure (%d)\n", 149340266059SGregory Neil Shapiro ntohs((unsigned short) hp->qdcount)); 1494c2aa98e2SPeter Wemm *statp = EX_SOFTWARE; 1495*5b0945b5SGregory Neil Shapiro goto error; 1496c2aa98e2SPeter Wemm } 1497c2aa98e2SPeter Wemm } 1498c2aa98e2SPeter Wemm 149940266059SGregory Neil Shapiro amatch = false; 150040266059SGregory Neil Shapiro for (ancount = ntohs((unsigned short) hp->ancount); 150106f25ae9SGregory Neil Shapiro --ancount >= 0 && ap < eom; 1502c2aa98e2SPeter Wemm ap += n) 1503c2aa98e2SPeter Wemm { 150440266059SGregory Neil Shapiro n = dn_expand((unsigned char *) &answer, eom, ap, 1505d0cef73dSGregory Neil Shapiro (RES_UNC_T) nbuf, sizeof(nbuf)); 1506c2aa98e2SPeter Wemm if (n < 0) 1507c2aa98e2SPeter Wemm break; 1508c2aa98e2SPeter Wemm ap += n; 1509c2aa98e2SPeter Wemm GETSHORT(type, ap); 151040266059SGregory Neil Shapiro ap += INT16SZ; /* skip over class */ 151140266059SGregory Neil Shapiro GETLONG(ttl, ap); 151240266059SGregory Neil Shapiro GETSHORT(n, ap); /* rdlength */ 1513c2aa98e2SPeter Wemm switch (type) 1514c2aa98e2SPeter Wemm { 1515c2aa98e2SPeter Wemm case T_MX: 151640266059SGregory Neil Shapiro gotmx = true; 1517*5b0945b5SGregory Neil Shapiro if (*dp != '\0' && HasWildcardMX) 1518c2aa98e2SPeter Wemm { 1519c2aa98e2SPeter Wemm /* 1520c2aa98e2SPeter Wemm ** If we are using MX matches and have 1521c2aa98e2SPeter Wemm ** not yet gotten one, save this one 1522c2aa98e2SPeter Wemm ** but keep searching for an A or 1523c2aa98e2SPeter Wemm ** CNAME match. 1524c2aa98e2SPeter Wemm */ 1525c2aa98e2SPeter Wemm 1526c2aa98e2SPeter Wemm if (trymx && mxmatch == NULL) 1527*5b0945b5SGregory Neil Shapiro mxmatch = dp; 1528c2aa98e2SPeter Wemm continue; 1529c2aa98e2SPeter Wemm } 1530c2aa98e2SPeter Wemm 1531c2aa98e2SPeter Wemm /* 1532c2aa98e2SPeter Wemm ** If we did not append a domain name, this 1533c2aa98e2SPeter Wemm ** must have been a canonical name to start 1534c2aa98e2SPeter Wemm ** with. Even if we did append a domain name, 1535c2aa98e2SPeter Wemm ** in the absence of a wildcard MX this must 1536c2aa98e2SPeter Wemm ** still be a real MX match. 1537c2aa98e2SPeter Wemm ** Such MX matches are as good as an A match, 1538c2aa98e2SPeter Wemm ** fall through. 1539c2aa98e2SPeter Wemm */ 154006f25ae9SGregory Neil Shapiro /* FALLTHROUGH */ 154106f25ae9SGregory Neil Shapiro 154206f25ae9SGregory Neil Shapiro # if NETINET6 154306f25ae9SGregory Neil Shapiro case T_AAAA: 1544*5b0945b5SGregory Neil Shapiro # endif 1545c2aa98e2SPeter Wemm case T_A: 1546c2aa98e2SPeter Wemm /* Flag that a good match was found */ 154740266059SGregory Neil Shapiro amatch = true; 1548c2aa98e2SPeter Wemm 1549c2aa98e2SPeter Wemm /* continue in case a CNAME also exists */ 1550c2aa98e2SPeter Wemm continue; 1551c2aa98e2SPeter Wemm 1552c2aa98e2SPeter Wemm case T_CNAME: 1553c2aa98e2SPeter Wemm if (DontExpandCnames) 1554c2aa98e2SPeter Wemm { 1555c2aa98e2SPeter Wemm /* got CNAME -- guaranteed canonical */ 155640266059SGregory Neil Shapiro amatch = true; 1557c2aa98e2SPeter Wemm break; 1558c2aa98e2SPeter Wemm } 1559c2aa98e2SPeter Wemm 1560c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH) 1561c2aa98e2SPeter Wemm { 1562c2aa98e2SPeter Wemm /*XXX should notify postmaster XXX*/ 1563c2aa98e2SPeter Wemm message("DNS failure: CNAME loop for %s", 1564c2aa98e2SPeter Wemm host); 1565c2aa98e2SPeter Wemm if (CurEnv->e_message == NULL) 1566c2aa98e2SPeter Wemm { 1567c2aa98e2SPeter Wemm char ebuf[MAXLINE]; 1568c2aa98e2SPeter Wemm 156940266059SGregory Neil Shapiro (void) sm_snprintf(ebuf, 1570d0cef73dSGregory Neil Shapiro sizeof(ebuf), 1571c2aa98e2SPeter Wemm "Deferred: DNS failure: CNAME loop for %.100s", 1572c2aa98e2SPeter Wemm host); 157340266059SGregory Neil Shapiro CurEnv->e_message = 157440266059SGregory Neil Shapiro sm_rpool_strdup_x( 157540266059SGregory Neil Shapiro CurEnv->e_rpool, ebuf); 1576c2aa98e2SPeter Wemm } 1577602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(NO_RECOVERY); 1578c2aa98e2SPeter Wemm *statp = EX_CONFIG; 1579*5b0945b5SGregory Neil Shapiro goto error; 1580c2aa98e2SPeter Wemm } 1581c2aa98e2SPeter Wemm 1582c2aa98e2SPeter Wemm /* value points at name */ 158340266059SGregory Neil Shapiro if ((ret = dn_expand((unsigned char *)&answer, 158440266059SGregory Neil Shapiro eom, ap, (RES_UNC_T) nbuf, 158540266059SGregory Neil Shapiro sizeof(nbuf))) < 0) 1586c2aa98e2SPeter Wemm break; 158740266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1588c2aa98e2SPeter Wemm 1589c2aa98e2SPeter Wemm /* 1590c2aa98e2SPeter Wemm ** RFC 1034 section 3.6 specifies that CNAME 1591c2aa98e2SPeter Wemm ** should point at the canonical name -- but 1592c2aa98e2SPeter Wemm ** urges software to try again anyway. 1593c2aa98e2SPeter Wemm */ 1594c2aa98e2SPeter Wemm 1595c2aa98e2SPeter Wemm goto cnameloop; 1596c2aa98e2SPeter Wemm 1597c2aa98e2SPeter Wemm default: 1598c2aa98e2SPeter Wemm /* not a record of interest */ 1599c2aa98e2SPeter Wemm continue; 1600c2aa98e2SPeter Wemm } 1601c2aa98e2SPeter Wemm } 1602c2aa98e2SPeter Wemm 1603c2aa98e2SPeter Wemm if (amatch) 1604c2aa98e2SPeter Wemm { 1605c2aa98e2SPeter Wemm /* 1606c2aa98e2SPeter Wemm ** Got a good match -- either an A, CNAME, or an 1607c2aa98e2SPeter Wemm ** exact MX record. Save it and get out of here. 1608c2aa98e2SPeter Wemm */ 1609c2aa98e2SPeter Wemm 1610*5b0945b5SGregory Neil Shapiro mxmatch = dp; 1611c2aa98e2SPeter Wemm break; 1612c2aa98e2SPeter Wemm } 1613c2aa98e2SPeter Wemm 1614c2aa98e2SPeter Wemm /* 1615c2aa98e2SPeter Wemm ** Nothing definitive yet. 1616c2aa98e2SPeter Wemm ** If this was a T_A query and we haven't yet found a MX 1617c2aa98e2SPeter Wemm ** match, try T_MX if allowed to do so. 1618c2aa98e2SPeter Wemm ** Otherwise, try the next domain. 1619c2aa98e2SPeter Wemm */ 1620c2aa98e2SPeter Wemm 162106f25ae9SGregory Neil Shapiro # if NETINET6 162240266059SGregory Neil Shapiro if (qtype == T_AAAA) 1623c2aa98e2SPeter Wemm qtype = T_A; 162440266059SGregory Neil Shapiro else 1625*5b0945b5SGregory Neil Shapiro # endif 1626*5b0945b5SGregory Neil Shapiro if (qtype == T_A && !gotmx && (trymx || *dp == '\0')) 1627c2aa98e2SPeter Wemm qtype = T_MX; 1628c2aa98e2SPeter Wemm else 1629c2aa98e2SPeter Wemm { 1630a7ec597cSGregory Neil Shapiro qtype = initial; 1631*5b0945b5SGregory Neil Shapiro sli++; 1632c2aa98e2SPeter Wemm } 1633c2aa98e2SPeter Wemm } 1634c2aa98e2SPeter Wemm 1635c2aa98e2SPeter Wemm /* if nothing was found, we are done */ 1636c2aa98e2SPeter Wemm if (mxmatch == NULL) 1637c2aa98e2SPeter Wemm { 1638193538b7SGregory Neil Shapiro if (*statp == EX_OK) 1639c2aa98e2SPeter Wemm *statp = EX_NOHOST; 1640*5b0945b5SGregory Neil Shapiro goto error; 1641c2aa98e2SPeter Wemm } 1642c2aa98e2SPeter Wemm 1643c2aa98e2SPeter Wemm /* 1644c2aa98e2SPeter Wemm ** Create canonical name and return. 1645c2aa98e2SPeter Wemm ** If saved domain name is null, name was already canonical. 1646c2aa98e2SPeter Wemm ** Otherwise append the saved domain name. 1647c2aa98e2SPeter Wemm */ 1648c2aa98e2SPeter Wemm 1649d0cef73dSGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host, 1650c2aa98e2SPeter Wemm *mxmatch == '\0' ? "" : ".", 1651c2aa98e2SPeter Wemm MAXDNAME, mxmatch); 165240266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize); 1653c2aa98e2SPeter Wemm if (tTd(8, 5)) 165440266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: %s\n", host); 1655c2aa98e2SPeter Wemm *statp = EX_OK; 165640266059SGregory Neil Shapiro 165740266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */ 165840266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL) 165940266059SGregory Neil Shapiro *pttl = ttl; 1660*5b0945b5SGregory Neil Shapiro # if DANE 1661*5b0945b5SGregory Neil Shapiro _res.options = old_options; 1662*5b0945b5SGregory Neil Shapiro # endif 1663*5b0945b5SGregory Neil Shapiro return ad ? HOST_SECURE : HOST_OK; 1664*5b0945b5SGregory Neil Shapiro 1665*5b0945b5SGregory Neil Shapiro error: 1666*5b0945b5SGregory Neil Shapiro # if DANE 1667*5b0945b5SGregory Neil Shapiro _res.options = old_options; 1668*5b0945b5SGregory Neil Shapiro # endif 1669*5b0945b5SGregory Neil Shapiro return HOST_NOTFOUND; 1670c2aa98e2SPeter Wemm } 1671*5b0945b5SGregory Neil Shapiro 1672c2aa98e2SPeter Wemm #endif /* NAMED_BIND */ 1673