]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/sendmail/src/domain.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / sendmail / src / domain.c
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 */