PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/freebsd/contrib/sendmail/src/domain.c

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