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