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