138032Speter/* 2261194Sgshapiro * Copyright (c) 1998-2004, 2006, 2010 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 15168515Sgshapiro#include "map.h" 16363466Sgshapiro#if _FFR_EAI 17363466Sgshapiro#include <unicode/uidna.h> 18363466Sgshapiro#endif 1938032Speter 2090792Sgshapiro#if NAMED_BIND 21266527SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name server)") 22363466Sgshapiro#else 23266527SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)") 24363466Sgshapiro#endif 2538032Speter 2638032Speter#if NAMED_BIND 2738032Speter 2864562Sgshapiro# include <arpa/inet.h> 29363466Sgshapiro# include <sm_resolve.h> 30363466Sgshapiro# if DANE 31363466Sgshapiro# include <tls.h> 32363466Sgshapiro# ifndef SM_NEG_TTL 33363466Sgshapiro# define SM_NEG_TTL 60 /* "negative" TTL */ 34363466Sgshapiro# endif 35363466Sgshapiro# endif 3638032Speter 3790792Sgshapiro 3864562Sgshapiro# ifndef MXHOSTBUFSIZE 3964562Sgshapiro# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 40363466Sgshapiro# endif 4138032Speter 4238032Speterstatic char MXHostBuf[MXHOSTBUFSIZE]; 43363466Sgshapiro# if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) 4490792Sgshapiro ERROR: _MXHOSTBUFSIZE is out of range 45363466Sgshapiro# endif 4638032Speter 4764562Sgshapiro# ifndef MAXDNSRCH 4864562Sgshapiro# define MAXDNSRCH 6 /* number of possible domains to search */ 49363466Sgshapiro# endif 5038032Speter 5164562Sgshapiro# ifndef RES_DNSRCH_VARIABLE 5264562Sgshapiro# define RES_DNSRCH_VARIABLE _res.dnsrch 53363466Sgshapiro# endif 5438032Speter 5564562Sgshapiro# ifndef NO_DATA 5664562Sgshapiro# define NO_DATA NO_ADDRESS 57363466Sgshapiro# endif 5838032Speter 5964562Sgshapiro# ifndef HFIXEDSZ 6064562Sgshapiro# define HFIXEDSZ 12 /* sizeof(HEADER) */ 61363466Sgshapiro# endif 6238032Speter 6364562Sgshapiro# define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 6464562Sgshapiro 6564562Sgshapiro# if defined(__RES) && (__RES >= 19940415) 6664562Sgshapiro# define RES_UNC_T char * 67363466Sgshapiro# else 6890792Sgshapiro# define RES_UNC_T unsigned char * 69363466Sgshapiro# endif 7064562Sgshapiro 7164562Sgshapirostatic int mxrand __P((char *)); 7290792Sgshapirostatic int fallbackmxrr __P((int, unsigned short *, char **)); 7364562Sgshapiro 74363466Sgshapiro# if DANE 75363466Sgshapiro 7690792Sgshapiro/* 77363466Sgshapiro** TLSAADD -- add TLSA records to dane_tlsa entry 78363466Sgshapiro** 79363466Sgshapiro** Parameters: 80363466Sgshapiro** name -- key for stab entry (for debugging output) 81363466Sgshapiro** dr -- DNS reply 82363466Sgshapiro** dane_tlsa -- dane_tlsa entry 83363466Sgshapiro** dnsrc -- DNS lookup return code (h_errno) 84363466Sgshapiro** n -- current number of TLSA records in dane_tlsa entry 85363466Sgshapiro** pttl -- (pointer to) TTL (in/out) 86363466Sgshapiro** level -- recursion level (CNAMEs) 87363466Sgshapiro** 88363466Sgshapiro** Returns: 89363466Sgshapiro** new number of TLSA records 90363466Sgshapiro*/ 91363466Sgshapiro 92363466Sgshapirostatic int tlsaadd __P((const char *, DNS_REPLY_T *, dane_tlsa_P, int, int, 93363466Sgshapiro unsigned int *, int)); 94363466Sgshapiro 95363466Sgshapirostatic int 96363466Sgshapirotlsaadd(name, dr, dane_tlsa, dnsrc, n, pttl, level) 97363466Sgshapiro const char *name; 98363466Sgshapiro DNS_REPLY_T *dr; 99363466Sgshapiro dane_tlsa_P dane_tlsa; 100363466Sgshapiro int dnsrc; 101363466Sgshapiro int n; 102363466Sgshapiro unsigned int *pttl; 103363466Sgshapiro int level; 104363466Sgshapiro{ 105363466Sgshapiro RESOURCE_RECORD_T *rr; 106363466Sgshapiro unsigned int ttl; 107363466Sgshapiro int nprev; 108363466Sgshapiro 109363466Sgshapiro if (dnsrc != 0) 110363466Sgshapiro { 111363466Sgshapiro if (tTd(8, 2)) 112363466Sgshapiro sm_dprintf("tlsaadd(%s), prev=%d, dnsrc=%d\n", 113363466Sgshapiro name, dane_tlsa->dane_tlsa_dnsrc, dnsrc); 114363466Sgshapiro 115363466Sgshapiro /* check previous error and keep the "most important" one? */ 116363466Sgshapiro dane_tlsa->dane_tlsa_dnsrc = dnsrc; 117363466Sgshapiro# if DNSSEC_TEST 118363466Sgshapiro if (tTd(8, 110)) 119363466Sgshapiro *pttl = tTdlevel(8)-110; /* how to make this an option? */ 120363466Sgshapiro else 121363466Sgshapiro# else 122363466Sgshapiro *pttl = SM_NEG_TTL; 123363466Sgshapiro# endif 124363466Sgshapiro 125363466Sgshapiro return n; 126363466Sgshapiro } 127363466Sgshapiro if (dr == NULL) 128363466Sgshapiro return n; 129363466Sgshapiro if (dr->dns_r_h.ad != 1 && Dane == DANE_SECURE) /* not secure? */ 130363466Sgshapiro return n; 131363466Sgshapiro ttl = *pttl; 132363466Sgshapiro 133363466Sgshapiro /* first: try to find TLSA records */ 134363466Sgshapiro nprev = n; 135363466Sgshapiro for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR; 136363466Sgshapiro rr = rr->rr_next) 137363466Sgshapiro { 138363466Sgshapiro int tlsa_chk; 139363466Sgshapiro 140363466Sgshapiro if (rr->rr_type != T_TLSA) 141363466Sgshapiro { 142363466Sgshapiro if (rr->rr_type != T_CNAME && tTd(8, 8)) 143363466Sgshapiro sm_dprintf("tlsaadd(%s), type=%s\n", name, 144363466Sgshapiro dns_type_to_string(rr->rr_type)); 145363466Sgshapiro continue; 146363466Sgshapiro } 147363466Sgshapiro tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data, rr->rr_size, name, 148363466Sgshapiro true); 149363466Sgshapiro if (!TLSA_IS_VALID(tlsa_chk)) 150363466Sgshapiro continue; 151363466Sgshapiro 152363466Sgshapiro /* 153363466Sgshapiro ** To do: the RRs should be sorted (by "complexity") -- 154363466Sgshapiro ** when more than one type is supported. 155363466Sgshapiro */ 156363466Sgshapiro 157363466Sgshapiro dane_tlsa->dane_tlsa_rr[n] = rr->rr_u.rr_data; 158363466Sgshapiro dane_tlsa->dane_tlsa_len[n] = rr->rr_size; 159363466Sgshapiro if (tTd(8, 2)) 160363466Sgshapiro { 161363466Sgshapiro unsigned char *p; 162363466Sgshapiro 163363466Sgshapiro p = rr->rr_u.rr_data; 164363466Sgshapiro sm_dprintf("tlsaadd(%s), n=%d, %d-%d-%d:%02x\n", name, 165363466Sgshapiro n, (int)p[0], (int)p[1], (int)p[2], (int)p[3]); 166363466Sgshapiro } 167363466Sgshapiro 168363466Sgshapiro /* require some minimum TTL? */ 169363466Sgshapiro if (ttl > rr->rr_ttl && rr->rr_ttl > 0) 170363466Sgshapiro ttl = rr->rr_ttl; 171363466Sgshapiro 172363466Sgshapiro /* hack: instead of copying the data, just "take it over" */ 173363466Sgshapiro rr->rr_u.rr_data = NULL; 174363466Sgshapiro ++n; 175363466Sgshapiro } 176363466Sgshapiro 177363466Sgshapiro /* second: check for CNAME records, but only if no TLSA RR was added */ 178363466Sgshapiro for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR && nprev == n; 179363466Sgshapiro rr = rr->rr_next) 180363466Sgshapiro { 181363466Sgshapiro DNS_REPLY_T *drc; 182363466Sgshapiro int err, herr; 183363466Sgshapiro 184363466Sgshapiro if (rr->rr_type != T_CNAME) 185363466Sgshapiro continue; 186363466Sgshapiro if (level > 1) 187363466Sgshapiro { 188363466Sgshapiro if (tTd(8, 2)) 189363466Sgshapiro sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d\n", 190363466Sgshapiro name, rr->rr_u.rr_txt, level); 191363466Sgshapiro continue; 192363466Sgshapiro } 193363466Sgshapiro 194363466Sgshapiro drc = dns_lookup_int(rr->rr_u.rr_txt, C_IN, T_TLSA, 0, 0, 195363466Sgshapiro (Dane == DANE_SECURE && 196363466Sgshapiro !TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX)) 197363466Sgshapiro ? SM_RES_DNSSEC : 0, 198363466Sgshapiro RR_RAW, &err, &herr); 199363466Sgshapiro 200363466Sgshapiro if (tTd(8, 2)) 201363466Sgshapiro sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d, dr=%p, ad=%d, err=%d, herr=%d\n", 202363466Sgshapiro name, rr->rr_u.rr_txt, level, 203363466Sgshapiro (void *)drc, drc != NULL ? drc->dns_r_h.ad : -1, 204363466Sgshapiro err, herr); 205363466Sgshapiro nprev = n = tlsaadd(name, drc, dane_tlsa, herr, n, pttl, 206363466Sgshapiro level + 1); 207363466Sgshapiro dns_free_data(drc); 208363466Sgshapiro drc = NULL; 209363466Sgshapiro } 210363466Sgshapiro 211363466Sgshapiro *pttl = ttl; 212363466Sgshapiro return n; 213363466Sgshapiro} 214363466Sgshapiro 215363466Sgshapiro/* 216363466Sgshapiro** GETTLSA -- get TLSA records for named host using DNS 217363466Sgshapiro** 218363466Sgshapiro** Parameters: 219363466Sgshapiro** host -- host 220363466Sgshapiro** name -- name for stab entry key (if NULL: host) 221363466Sgshapiro** pste -- (pointer to) stab entry (output) 222363466Sgshapiro** flags -- TLSAFL* 223363466Sgshapiro** mxttl -- TTL of MX (or host) 224363466Sgshapiro** port -- port 225363466Sgshapiro** 226363466Sgshapiro** Returns: 227363466Sgshapiro** The number of TLSA records found. 228363466Sgshapiro** <0 if there is an internal failure. 229363466Sgshapiro** 230363466Sgshapiro** Side effects: 231363466Sgshapiro** Enters TLSA RRs into stab(). 232363466Sgshapiro** If the DNS lookup fails temporarily, an "empty" entry is 233363466Sgshapiro** created with that DNS error code. 234363466Sgshapiro*/ 235363466Sgshapiro 236363466Sgshapiroint 237363466Sgshapirogettlsa(host, name, pste, flags, mxttl, port) 238363466Sgshapiro char *host; 239363466Sgshapiro char *name; 240363466Sgshapiro STAB **pste; 241363466Sgshapiro unsigned long flags; 242363466Sgshapiro unsigned int mxttl; 243363466Sgshapiro unsigned int port; 244363466Sgshapiro{ 245363466Sgshapiro DNS_REPLY_T *dr; 246363466Sgshapiro dane_tlsa_P dane_tlsa; 247363466Sgshapiro STAB *ste; 248363466Sgshapiro time_t now; 249363466Sgshapiro unsigned int ttl; 250363466Sgshapiro int n_rrs, len, err, herr; 251363466Sgshapiro bool isrname; 252363466Sgshapiro char nbuf[MAXDNAME]; 253363466Sgshapiro char key[MAXDNAME]; 254363466Sgshapiro 255363466Sgshapiro SM_REQUIRE(host != NULL); 256363466Sgshapiro if (pste != NULL) 257363466Sgshapiro *pste = NULL; 258363466Sgshapiro if ('\0' == *host) 259363466Sgshapiro return 0; 260363466Sgshapiro 261363466Sgshapiro isrname = NULL == name; 262363466Sgshapiro if (isrname) 263363466Sgshapiro name = host; 264363466Sgshapiro now = 0; 265363466Sgshapiro n_rrs = 0; 266363466Sgshapiro dr = NULL; 267363466Sgshapiro dane_tlsa = NULL; 268363466Sgshapiro len = strlen(name); 269363466Sgshapiro if (len > 1 && name[len - 1] == '.') 270363466Sgshapiro { 271363466Sgshapiro len--; 272363466Sgshapiro name[len] = '\0'; 273363466Sgshapiro } 274363466Sgshapiro else 275363466Sgshapiro len = -1; 276363466Sgshapiro if (0 == port || tTd(66, 10)) 277363466Sgshapiro port = 25; 278363466Sgshapiro (void) sm_snprintf(key, sizeof(key), "_%u..%s", port, name); 279363466Sgshapiro ste = stab(key, ST_TLSA_RR, ST_FIND); 280363466Sgshapiro if (tTd(8, 2)) 281363466Sgshapiro sm_dprintf("gettlsa(%s, %s, ste=%p, pste=%p, flags=%lX, port=%d)\n", 282363466Sgshapiro host, isrname ? "" : name, (void *)ste, (void *)pste, 283363466Sgshapiro flags, port); 284363466Sgshapiro 285363466Sgshapiro if (ste != NULL) 286363466Sgshapiro { 287363466Sgshapiro dane_tlsa = ste->s_tlsa; 288363466Sgshapiro if ((TLSAFLADMX & flags) != 0) 289363466Sgshapiro TLSA_CLR_FL(ste->s_tlsa, TLSAFLNOADMX); 290363466Sgshapiro } 291363466Sgshapiro 292363466Sgshapiro /* Do not reload TLSA RRs if the MX RRs were not securely retrieved. */ 293363466Sgshapiro if (pste != NULL 294363466Sgshapiro && dane_tlsa != NULL && TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX) 295363466Sgshapiro && DANE_SECURE == Dane) 296363466Sgshapiro goto end; 297363466Sgshapiro 298363466Sgshapiro if (ste != NULL) 299363466Sgshapiro { 300363466Sgshapiro SM_ASSERT(dane_tlsa != NULL); 301363466Sgshapiro now = curtime(); 302363466Sgshapiro if (dane_tlsa->dane_tlsa_exp <= now 303363466Sgshapiro && 0 == (TLSAFLNOEXP & flags)) 304363466Sgshapiro dane_tlsa_clr(dane_tlsa); 305363466Sgshapiro else 306363466Sgshapiro { 307363466Sgshapiro n_rrs = dane_tlsa->dane_tlsa_n; 308363466Sgshapiro goto end; 309363466Sgshapiro } 310363466Sgshapiro } 311363466Sgshapiro 312363466Sgshapiro if (dane_tlsa == NULL) 313363466Sgshapiro { 314363466Sgshapiro dane_tlsa = (dane_tlsa_P) sm_malloc(sizeof(*dane_tlsa)); 315363466Sgshapiro if (dane_tlsa == NULL) 316363466Sgshapiro { 317363466Sgshapiro n_rrs = -ENOMEM; 318363466Sgshapiro goto end; 319363466Sgshapiro } 320363466Sgshapiro memset(dane_tlsa, '\0', sizeof(*dane_tlsa)); 321363466Sgshapiro } 322363466Sgshapiro 323363466Sgshapiro /* There are flags to store -- just set those, do nothing else. */ 324363466Sgshapiro if (TLSA_STORE_FL(flags)) 325363466Sgshapiro { 326363466Sgshapiro dane_tlsa->dane_tlsa_flags = flags; 327363466Sgshapiro ttl = mxttl > 0 ? mxttl: SM_DEFAULT_TTL; 328363466Sgshapiro goto done; 329363466Sgshapiro } 330363466Sgshapiro 331363466Sgshapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "_%u._tcp.%s", port, host); 332363466Sgshapiro dr = dns_lookup_int(nbuf, C_IN, T_TLSA, 0, 0, 333363466Sgshapiro TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX) ? 0 : SM_RES_DNSSEC, 334363466Sgshapiro RR_RAW, &err, &herr); 335363466Sgshapiro if (tTd(8, 2)) 336363466Sgshapiro sm_dprintf("gettlsa(%s), dr=%p, ad=%d, err=%d, herr=%d\n", host, 337363466Sgshapiro (void *)dr, dr != NULL ? dr->dns_r_h.ad : -1, err, herr); 338363466Sgshapiro ttl = UINT_MAX; 339363466Sgshapiro n_rrs = tlsaadd(key, dr, dane_tlsa, herr, n_rrs, &ttl, 0); 340363466Sgshapiro 341363466Sgshapiro /* no valid entries found? */ 342363466Sgshapiro if (n_rrs == 0 && !TLSA_RR_TEMPFAIL(dane_tlsa)) 343363466Sgshapiro { 344363466Sgshapiro if (tTd(8, 2)) 345363466Sgshapiro sm_dprintf("gettlsa(%s), n_rrs=%d, herr=%d, status=NOT_ADDED\n", 346363466Sgshapiro host, n_rrs, dane_tlsa->dane_tlsa_dnsrc); 347363466Sgshapiro goto cleanup; 348363466Sgshapiro } 349363466Sgshapiro 350363466Sgshapiro done: 351363466Sgshapiro dane_tlsa->dane_tlsa_n = n_rrs; 352363466Sgshapiro if (!isrname) 353363466Sgshapiro { 354363466Sgshapiro SM_FREE(dane_tlsa->dane_tlsa_sni); 355363466Sgshapiro dane_tlsa->dane_tlsa_sni = sm_strdup(host); 356363466Sgshapiro } 357363466Sgshapiro if (NULL == ste) 358363466Sgshapiro { 359363466Sgshapiro ste = stab(key, ST_TLSA_RR, ST_ENTER); 360363466Sgshapiro if (NULL == ste) 361363466Sgshapiro goto error; 362363466Sgshapiro } 363363466Sgshapiro ste->s_tlsa = dane_tlsa; 364363466Sgshapiro if (now == 0) 365363466Sgshapiro now = curtime(); 366363466Sgshapiro dane_tlsa->dane_tlsa_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 367363466Sgshapiro dns_free_data(dr); 368363466Sgshapiro dr = NULL; 369363466Sgshapiro goto end; 370363466Sgshapiro 371363466Sgshapiro error: 372363466Sgshapiro if (tTd(8, 2)) 373363466Sgshapiro sm_dprintf("gettlsa(%s, %s), status=error\n", host, key); 374363466Sgshapiro n_rrs = -1; 375363466Sgshapiro cleanup: 376363466Sgshapiro if (NULL == ste) 377363466Sgshapiro dane_tlsa_free(dane_tlsa); 378363466Sgshapiro dns_free_data(dr); 379363466Sgshapiro dr = NULL; 380363466Sgshapiro 381363466Sgshapiro end: 382363466Sgshapiro if (pste != NULL && ste != NULL) 383363466Sgshapiro *pste = ste; 384363466Sgshapiro if (len > 0) 385363466Sgshapiro host[len] = '.'; 386363466Sgshapiro return n_rrs; 387363466Sgshapiro} 388363466Sgshapiro# endif /* DANE */ 389363466Sgshapiro 390363466Sgshapiro/* 39190792Sgshapiro** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 39290792Sgshapiro** 39390792Sgshapiro** We have to initialize this once before doing anything else. 39490792Sgshapiro** Moreover, we have to repeat this from time to time to avoid 39590792Sgshapiro** stale data, e.g., in persistent queue runners. 39690792Sgshapiro** This should be done in a parent process so the child 39790792Sgshapiro** processes have the right data. 39890792Sgshapiro** 39990792Sgshapiro** Parameters: 40090792Sgshapiro** host -- the name of the fallback MX host. 40190792Sgshapiro** 40290792Sgshapiro** Returns: 40390792Sgshapiro** number of MX records. 40490792Sgshapiro** 40590792Sgshapiro** Side Effects: 406132943Sgshapiro** Populates NumFallbackMXHosts and fbhosts. 40790792Sgshapiro** Sets renewal time (based on TTL). 40890792Sgshapiro*/ 40990792Sgshapiro 410132943Sgshapiroint NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ 41190792Sgshapirostatic char *fbhosts[MAXMXHOSTS + 1]; 41290792Sgshapiro 41390792Sgshapiroint 41490792Sgshapirogetfallbackmxrr(host) 41590792Sgshapiro char *host; 41690792Sgshapiro{ 41790792Sgshapiro int i, rcode; 41890792Sgshapiro int ttl; 41990792Sgshapiro static time_t renew = 0; 42090792Sgshapiro 42190792Sgshapiro#if 0 42290792Sgshapiro /* This is currently done before this function is called. */ 42390792Sgshapiro if (host == NULL || *host == '\0') 42490792Sgshapiro return 0; 42590792Sgshapiro#endif /* 0 */ 426132943Sgshapiro if (NumFallbackMXHosts > 0 && renew > curtime()) 427132943Sgshapiro return NumFallbackMXHosts; 428363466Sgshapiro 429363466Sgshapiro /* for DANE we need to invoke getmxrr() to get the TLSA RRs. */ 430363466Sgshapiro#if !DANE 43190792Sgshapiro if (host[0] == '[') 43290792Sgshapiro { 43390792Sgshapiro fbhosts[0] = host; 434132943Sgshapiro NumFallbackMXHosts = 1; 43590792Sgshapiro } 43690792Sgshapiro else 437363466Sgshapiro#endif 43890792Sgshapiro { 43990792Sgshapiro /* free old data */ 440132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 44190792Sgshapiro sm_free(fbhosts[i]); 44290792Sgshapiro 443363466Sgshapiro /* 444363466Sgshapiro ** Get new data. 445363466Sgshapiro ** Note: passing 0 as port is not correct but we cannot 446363466Sgshapiro ** determine the port number as there is no mailer. 447363466Sgshapiro */ 448363466Sgshapiro 449363466Sgshapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, 450363466Sgshapiro#if DANE 451363466Sgshapiro (DANE_SECURE == Dane) ? ISAD : 452363466Sgshapiro#endif 453363466Sgshapiro 0, 454363466Sgshapiro &rcode, &ttl, 0); 45590792Sgshapiro renew = curtime() + ttl; 456132943Sgshapiro for (i = 0; i < NumFallbackMXHosts; i++) 45790792Sgshapiro fbhosts[i] = newstr(fbhosts[i]); 45890792Sgshapiro } 459363466Sgshapiro if (NumFallbackMXHosts == NULLMX) 460363466Sgshapiro NumFallbackMXHosts = 0; 461132943Sgshapiro return NumFallbackMXHosts; 46290792Sgshapiro} 46390792Sgshapiro 46490792Sgshapiro/* 46590792Sgshapiro** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 46690792Sgshapiro** 46790792Sgshapiro** Parameters: 46890792Sgshapiro** nmx -- current number of MX records. 46990792Sgshapiro** prefs -- array of preferences. 47090792Sgshapiro** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 47190792Sgshapiro** 47290792Sgshapiro** Returns: 47390792Sgshapiro** new number of MX records. 47490792Sgshapiro** 47590792Sgshapiro** Side Effects: 476132943Sgshapiro** If FallbackMX was set, it appends the MX records for 47790792Sgshapiro** that host to mxhosts (and modifies prefs accordingly). 47890792Sgshapiro*/ 47990792Sgshapiro 48090792Sgshapirostatic int 48190792Sgshapirofallbackmxrr(nmx, prefs, mxhosts) 48290792Sgshapiro int nmx; 48390792Sgshapiro unsigned short *prefs; 48490792Sgshapiro char **mxhosts; 48590792Sgshapiro{ 48690792Sgshapiro int i; 48790792Sgshapiro 488132943Sgshapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++) 48990792Sgshapiro { 49090792Sgshapiro if (nmx > 0) 49190792Sgshapiro prefs[nmx] = prefs[nmx - 1] + 1; 49290792Sgshapiro else 49390792Sgshapiro prefs[nmx] = 0; 49490792Sgshapiro mxhosts[nmx++] = fbhosts[i]; 49590792Sgshapiro } 49690792Sgshapiro return nmx; 49790792Sgshapiro} 49890792Sgshapiro 49990792Sgshapiro/* 50038032Speter** GETMXRR -- get MX resource records for a domain 50138032Speter** 50238032Speter** Parameters: 50338032Speter** host -- the name of the host to MX. 50438032Speter** mxhosts -- a pointer to a return buffer of MX records. 50564562Sgshapiro** mxprefs -- a pointer to a return buffer of MX preferences. 50664562Sgshapiro** If NULL, don't try to populate. 507363466Sgshapiro** flags -- flags: 508363466Sgshapiro** DROPLOCALHOSt -- If true, all MX records less preferred 50938032Speter** than the local host (as determined by $=w) will 51038032Speter** be discarded. 511363466Sgshapiro** TRYFALLBACK -- add also fallback MX host? 512363466Sgshapiro** ISAD -- host lookup was secure? 51338032Speter** rcode -- a pointer to an EX_ status code. 51490792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 51538032Speter** 51638032Speter** Returns: 51738032Speter** The number of MX records found. 51838032Speter** -1 if there is an internal failure. 51938032Speter** If no MX records are found, mxhosts[0] is set to host 52038032Speter** and 1 is returned. 52190792Sgshapiro** 52290792Sgshapiro** Side Effects: 52390792Sgshapiro** The entries made for mxhosts point to a static array 52490792Sgshapiro** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 52590792Sgshapiro** if it must be preserved across calls to this function. 52638032Speter*/ 52738032Speter 52838032Speterint 529363466Sgshapirogetmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port) 53038032Speter char *host; 53138032Speter char **mxhosts; 53290792Sgshapiro unsigned short *mxprefs; 533363466Sgshapiro unsigned int flags; 53438032Speter int *rcode; 53590792Sgshapiro int *pttl; 536363466Sgshapiro int port; 53738032Speter{ 53890792Sgshapiro register unsigned char *eom, *cp; 53938032Speter register int i, j, n; 54038032Speter int nmx = 0; 54138032Speter register char *bp; 54238032Speter HEADER *hp; 54338032Speter querybuf answer; 54438032Speter int ancount, qdcount, buflen; 54590792Sgshapiro bool seenlocal = false; 54690792Sgshapiro unsigned short pref, type; 54790792Sgshapiro unsigned short localpref = 256; 548132943Sgshapiro char *fallbackMX = FallbackMX; 54990792Sgshapiro bool trycanon = false; 55090792Sgshapiro unsigned short *prefs; 551141858Sgshapiro int (*resfunc) __P((const char *, int, int, u_char *, int)); 55290792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 55338032Speter int weight[MAXMXHOSTS]; 55490792Sgshapiro int ttl = 0; 555363466Sgshapiro bool ad; 556363466Sgshapiro bool seennullmx = false; 55764562Sgshapiro extern int res_query(), res_search(); 558363466Sgshapiro# if DANE 559363466Sgshapiro bool cname2mx; 560363466Sgshapiro char qname[MAXNAME]; 561363466Sgshapiro unsigned long old_options = 0; 562363466Sgshapiro# endif 56338032Speter 56438032Speter if (tTd(8, 2)) 565363466Sgshapiro sm_dprintf("getmxrr(%s, droplocalhost=%d, flags=%X, port=%d)\n", 566363466Sgshapiro host, (flags & DROPLOCALHOST) != 0, flags, port); 567363466Sgshapiro ad = (flags & ISAD) != 0; 568147078Sgshapiro *rcode = EX_OK; 569147078Sgshapiro if (pttl != NULL) 570147078Sgshapiro *pttl = SM_DEFAULT_TTL; 571120256Sgshapiro if (*host == '\0') 572120256Sgshapiro return 0; 573363466Sgshapiro# if DANE 574363466Sgshapiro cname2mx = false; 575363466Sgshapiro qname[0] = '\0'; 576363466Sgshapiro old_options = _res.options; 577363466Sgshapiro if (ad) 578363466Sgshapiro _res.options |= SM_RES_DNSSEC; 579363466Sgshapiro# endif 58038032Speter 581363466Sgshapiro if ((fallbackMX != NULL && (flags & DROPLOCALHOST) != 0 && 582363466Sgshapiro wordinclass(fallbackMX, 'w')) || (flags & TRYFALLBACK) == 0) 58338032Speter { 58438032Speter /* don't use fallback for this pass */ 58538032Speter fallbackMX = NULL; 58638032Speter } 58738032Speter 58864562Sgshapiro if (mxprefs != NULL) 58964562Sgshapiro prefs = mxprefs; 59064562Sgshapiro else 59164562Sgshapiro prefs = prefer; 59264562Sgshapiro 59338032Speter /* efficiency hack -- numeric or non-MX lookups */ 59438032Speter if (host[0] == '[') 59538032Speter goto punt; 59638032Speter 597363466Sgshapiro# if DANE 59838032Speter /* 599363466Sgshapiro ** NOTE: This only works if nocanonify is used, 600363466Sgshapiro ** otherwise the name is already rewritten. 601363466Sgshapiro */ 602363466Sgshapiro 603363466Sgshapiro /* always or only when "needed"? */ 604363466Sgshapiro if (DANE_ALWAYS == Dane || (ad && DANE_SECURE == Dane)) 605363466Sgshapiro (void) sm_strlcpy(qname, host, sizeof(qname)); 606363466Sgshapiro# endif /* DANE */ 607363466Sgshapiro 608363466Sgshapiro# if _FFR_EAI 609363466Sgshapiro if (!addr_is_ascii(host)) 610363466Sgshapiro { 611363466Sgshapiro char buf[1024]; 612363466Sgshapiro UErrorCode error = U_ZERO_ERROR; 613363466Sgshapiro UIDNAInfo info = UIDNA_INFO_INITIALIZER; 614363466Sgshapiro UIDNA *idna; 615363466Sgshapiro 616363466Sgshapiro idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error); 617363466Sgshapiro (void) uidna_nameToASCII_UTF8(idna, host, strlen(host), 618363466Sgshapiro buf, sizeof(buf) - 1, 619363466Sgshapiro &info, &error); 620363466Sgshapiro uidna_close(idna); 621363466Sgshapiro host = sm_rpool_strdup_x(CurEnv->e_rpool, buf); 622363466Sgshapiro } 623363466Sgshapiro# endif /* _FFR_EAI */ 624363466Sgshapiro 625363466Sgshapiro /* 62638032Speter ** If we don't have MX records in our host switch, don't 62738032Speter ** try for MX records. Note that this really isn't "right", 62838032Speter ** since we might be set up to try NIS first and then DNS; 62938032Speter ** if the host is found in NIS we really shouldn't be doing 63038032Speter ** MX lookups. However, that should be a degenerate case. 63138032Speter */ 63238032Speter 63338032Speter if (!UseNameServer) 63438032Speter goto punt; 63538032Speter if (HasWildcardMX && ConfigLevel >= 6) 63638032Speter resfunc = res_query; 63738032Speter else 63838032Speter resfunc = res_search; 639363466Sgshapiro# if DNSSEC_TEST 640363466Sgshapiro if (tTd(8, 110)) 641363466Sgshapiro resfunc = tstdns_search; 642363466Sgshapiro# endif 64338032Speter 64438032Speter errno = 0; 645363466Sgshapiro hp = (HEADER *)&answer; 64690792Sgshapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, 64790792Sgshapiro sizeof(answer)); 64838032Speter if (n < 0) 64938032Speter { 65038032Speter if (tTd(8, 1)) 651363466Sgshapiro# if DNSSEC_TEST 652363466Sgshapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d (%s), h_errno=%d (%s))\n", 653363466Sgshapiro host, errno, strerror(errno), 654363466Sgshapiro h_errno, herrno2txt(h_errno)); 655363466Sgshapiro# else 656363466Sgshapiro sm_dprintf("getmxrr: res_search(%s) failed, h_errno=%d\n", 657363466Sgshapiro host, h_errno); 658363466Sgshapiro# endif 65938032Speter switch (h_errno) 66038032Speter { 66138032Speter case NO_DATA: 66290792Sgshapiro trycanon = true; 66364562Sgshapiro /* FALLTHROUGH */ 66438032Speter 66538032Speter case NO_RECOVERY: 66638032Speter /* no MX data on this host */ 66738032Speter goto punt; 66838032Speter 66938032Speter case HOST_NOT_FOUND: 67064562Sgshapiro# if BROKEN_RES_SEARCH 671363466Sgshapiro case 0: /* Ultrix resolver returns failure w/ h_errno=0 */ 672363466Sgshapiro# endif 67338032Speter /* host doesn't exist in DNS; might be in /etc/hosts */ 67490792Sgshapiro trycanon = true; 67538032Speter *rcode = EX_NOHOST; 67638032Speter goto punt; 67738032Speter 67838032Speter case TRY_AGAIN: 67938032Speter case -1: 68038032Speter /* couldn't connect to the name server */ 68138032Speter if (fallbackMX != NULL) 68238032Speter { 68338032Speter /* name server is hosed -- push to fallback */ 684363466Sgshapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 685363466Sgshapiro goto done; 68638032Speter } 68738032Speter /* it might come up later; better queue it up */ 68838032Speter *rcode = EX_TEMPFAIL; 68938032Speter break; 69038032Speter 69138032Speter default: 69290792Sgshapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", 69338032Speter host, h_errno); 69438032Speter *rcode = EX_OSERR; 69538032Speter break; 69638032Speter } 69738032Speter 69838032Speter /* irreconcilable differences */ 699363466Sgshapiro goto error; 70038032Speter } 70138032Speter 702363466Sgshapiro ad = ad && hp->ad; 703363466Sgshapiro if (tTd(8, 2)) 704363466Sgshapiro sm_dprintf("getmxrr(%s), hp=%p, ad=%d\n", host, (void*)hp, ad); 705363466Sgshapiro 70638032Speter /* avoid problems after truncation in tcp packets */ 70738032Speter if (n > sizeof(answer)) 70838032Speter n = sizeof(answer); 70938032Speter 71038032Speter /* find first satisfactory answer */ 71190792Sgshapiro cp = (unsigned char *)&answer + HFIXEDSZ; 71290792Sgshapiro eom = (unsigned char *)&answer + n; 713363466Sgshapiro 71490792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 71564562Sgshapiro qdcount--; 71664562Sgshapiro cp += n + QFIXEDSZ) 71764562Sgshapiro { 71838032Speter if ((n = dn_skipname(cp, eom)) < 0) 71938032Speter goto punt; 72064562Sgshapiro } 72190792Sgshapiro 72290792Sgshapiro /* NOTE: see definition of MXHostBuf! */ 72338032Speter buflen = sizeof(MXHostBuf) - 1; 72490792Sgshapiro SM_ASSERT(buflen > 0); 72538032Speter bp = MXHostBuf; 72690792Sgshapiro ancount = ntohs((unsigned short) hp->ancount); 72790792Sgshapiro 72890792Sgshapiro /* See RFC 1035 for layout of RRs. */ 729132943Sgshapiro /* XXX leave room for FallbackMX ? */ 73038032Speter while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 73138032Speter { 73290792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 73390792Sgshapiro (RES_UNC_T) bp, buflen)) < 0) 73438032Speter break; 73538032Speter cp += n; 73638032Speter GETSHORT(type, cp); 73790792Sgshapiro cp += INT16SZ; /* skip over class */ 73890792Sgshapiro GETLONG(ttl, cp); 73990792Sgshapiro GETSHORT(n, cp); /* rdlength */ 740363466Sgshapiro# if DANE 741363466Sgshapiro if (type == T_CNAME) 742363466Sgshapiro cname2mx = true; 743363466Sgshapiro# endif 74438032Speter if (type != T_MX) 74538032Speter { 746363466Sgshapiro if ((tTd(8, 8) || _res.options & RES_DEBUG) 747363466Sgshapiro# if DANE 748363466Sgshapiro && type != T_RRSIG 749363466Sgshapiro# endif 750363466Sgshapiro ) 751363466Sgshapiro sm_dprintf("unexpected answer type %s, size %d\n", 752363466Sgshapiro dns_type_to_string(type), n); 75338032Speter cp += n; 75438032Speter continue; 75538032Speter } 75638032Speter GETSHORT(pref, cp); 75790792Sgshapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp, 75838032Speter (RES_UNC_T) bp, buflen)) < 0) 75938032Speter break; 76038032Speter cp += n; 76190792Sgshapiro n = strlen(bp); 762363466Sgshapiro 763363466Sgshapiro /* Support for RFC7505 "MX 0 ." */ 764363466Sgshapiro if (pref == 0 && *bp == '\0') 765363466Sgshapiro seennullmx = true; 766363466Sgshapiro 76738032Speter if (wordinclass(bp, 'w')) 76838032Speter { 76938032Speter if (tTd(8, 3)) 77090792Sgshapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n", 77138032Speter bp, pref); 772363466Sgshapiro if ((flags & DROPLOCALHOST) != 0) 77338032Speter { 77438032Speter if (!seenlocal || pref < localpref) 77538032Speter localpref = pref; 77690792Sgshapiro seenlocal = true; 77738032Speter continue; 77838032Speter } 77938032Speter weight[nmx] = 0; 78038032Speter } 78138032Speter else 78238032Speter weight[nmx] = mxrand(bp); 78364562Sgshapiro prefs[nmx] = pref; 78438032Speter mxhosts[nmx++] = bp; 785363466Sgshapiro# if DANE 786363466Sgshapiro if (CHK_DANE(Dane) && port >= 0) 787363466Sgshapiro { 788363466Sgshapiro int nrr; 789363466Sgshapiro unsigned long flags; 790363466Sgshapiro 791363466Sgshapiro flags = ad ? TLSAFLADMX : TLSAFLNOADMX; 792363466Sgshapiro nrr = gettlsa(bp, NULL, NULL, flags, ttl, port); 793363466Sgshapiro 794363466Sgshapiro /* Only check qname if no TLSA RRs were found */ 795363466Sgshapiro if (0 == nrr && cname2mx && '\0' != qname[0] && 796363466Sgshapiro strcmp(qname, bp)) 797363466Sgshapiro gettlsa(qname, bp, NULL, flags, ttl, port); 798363466Sgshapiro /* XXX is this the right ad flag? */ 799363466Sgshapiro } 800363466Sgshapiro# endif 801363466Sgshapiro 802363466Sgshapiro /* 803363466Sgshapiro ** Note: n can be 0 for something like: 804363466Sgshapiro ** host MX 0 . 805363466Sgshapiro ** See RFC 7505 806363466Sgshapiro */ 807363466Sgshapiro 80838032Speter bp += n; 809363466Sgshapiro if (0 == n || bp[-1] != '.') 81038032Speter { 81138032Speter *bp++ = '.'; 81238032Speter n++; 81338032Speter } 81438032Speter *bp++ = '\0'; 81590792Sgshapiro if (buflen < n + 1) 81690792Sgshapiro { 81790792Sgshapiro /* don't want to wrap buflen */ 81890792Sgshapiro break; 81990792Sgshapiro } 82038032Speter buflen -= n + 1; 82138032Speter } 82238032Speter 823363466Sgshapiro /* Support for RFC7505 "MX 0 ." */ 824363466Sgshapiro if (seennullmx && nmx == 1) 825363466Sgshapiro { 826363466Sgshapiro if (tTd(8, 4)) 827363466Sgshapiro sm_dprintf("getmxrr: Null MX record found, domain doesn't accept mail (RFC7505)\n"); 828363466Sgshapiro *rcode = EX_UNAVAILABLE; 829363466Sgshapiro return NULLMX; 830363466Sgshapiro } 831363466Sgshapiro 83290792Sgshapiro /* return only one TTL entry, that should be sufficient */ 83390792Sgshapiro if (ttl > 0 && pttl != NULL) 83490792Sgshapiro *pttl = ttl; 83590792Sgshapiro 83638032Speter /* sort the records */ 83738032Speter for (i = 0; i < nmx; i++) 83838032Speter { 83938032Speter for (j = i + 1; j < nmx; j++) 84038032Speter { 84164562Sgshapiro if (prefs[i] > prefs[j] || 84264562Sgshapiro (prefs[i] == prefs[j] && weight[i] > weight[j])) 84338032Speter { 84438032Speter register int temp; 84538032Speter register char *temp1; 84638032Speter 84764562Sgshapiro temp = prefs[i]; 84864562Sgshapiro prefs[i] = prefs[j]; 84964562Sgshapiro prefs[j] = temp; 85038032Speter temp1 = mxhosts[i]; 85138032Speter mxhosts[i] = mxhosts[j]; 85238032Speter mxhosts[j] = temp1; 85338032Speter temp = weight[i]; 85438032Speter weight[i] = weight[j]; 85538032Speter weight[j] = temp; 85638032Speter } 85738032Speter } 85864562Sgshapiro if (seenlocal && prefs[i] >= localpref) 85938032Speter { 86038032Speter /* truncate higher preference part of list */ 86138032Speter nmx = i; 86238032Speter } 86338032Speter } 86438032Speter 86538032Speter /* delete duplicates from list (yes, some bozos have duplicates) */ 86638032Speter for (i = 0; i < nmx - 1; ) 86738032Speter { 86890792Sgshapiro if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 86938032Speter i++; 87038032Speter else 87138032Speter { 87238032Speter /* compress out duplicate */ 87338032Speter for (j = i + 1; j < nmx; j++) 87464562Sgshapiro { 87538032Speter mxhosts[j] = mxhosts[j + 1]; 87664562Sgshapiro prefs[j] = prefs[j + 1]; 87764562Sgshapiro } 87838032Speter nmx--; 87938032Speter } 88038032Speter } 88138032Speter 88238032Speter if (nmx == 0) 88338032Speter { 88438032Speterpunt: 88564562Sgshapiro if (seenlocal) 88638032Speter { 88764562Sgshapiro struct hostent *h = NULL; 88864562Sgshapiro 88938032Speter /* 89038032Speter ** If we have deleted all MX entries, this is 89138032Speter ** an error -- we should NEVER send to a host that 89238032Speter ** has an MX, and this should have been caught 89338032Speter ** earlier in the config file. 89438032Speter ** 89538032Speter ** Some sites prefer to go ahead and try the 89638032Speter ** A record anyway; that case is handled by 89738032Speter ** setting TryNullMXList. I believe this is a 89838032Speter ** bad idea, but it's up to you.... 89938032Speter */ 90038032Speter 90164562Sgshapiro if (TryNullMXList) 90264562Sgshapiro { 90373188Sgshapiro SM_SET_H_ERRNO(0); 90464562Sgshapiro errno = 0; 90564562Sgshapiro h = sm_gethostbyname(host, AF_INET); 90664562Sgshapiro if (h == NULL) 90764562Sgshapiro { 90864562Sgshapiro if (errno == ETIMEDOUT || 90964562Sgshapiro h_errno == TRY_AGAIN || 91064562Sgshapiro (errno == ECONNREFUSED && 91164562Sgshapiro UseNameServer)) 91264562Sgshapiro { 91364562Sgshapiro *rcode = EX_TEMPFAIL; 914363466Sgshapiro goto error; 91564562Sgshapiro } 91664562Sgshapiro# if NETINET6 91773188Sgshapiro SM_SET_H_ERRNO(0); 91864562Sgshapiro errno = 0; 91964562Sgshapiro h = sm_gethostbyname(host, AF_INET6); 92064562Sgshapiro if (h == NULL && 92164562Sgshapiro (errno == ETIMEDOUT || 92264562Sgshapiro h_errno == TRY_AGAIN || 92364562Sgshapiro (errno == ECONNREFUSED && 92464562Sgshapiro UseNameServer))) 92564562Sgshapiro { 92664562Sgshapiro *rcode = EX_TEMPFAIL; 927363466Sgshapiro goto error; 92864562Sgshapiro } 92964562Sgshapiro# endif /* NETINET6 */ 93064562Sgshapiro } 93164562Sgshapiro } 93264562Sgshapiro 93364562Sgshapiro if (h == NULL) 93464562Sgshapiro { 93564562Sgshapiro *rcode = EX_CONFIG; 93664562Sgshapiro syserr("MX list for %s points back to %s", 93764562Sgshapiro host, MyHostName); 938363466Sgshapiro goto error; 93964562Sgshapiro } 94090792Sgshapiro# if NETINET6 94171345Sgshapiro freehostent(h); 942159609Sgshapiro h = NULL; 943363466Sgshapiro# endif 94438032Speter } 945168515Sgshapiro if (strlen(host) >= sizeof(MXHostBuf)) 94638032Speter { 94738032Speter *rcode = EX_CONFIG; 94838032Speter syserr("Host name %s too long", 94938032Speter shortenstring(host, MAXSHORTSTR)); 950363466Sgshapiro goto error; 95138032Speter } 952168515Sgshapiro (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf)); 95338032Speter mxhosts[0] = MXHostBuf; 95464562Sgshapiro prefs[0] = 0; 95538032Speter if (host[0] == '[') 95638032Speter { 95738032Speter register char *p; 95864562Sgshapiro# if NETINET6 95964562Sgshapiro struct sockaddr_in6 tmp6; 960363466Sgshapiro# endif 96138032Speter 96238032Speter /* this may be an MX suppression-style address */ 96338032Speter p = strchr(MXHostBuf, ']'); 96438032Speter if (p != NULL) 96538032Speter { 96638032Speter *p = '\0'; 96764562Sgshapiro 96838032Speter if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 96938032Speter { 97038032Speter nmx++; 97138032Speter *p = ']'; 97238032Speter } 97364562Sgshapiro# if NETINET6 97490792Sgshapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1], 97590792Sgshapiro &tmp6.sin6_addr) == 1) 97664562Sgshapiro { 97764562Sgshapiro nmx++; 97864562Sgshapiro *p = ']'; 97964562Sgshapiro } 98064562Sgshapiro# endif /* NETINET6 */ 98138032Speter else 98238032Speter { 98390792Sgshapiro trycanon = true; 98438032Speter mxhosts[0]++; 98538032Speter } 98638032Speter } 98738032Speter } 98838032Speter if (trycanon && 989363466Sgshapiro (n = getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, 990363466Sgshapiro pttl)) != HOST_NOTFOUND) 99138032Speter { 99290792Sgshapiro /* XXX MXHostBuf == "" ? is that possible? */ 99338032Speter bp = &MXHostBuf[strlen(MXHostBuf)]; 99438032Speter if (bp[-1] != '.') 99538032Speter { 99638032Speter *bp++ = '.'; 99738032Speter *bp = '\0'; 99838032Speter } 99938032Speter nmx = 1; 1000363466Sgshapiro# if DANE 1001363466Sgshapiro if (tTd(8, 3)) 1002363466Sgshapiro sm_dprintf("getmxrr=%s, getcanonname=%d\n", 1003363466Sgshapiro mxhosts[0], n); 1004363466Sgshapiro if (CHK_DANE(Dane) && port >= 0) 1005363466Sgshapiro { 1006363466Sgshapiro int nrr; 1007363466Sgshapiro unsigned long flags; 1008363466Sgshapiro unsigned int cttl; 1009363466Sgshapiro 1010363466Sgshapiro if (pttl != NULL) 1011363466Sgshapiro cttl = *pttl; 1012363466Sgshapiro else if (ttl > 0) 1013363466Sgshapiro cttl = ttl; 1014363466Sgshapiro else 1015363466Sgshapiro cttl = SM_DEFAULT_TTL; 1016363466Sgshapiro 1017363466Sgshapiro flags = (ad && n == HOST_SECURE) 1018363466Sgshapiro ? TLSAFLADMX : TLSAFLNOADMX; 1019363466Sgshapiro nrr = gettlsa(mxhosts[0], NULL, NULL, flags, 1020363466Sgshapiro cttl, port); 1021363466Sgshapiro 1022363466Sgshapiro /* 1023363466Sgshapiro ** Only check qname if no TLSA RRs were found 1024363466Sgshapiro ** XXX: what about (temp) DNS errors? 1025363466Sgshapiro */ 1026363466Sgshapiro 1027363466Sgshapiro if (0 == nrr && '\0' != qname[0] && 1028363466Sgshapiro strcmp(qname, mxhosts[0])) 1029363466Sgshapiro gettlsa(qname, mxhosts[0], NULL, flags, 1030363466Sgshapiro cttl, port); 1031363466Sgshapiro /* XXX is this the right ad flag? */ 1032363466Sgshapiro } 1033363466Sgshapiro# endif 103438032Speter } 103538032Speter } 103638032Speter 103738032Speter /* if we have a default lowest preference, include that */ 103838032Speter if (fallbackMX != NULL && !seenlocal) 103964562Sgshapiro { 1040363466Sgshapiro /* TODO: DNSsec status of fallbacks */ 104190792Sgshapiro nmx = fallbackmxrr(nmx, prefs, mxhosts); 104264562Sgshapiro } 1043363466Sgshapiro done: 1044363466Sgshapiro# if DANE 1045363466Sgshapiro _res.options = old_options; 1046363466Sgshapiro# endif 104764562Sgshapiro return nmx; 1048363466Sgshapiro 1049363466Sgshapiro error: 1050363466Sgshapiro# if DANE 1051363466Sgshapiro _res.options = old_options; 1052363466Sgshapiro# endif 1053363466Sgshapiro return -1; 105438032Speter} 1055363466Sgshapiro 105690792Sgshapiro/* 105738032Speter** MXRAND -- create a randomizer for equal MX preferences 105838032Speter** 105938032Speter** If two MX hosts have equal preferences we want to randomize 106038032Speter** the selection. But in order for signatures to be the same, 106138032Speter** we need to randomize the same way each time. This function 106238032Speter** computes a pseudo-random hash function from the host name. 106338032Speter** 106438032Speter** Parameters: 106538032Speter** host -- the name of the host. 106638032Speter** 106738032Speter** Returns: 106838032Speter** A random but repeatable value based on the host name. 106938032Speter*/ 107038032Speter 107164562Sgshapirostatic int 107238032Spetermxrand(host) 107338032Speter register char *host; 107438032Speter{ 107538032Speter int hfunc; 107638032Speter static unsigned int seed; 107738032Speter 107838032Speter if (seed == 0) 107938032Speter { 108038032Speter seed = (int) curtime() & 0xffff; 108138032Speter if (seed == 0) 108238032Speter seed++; 108338032Speter } 108438032Speter 108538032Speter if (tTd(17, 9)) 108690792Sgshapiro sm_dprintf("mxrand(%s)", host); 108738032Speter 108838032Speter hfunc = seed; 108938032Speter while (*host != '\0') 109038032Speter { 109138032Speter int c = *host++; 109238032Speter 109338032Speter if (isascii(c) && isupper(c)) 109438032Speter c = tolower(c); 109538032Speter hfunc = ((hfunc << 1) ^ c) % 2003; 109638032Speter } 109738032Speter 109838032Speter hfunc &= 0xff; 109938032Speter hfunc++; 110038032Speter 110138032Speter if (tTd(17, 9)) 110290792Sgshapiro sm_dprintf(" = %d\n", hfunc); 110338032Speter return hfunc; 110438032Speter} 110590792Sgshapiro/* 110638032Speter** BESTMX -- find the best MX for a name 110738032Speter** 110838032Speter** This is really a hack, but I don't see any obvious way 110938032Speter** to generalize it at the moment. 111038032Speter*/ 111138032Speter 111238032Speter/* ARGSUSED3 */ 111338032Speterchar * 111438032Speterbestmx_map_lookup(map, name, av, statp) 111538032Speter MAP *map; 111638032Speter char *name; 111738032Speter char **av; 111838032Speter int *statp; 111938032Speter{ 112038032Speter int nmx; 112138032Speter int saveopts = _res.options; 112290792Sgshapiro int i; 112390792Sgshapiro ssize_t len = 0; 112490792Sgshapiro char *result; 112590792Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 1126363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION 112790792Sgshapiro char *buf; 1128363466Sgshapiro# else 112938032Speter char *p; 113042575Speter char buf[PSBUFSIZE / 2]; 1131363466Sgshapiro# endif 113238032Speter 113338032Speter _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 1134363466Sgshapiro nmx = getmxrr(name, mxhosts, NULL, 0, statp, NULL, -1); 113538032Speter _res.options = saveopts; 113638032Speter if (nmx <= 0) 113738032Speter return NULL; 113838032Speter if (bitset(MF_MATCHONLY, map->map_mflags)) 113938032Speter return map_rewrite(map, name, strlen(name), NULL); 114038032Speter if ((map->map_coldelim == '\0') || (nmx == 1)) 114138032Speter return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 114238032Speter 114338032Speter /* 114442575Speter ** We were given a -z flag (return all MXs) and there are multiple 114542575Speter ** ones. We need to build them all into a list. 114638032Speter */ 114790792Sgshapiro 1148363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION 114990792Sgshapiro for (i = 0; i < nmx; i++) 115090792Sgshapiro { 115190792Sgshapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL) 115290792Sgshapiro { 115390792Sgshapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 115490792Sgshapiro mxhosts[i], map->map_coldelim); 115590792Sgshapiro return NULL; 115690792Sgshapiro } 115790792Sgshapiro len += strlen(mxhosts[i]) + 1; 115890792Sgshapiro if (len < 0) 115990792Sgshapiro { 116090792Sgshapiro len -= strlen(mxhosts[i]) + 1; 116190792Sgshapiro break; 116290792Sgshapiro } 116390792Sgshapiro } 116490792Sgshapiro buf = (char *) sm_malloc(len); 116590792Sgshapiro if (buf == NULL) 116690792Sgshapiro { 116790792Sgshapiro *statp = EX_UNAVAILABLE; 116890792Sgshapiro return NULL; 116990792Sgshapiro } 117090792Sgshapiro *buf = '\0'; 117190792Sgshapiro for (i = 0; i < nmx; i++) 117290792Sgshapiro { 117390792Sgshapiro int end; 117490792Sgshapiro 117590792Sgshapiro end = sm_strlcat(buf, mxhosts[i], len); 117690792Sgshapiro if (i != nmx && end + 1 < len) 117790792Sgshapiro { 117890792Sgshapiro buf[end] = map->map_coldelim; 117990792Sgshapiro buf[end + 1] = '\0'; 118090792Sgshapiro } 118190792Sgshapiro } 118290792Sgshapiro 118390792Sgshapiro /* Cleanly truncate for rulesets */ 118490792Sgshapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); 1185363466Sgshapiro# else /* _FFR_BESTMX_BETTER_TRUNCATION */ 118638032Speter p = buf; 118738032Speter for (i = 0; i < nmx; i++) 118838032Speter { 118990792Sgshapiro size_t slen; 119064562Sgshapiro 119138032Speter if (strchr(mxhosts[i], map->map_coldelim) != NULL) 119238032Speter { 119338032Speter syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 119438032Speter mxhosts[i], map->map_coldelim); 119538032Speter return NULL; 119638032Speter } 119738032Speter slen = strlen(mxhosts[i]); 1198168515Sgshapiro if (len + slen + 2 > sizeof(buf)) 119938032Speter break; 120038032Speter if (i > 0) 120138032Speter { 120238032Speter *p++ = map->map_coldelim; 120338032Speter len++; 120438032Speter } 1205168515Sgshapiro (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len); 120638032Speter p += slen; 120738032Speter len += slen; 120838032Speter } 1209363466Sgshapiro# endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 121090792Sgshapiro 121190792Sgshapiro result = map_rewrite(map, buf, len, av); 1212363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION 121390792Sgshapiro sm_free(buf); 1214363466Sgshapiro# endif 121590792Sgshapiro return result; 121638032Speter} 121790792Sgshapiro/* 121838032Speter** DNS_GETCANONNAME -- get the canonical name for named host using DNS 121938032Speter** 122038032Speter** This algorithm tries to be smart about wildcard MX records. 122138032Speter** This is hard to do because DNS doesn't tell is if we matched 122238032Speter** against a wildcard or a specific MX. 122364562Sgshapiro** 122438032Speter** We always prefer A & CNAME records, since these are presumed 122538032Speter** to be specific. 122638032Speter** 122738032Speter** If we match an MX in one pass and lose it in the next, we use 122838032Speter** the old one. For example, consider an MX matching *.FOO.BAR.COM. 122938032Speter** A hostname bletch.foo.bar.com will match against this MX, but 123038032Speter** will stop matching when we try bletch.bar.com -- so we know 123138032Speter** that bletch.foo.bar.com must have been right. This fails if 123238032Speter** there was also an MX record matching *.BAR.COM, but there are 123338032Speter** some things that just can't be fixed. 123438032Speter** 123538032Speter** Parameters: 123638032Speter** host -- a buffer containing the name of the host. 123738032Speter** This is a value-result parameter. 123838032Speter** hbsize -- the size of the host buffer. 123938032Speter** trymx -- if set, try MX records as well as A and CNAME. 124038032Speter** statp -- pointer to place to store status. 124190792Sgshapiro** pttl -- pointer to return TTL (can be NULL). 124238032Speter** 124338032Speter** Returns: 1244363466Sgshapiro** >0 -- if the host was found. 1245363466Sgshapiro** 0 -- otherwise. 124638032Speter*/ 124738032Speter 1248363466Sgshapiroint 124990792Sgshapirodns_getcanonname(host, hbsize, trymx, statp, pttl) 125038032Speter char *host; 125138032Speter int hbsize; 125238032Speter bool trymx; 125338032Speter int *statp; 125490792Sgshapiro int *pttl; 125538032Speter{ 125690792Sgshapiro register unsigned char *eom, *ap; 125738032Speter register char *cp; 125864562Sgshapiro register int n; 125938032Speter HEADER *hp; 126038032Speter querybuf answer; 1261363466Sgshapiro int ancount, qdcount, ret, type, qtype, initial, loopcnt, ttl, sli; 126238032Speter char **domain; 1263363466Sgshapiro char *dp; 126438032Speter char *mxmatch; 1265363466Sgshapiro bool amatch, gotmx, ad; 126690792Sgshapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; 1267363466Sgshapiro# if DNSSEC_TEST 1268363466Sgshapiro# define ADDSL 1 /* NameSearchList may add another entry to searchlist! */ 1269363466Sgshapiro# else 1270363466Sgshapiro# define ADDSL 0 1271363466Sgshapiro# endif 1272363466Sgshapiro char *searchlist[MAXDNSRCH + 2 + ADDSL]; 1273363466Sgshapiro# define SLSIZE SM_ARRAY_SIZE(searchlist) 1274363466Sgshapiro int (*resqdomain) __P((const char *, const char *, int, int, unsigned char *, int)); 1275363466Sgshapiro# if DANE 1276363466Sgshapiro unsigned long old_options = 0; 1277363466Sgshapiro# endif 127838032Speter 1279363466Sgshapiro ttl = 0; 1280363466Sgshapiro gotmx = false; 1281363466Sgshapiro ad = true; 128238032Speter if (tTd(8, 2)) 128390792Sgshapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 128438032Speter 128538032Speter if ((_res.options & RES_INIT) == 0 && res_init() == -1) 128638032Speter { 128738032Speter *statp = EX_UNAVAILABLE; 1288363466Sgshapiro return HOST_NOTFOUND; 128938032Speter } 129038032Speter 1291363466Sgshapiro# if DANE 1292363466Sgshapiro old_options = _res.options; 1293363466Sgshapiro if (DANE_SECURE == Dane) 1294363466Sgshapiro _res.options |= SM_RES_DNSSEC; 1295363466Sgshapiro# endif 1296363466Sgshapiro 129771345Sgshapiro *statp = EX_OK; 1298363466Sgshapiro resqdomain = res_querydomain; 1299363466Sgshapiro# if DNSSEC_TEST 1300363466Sgshapiro if (tTd(8, 110)) 1301363466Sgshapiro resqdomain = tstdns_querydomain; 1302363466Sgshapiro# endif 130371345Sgshapiro 130438032Speter /* 130538032Speter ** Initialize domain search list. If there is at least one 130638032Speter ** dot in the name, search the unmodified name first so we 130738032Speter ** find "vse.CS" in Czechoslovakia instead of in the local 130864562Sgshapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 130964562Sgshapiro ** longer a country named Czechoslovakia but this type of problem 131064562Sgshapiro ** is still present. 131138032Speter ** 131238032Speter ** Older versions of the resolver could create this 131338032Speter ** list by tearing apart the host name. 131438032Speter */ 131538032Speter 131638032Speter loopcnt = 0; 131738032Spetercnameloop: 131838032Speter /* Check for dots in the name */ 131938032Speter for (cp = host, n = 0; *cp != '\0'; cp++) 132038032Speter if (*cp == '.') 132138032Speter n++; 132238032Speter 132338032Speter /* 132464562Sgshapiro ** Build the search list. 132538032Speter ** If there is at least one dot in name, start with a null 132638032Speter ** domain to search the unmodified name first. 132738032Speter ** If name does not end with a dot and search up local domain 132838032Speter ** tree desired, append each local domain component to the 132938032Speter ** search list; if name contains no dots and default domain 133038032Speter ** name is desired, append default domain name to search list; 133138032Speter ** else if name ends in a dot, remove that dot. 133238032Speter */ 133338032Speter 1334363466Sgshapiro sli = 0; 133538032Speter if (n > 0) 1336363466Sgshapiro searchlist[sli++] = ""; 1337363466Sgshapiro# if DNSSEC_TEST 1338363466Sgshapiro if (NameSearchList != NULL) 1339363466Sgshapiro { 1340363466Sgshapiro SM_ASSERT(sli < SLSIZE); 1341363466Sgshapiro searchlist[sli++] = NameSearchList; 1342363466Sgshapiro } 1343363466Sgshapiro# endif 134438032Speter if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 134538032Speter { 134664562Sgshapiro /* make sure there are less than MAXDNSRCH domains */ 134764562Sgshapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0; 1348363466Sgshapiro *domain != NULL && ret < MAXDNSRCH && sli < SLSIZE; 134964562Sgshapiro ret++) 1350363466Sgshapiro searchlist[sli++] = *domain++; 135138032Speter } 135238032Speter else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 135338032Speter { 1354363466Sgshapiro SM_ASSERT(sli < SLSIZE); 1355363466Sgshapiro searchlist[sli++] = _res.defdname; 135638032Speter } 135738032Speter else if (*cp == '.') 135838032Speter { 135938032Speter *cp = '\0'; 136038032Speter } 1361363466Sgshapiro SM_ASSERT(sli < SLSIZE); 1362363466Sgshapiro searchlist[sli] = NULL; 136338032Speter 136438032Speter /* 136538032Speter ** Now loop through the search list, appending each domain in turn 136638032Speter ** name and searching for a match. 136738032Speter */ 136838032Speter 136938032Speter mxmatch = NULL; 1370120256Sgshapiro initial = T_A; 1371120256Sgshapiro# if NETINET6 1372120256Sgshapiro if (InetMode == AF_INET6) 1373120256Sgshapiro initial = T_AAAA; 1374363466Sgshapiro# endif 1375120256Sgshapiro qtype = initial; 137638032Speter 1377363466Sgshapiro for (sli = 0; sli < SLSIZE; ) 137838032Speter { 1379363466Sgshapiro dp = searchlist[sli]; 1380363466Sgshapiro if (NULL == dp) 1381363466Sgshapiro break; 1382120256Sgshapiro if (qtype == initial) 138390792Sgshapiro gotmx = false; 138438032Speter if (tTd(8, 5)) 138590792Sgshapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", 1386363466Sgshapiro host, dp, 138764562Sgshapiro# if NETINET6 138864562Sgshapiro qtype == T_AAAA ? "AAAA" : 1389363466Sgshapiro# endif 139064562Sgshapiro qtype == T_A ? "A" : 139164562Sgshapiro qtype == T_MX ? "MX" : 139264562Sgshapiro "???"); 139371345Sgshapiro errno = 0; 1394363466Sgshapiro hp = (HEADER *) &answer; 1395363466Sgshapiro ret = (*resqdomain)(host, dp, C_IN, qtype, 139638032Speter answer.qb2, sizeof(answer.qb2)); 139738032Speter if (ret <= 0) 139838032Speter { 139990792Sgshapiro int save_errno = errno; 140090792Sgshapiro 140138032Speter if (tTd(8, 7)) 140290792Sgshapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n", 140390792Sgshapiro save_errno, h_errno); 140438032Speter 140590792Sgshapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) 140638032Speter { 140771345Sgshapiro /* 140890792Sgshapiro ** the name server seems to be down or broken. 140971345Sgshapiro */ 141071345Sgshapiro 141173188Sgshapiro SM_SET_H_ERRNO(TRY_AGAIN); 1412363466Sgshapiro if (*dp == '\0') 141394334Sgshapiro { 141494334Sgshapiro if (*statp == EX_OK) 141594334Sgshapiro *statp = EX_TEMPFAIL; 141694334Sgshapiro goto nexttype; 141794334Sgshapiro } 141838032Speter *statp = EX_TEMPFAIL; 141964562Sgshapiro 142073188Sgshapiro if (WorkAroundBrokenAAAA) 142173188Sgshapiro { 142273188Sgshapiro /* 142373188Sgshapiro ** Only return if not TRY_AGAIN as an 142473188Sgshapiro ** attempt with a different qtype may 142573188Sgshapiro ** succeed (res_querydomain() calls 142673188Sgshapiro ** res_query() calls res_send() which 142773188Sgshapiro ** sets errno to ETIMEDOUT if the 142873188Sgshapiro ** nameservers could be contacted but 142973188Sgshapiro ** didn't give an answer). 143073188Sgshapiro */ 143171345Sgshapiro 143290792Sgshapiro if (save_errno != ETIMEDOUT) 1433363466Sgshapiro goto error; 143473188Sgshapiro } 143590792Sgshapiro else 1436363466Sgshapiro goto error; 143738032Speter } 143838032Speter 143994334Sgshapironexttype: 144038032Speter if (h_errno != HOST_NOT_FOUND) 144138032Speter { 144238032Speter /* might have another type of interest */ 144364562Sgshapiro# if NETINET6 144490792Sgshapiro if (qtype == T_AAAA) 144538032Speter { 144664562Sgshapiro qtype = T_A; 144764562Sgshapiro continue; 144864562Sgshapiro } 144990792Sgshapiro else 145064562Sgshapiro# endif /* NETINET6 */ 145190792Sgshapiro if (qtype == T_A && !gotmx && 1452363466Sgshapiro (trymx || *dp == '\0')) 145364562Sgshapiro { 145438032Speter qtype = T_MX; 145538032Speter continue; 145638032Speter } 145738032Speter } 145838032Speter 145938032Speter /* definite no -- try the next domain */ 1460363466Sgshapiro sli++; 1461120256Sgshapiro qtype = initial; 146238032Speter continue; 146338032Speter } 146438032Speter else if (tTd(8, 7)) 146590792Sgshapiro sm_dprintf("\tYES\n"); 146638032Speter 146738032Speter /* avoid problems after truncation in tcp packets */ 146838032Speter if (ret > sizeof(answer)) 146938032Speter ret = sizeof(answer); 1470159609Sgshapiro SM_ASSERT(ret >= 0); 147138032Speter 147238032Speter /* 147338032Speter ** Appear to have a match. Confirm it by searching for A or 147438032Speter ** CNAME records. If we don't have a local domain 147538032Speter ** wild card MX record, we will accept MX as well. 147638032Speter */ 147738032Speter 147890792Sgshapiro ap = (unsigned char *) &answer + HFIXEDSZ; 147990792Sgshapiro eom = (unsigned char *) &answer + ret; 148038032Speter 1481363466Sgshapiro if (0 == hp->ad) 1482363466Sgshapiro ad = false; 1483363466Sgshapiro 148438032Speter /* skip question part of response -- we know what we asked */ 148590792Sgshapiro for (qdcount = ntohs((unsigned short) hp->qdcount); 148664562Sgshapiro qdcount--; 148764562Sgshapiro ap += ret + QFIXEDSZ) 148838032Speter { 148938032Speter if ((ret = dn_skipname(ap, eom)) < 0) 149038032Speter { 149138032Speter if (tTd(8, 20)) 149290792Sgshapiro sm_dprintf("qdcount failure (%d)\n", 149390792Sgshapiro ntohs((unsigned short) hp->qdcount)); 149438032Speter *statp = EX_SOFTWARE; 1495363466Sgshapiro goto error; 149638032Speter } 149738032Speter } 149838032Speter 149990792Sgshapiro amatch = false; 150090792Sgshapiro for (ancount = ntohs((unsigned short) hp->ancount); 150164562Sgshapiro --ancount >= 0 && ap < eom; 150264562Sgshapiro ap += n) 150338032Speter { 150490792Sgshapiro n = dn_expand((unsigned char *) &answer, eom, ap, 1505168515Sgshapiro (RES_UNC_T) nbuf, sizeof(nbuf)); 150638032Speter if (n < 0) 150738032Speter break; 150838032Speter ap += n; 150938032Speter GETSHORT(type, ap); 151090792Sgshapiro ap += INT16SZ; /* skip over class */ 151190792Sgshapiro GETLONG(ttl, ap); 151290792Sgshapiro GETSHORT(n, ap); /* rdlength */ 151338032Speter switch (type) 151438032Speter { 151538032Speter case T_MX: 151690792Sgshapiro gotmx = true; 1517363466Sgshapiro if (*dp != '\0' && HasWildcardMX) 151838032Speter { 151938032Speter /* 152038032Speter ** If we are using MX matches and have 152138032Speter ** not yet gotten one, save this one 152264562Sgshapiro ** but keep searching for an A or 152338032Speter ** CNAME match. 152438032Speter */ 152538032Speter 152638032Speter if (trymx && mxmatch == NULL) 1527363466Sgshapiro mxmatch = dp; 152838032Speter continue; 152938032Speter } 153038032Speter 153138032Speter /* 153238032Speter ** If we did not append a domain name, this 153338032Speter ** must have been a canonical name to start 153438032Speter ** with. Even if we did append a domain name, 153538032Speter ** in the absence of a wildcard MX this must 153638032Speter ** still be a real MX match. 153738032Speter ** Such MX matches are as good as an A match, 153838032Speter ** fall through. 153938032Speter */ 154064562Sgshapiro /* FALLTHROUGH */ 154138032Speter 154264562Sgshapiro# if NETINET6 154364562Sgshapiro case T_AAAA: 1544363466Sgshapiro# endif 154538032Speter case T_A: 154638032Speter /* Flag that a good match was found */ 154790792Sgshapiro amatch = true; 154838032Speter 154938032Speter /* continue in case a CNAME also exists */ 155038032Speter continue; 155138032Speter 155238032Speter case T_CNAME: 155338032Speter if (DontExpandCnames) 155438032Speter { 155538032Speter /* got CNAME -- guaranteed canonical */ 155690792Sgshapiro amatch = true; 155738032Speter break; 155838032Speter } 155938032Speter 156038032Speter if (loopcnt++ > MAXCNAMEDEPTH) 156138032Speter { 156238032Speter /*XXX should notify postmaster XXX*/ 156338032Speter message("DNS failure: CNAME loop for %s", 156438032Speter host); 156538032Speter if (CurEnv->e_message == NULL) 156638032Speter { 156738032Speter char ebuf[MAXLINE]; 156838032Speter 156990792Sgshapiro (void) sm_snprintf(ebuf, 1570168515Sgshapiro sizeof(ebuf), 157138032Speter "Deferred: DNS failure: CNAME loop for %.100s", 157238032Speter host); 157390792Sgshapiro CurEnv->e_message = 157490792Sgshapiro sm_rpool_strdup_x( 157590792Sgshapiro CurEnv->e_rpool, ebuf); 157638032Speter } 157773188Sgshapiro SM_SET_H_ERRNO(NO_RECOVERY); 157838032Speter *statp = EX_CONFIG; 1579363466Sgshapiro goto error; 158038032Speter } 158138032Speter 158238032Speter /* value points at name */ 158390792Sgshapiro if ((ret = dn_expand((unsigned char *)&answer, 158490792Sgshapiro eom, ap, (RES_UNC_T) nbuf, 158590792Sgshapiro sizeof(nbuf))) < 0) 158638032Speter break; 158790792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 158838032Speter 158938032Speter /* 159038032Speter ** RFC 1034 section 3.6 specifies that CNAME 159138032Speter ** should point at the canonical name -- but 159238032Speter ** urges software to try again anyway. 159338032Speter */ 159438032Speter 159538032Speter goto cnameloop; 159638032Speter 159738032Speter default: 159838032Speter /* not a record of interest */ 159938032Speter continue; 160038032Speter } 160138032Speter } 160238032Speter 160338032Speter if (amatch) 160438032Speter { 160564562Sgshapiro /* 160638032Speter ** Got a good match -- either an A, CNAME, or an 160738032Speter ** exact MX record. Save it and get out of here. 160838032Speter */ 160938032Speter 1610363466Sgshapiro mxmatch = dp; 161138032Speter break; 161238032Speter } 161338032Speter 161438032Speter /* 161538032Speter ** Nothing definitive yet. 161638032Speter ** If this was a T_A query and we haven't yet found a MX 161738032Speter ** match, try T_MX if allowed to do so. 161838032Speter ** Otherwise, try the next domain. 161938032Speter */ 162038032Speter 162164562Sgshapiro# if NETINET6 162290792Sgshapiro if (qtype == T_AAAA) 162338032Speter qtype = T_A; 162490792Sgshapiro else 1625363466Sgshapiro# endif 1626363466Sgshapiro if (qtype == T_A && !gotmx && (trymx || *dp == '\0')) 162738032Speter qtype = T_MX; 162838032Speter else 162938032Speter { 1630120256Sgshapiro qtype = initial; 1631363466Sgshapiro sli++; 163238032Speter } 163338032Speter } 163438032Speter 163538032Speter /* if nothing was found, we are done */ 163638032Speter if (mxmatch == NULL) 163738032Speter { 163871345Sgshapiro if (*statp == EX_OK) 163971345Sgshapiro *statp = EX_NOHOST; 1640363466Sgshapiro goto error; 164138032Speter } 164238032Speter 164338032Speter /* 164438032Speter ** Create canonical name and return. 164538032Speter ** If saved domain name is null, name was already canonical. 164638032Speter ** Otherwise append the saved domain name. 164738032Speter */ 164838032Speter 1649168515Sgshapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host, 165090792Sgshapiro *mxmatch == '\0' ? "" : ".", 165190792Sgshapiro MAXDNAME, mxmatch); 165290792Sgshapiro (void) sm_strlcpy(host, nbuf, hbsize); 165338032Speter if (tTd(8, 5)) 165490792Sgshapiro sm_dprintf("dns_getcanonname: %s\n", host); 165538032Speter *statp = EX_OK; 165690792Sgshapiro 165790792Sgshapiro /* return only one TTL entry, that should be sufficient */ 165890792Sgshapiro if (ttl > 0 && pttl != NULL) 165990792Sgshapiro *pttl = ttl; 1660363466Sgshapiro# if DANE 1661363466Sgshapiro _res.options = old_options; 1662363466Sgshapiro# endif 1663363466Sgshapiro return ad ? HOST_SECURE : HOST_OK; 1664363466Sgshapiro 1665363466Sgshapiro error: 1666363466Sgshapiro# if DANE 1667363466Sgshapiro _res.options = old_options; 1668363466Sgshapiro# endif 1669363466Sgshapiro return HOST_NOTFOUND; 167038032Speter} 1671363466Sgshapiro 167238032Speter#endif /* NAMED_BIND */ 1673