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