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