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