use wordinclass() abstraction for searching classes
[unix-history] / usr / src / usr.sbin / sendmail / src / domain.c
CommitLineData
8c0f18d2 1/*
dc45ba8c 2 * Copyright (c) 1986 Eric P. Allman
fa8514f8
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
1acb1197 5 *
417f7a11 6 * %sccs.include.redist.c%
1acb1197 7 */
8c0f18d2 8
97543758 9#include "sendmail.h"
a29056d8 10
8c0f18d2 11#ifndef lint
efe7f723 12#if NAMED_BIND
c85d44ca 13static char sccsid[] = "@(#)domain.c 8.30 (Berkeley) %G% (with name server)";
a29056d8 14#else
c85d44ca 15static char sccsid[] = "@(#)domain.c 8.30 (Berkeley) %G% (without name server)";
a29056d8 16#endif
1acb1197
KB
17#endif /* not lint */
18
efe7f723 19#if NAMED_BIND
a29056d8 20
a29056d8 21#include <errno.h>
bece62ab
KB
22#include <resolv.h>
23#include <netdb.h>
8c0f18d2 24
7e70d3c8
EA
25typedef union
26{
27 HEADER qb1;
a0c4b056 28 u_char qb2[PACKETSZ];
8c0f18d2
JB
29} querybuf;
30
96091294 31static char MXHostBuf[MAXMXHOSTS*PACKETSZ];
8c0f18d2 32
322eceee
EA
33#ifndef MAXDNSRCH
34#define MAXDNSRCH 6 /* number of possible domains to search */
35#endif
36
49a282c6
EA
37#ifndef MAX
38#define MAX(a, b) ((a) > (b) ? (a) : (b))
39#endif
40
35f1f39e
EA
41#ifndef NO_DATA
42# define NO_DATA NO_ADDRESS
43#endif
44
648eb46e
EA
45#ifndef HFIXEDSZ
46# define HFIXEDSZ 12 /* sizeof(HEADER) */
c583011b
EA
47#endif
48
fa8514f8 49#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
9f090fac
EA
50
51#if defined(__RES) && (__RES >= 19940415)
52# define RES_UNC_T char *
53#else
54# define RES_UNC_T u_char *
55#endif
85fe919d
EA
56\f/*
57** GETMXRR -- get MX resource records for a domain
58**
59** Parameters:
60** host -- the name of the host to MX.
61** mxhosts -- a pointer to a return buffer of MX records.
c3577cd6
EA
62** droplocalhost -- If TRUE, all MX records less preferred
63** than the local host (as determined by $=w) will
64** be discarded.
85fe919d
EA
65** rcode -- a pointer to an EX_ status code.
66**
67** Returns:
68** The number of MX records found.
69** -1 if there is an internal failure.
70** If no MX records are found, mxhosts[0] is set to host
71** and 1 is returned.
72*/
73
c3577cd6 74getmxrr(host, mxhosts, droplocalhost, rcode)
96091294
EA
75 char *host;
76 char **mxhosts;
c3577cd6 77 bool droplocalhost;
bece62ab 78 int *rcode;
8c0f18d2 79{
bece62ab
KB
80 extern int h_errno;
81 register u_char *eom, *cp;
035aca77
EA
82 register int i, j, n;
83 int nmx = 0;
bece62ab 84 register char *bp;
8c0f18d2 85 HEADER *hp;
bece62ab 86 querybuf answer;
e1b35328 87 int ancount, qdcount, buflen;
035aca77 88 bool seenlocal = FALSE;
6120c83f
EA
89 u_short pref, localpref, type;
90 char *fallbackMX = FallBackMX;
91 static bool firsttime = TRUE;
4334f193 92 bool trycanon = FALSE;
6120c83f 93 u_short prefer[MAXMXHOSTS];
7e70d3c8 94 int weight[MAXMXHOSTS];
035aca77 95 extern bool getcanonname();
8c0f18d2 96
e1bf6b36
EA
97 if (tTd(8, 2))
98 printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
99
6120c83f
EA
100 if (fallbackMX != NULL)
101 {
9f090fac
EA
102 if (firsttime &&
103 res_query(FallBackMX, C_IN, T_A,
104 (u_char *) &answer, sizeof answer) < 0)
6120c83f
EA
105 {
106 /* this entry is bogus */
107 fallbackMX = FallBackMX = NULL;
108 }
c85d44ca 109 else if (droplocalhost && wordinclass(fallbackMX, 'w'))
6120c83f
EA
110 {
111 /* don't use fallback for this pass */
112 fallbackMX = NULL;
113 }
114 firsttime = FALSE;
115 }
116
d6f355ae
EA
117 /* efficiency hack -- numeric or non-MX lookups */
118 if (host[0] == '[')
119 goto punt;
120
437376a9
EA
121 /*
122 ** If we don't have MX records in our host switch, don't
123 ** try for MX records. Note that this really isn't "right",
124 ** since we might be set up to try NIS first and then DNS;
125 ** if the host is found in NIS we really shouldn't be doing
126 ** MX lookups. However, that should be a degenerate case.
127 */
128
82625f68 129 if (!UseNameServer)
437376a9
EA
130 goto punt;
131
19c9e482 132 errno = 0;
9f090fac 133 n = res_search(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
a29056d8
EA
134 if (n < 0)
135 {
bece62ab 136 if (tTd(8, 1))
5ee1c83d
EA
137 printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
138 (host == NULL) ? "<NULL>" : host, errno, h_errno);
a29056d8
EA
139 switch (h_errno)
140 {
141 case NO_DATA:
4334f193
EA
142 trycanon = TRUE;
143 /* fall through */
144
a29056d8
EA
145 case NO_RECOVERY:
146 /* no MX data on this host */
bece62ab 147 goto punt;
a29056d8
EA
148
149 case HOST_NOT_FOUND:
a3815d39 150#ifdef BROKEN_RES_SEARCH
3e9f7c31 151 case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
a3815d39 152#endif
3e9f7c31 153 /* host doesn't exist in DNS; might be in /etc/hosts */
bece62ab 154 *rcode = EX_NOHOST;
3e9f7c31 155 goto punt;
a29056d8
EA
156
157 case TRY_AGAIN:
158 /* couldn't connect to the name server */
a29056d8 159 /* it might come up later; better queue it up */
bece62ab
KB
160 *rcode = EX_TEMPFAIL;
161 break;
993881b0
EA
162
163 default:
164 syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
165 host, h_errno);
166 *rcode = EX_OSERR;
167 break;
8c0f18d2 168 }
a29056d8
EA
169
170 /* irreconcilable differences */
171 return (-1);
8c0f18d2 172 }
bece62ab
KB
173
174 /* find first satisfactory answer */
175 hp = (HEADER *)&answer;
648eb46e 176 cp = (u_char *)&answer + HFIXEDSZ;
bece62ab
KB
177 eom = (u_char *)&answer + n;
178 for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
ba5c6ea8 179 if ((n = dn_skipname(cp, eom)) < 0)
bece62ab 180 goto punt;
96091294
EA
181 buflen = sizeof(MXHostBuf) - 1;
182 bp = MXHostBuf;
bece62ab 183 ancount = ntohs(hp->ancount);
a2798604 184 while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
3251a23d 185 {
0df908a9 186 if ((n = dn_expand((u_char *)&answer,
9f090fac 187 eom, cp, (RES_UNC_T) bp, buflen)) < 0)
8c0f18d2
JB
188 break;
189 cp += n;
bece62ab 190 GETSHORT(type, cp);
648eb46e 191 cp += INT16SZ + INT32SZ;
bece62ab 192 GETSHORT(n, cp);
3251a23d
EA
193 if (type != T_MX)
194 {
322eceee 195 if (tTd(8, 8) || _res.options & RES_DEBUG)
8c0f18d2 196 printf("unexpected answer type %d, size %d\n",
bece62ab 197 type, n);
8c0f18d2
JB
198 cp += n;
199 continue;
200 }
bece62ab 201 GETSHORT(pref, cp);
3251a23d 202 if ((n = dn_expand((u_char *)&answer, eom, cp,
9f090fac 203 (RES_UNC_T) bp, buflen)) < 0)
8c0f18d2 204 break;
3fff2c44 205 cp += n;
c85d44ca 206 if (droplocalhost && wordinclass(bp, 'w'))
3251a23d 207 {
e1bf6b36
EA
208 if (tTd(8, 3))
209 printf("found localhost (%s) in MX list, pref=%d\n",
210 bp, pref);
e1b35328 211 if (!seenlocal || pref < localpref)
bece62ab 212 localpref = pref;
e1b35328 213 seenlocal = TRUE;
3fff2c44
JB
214 continue;
215 }
ad21d9c2
EA
216 if (fallbackMX != NULL && strcasecmp(bp, fallbackMX) == 0)
217 fallbackMX = NULL;
7e70d3c8 218 weight[nmx] = mxrand(bp);
8c0f18d2
JB
219 prefer[nmx] = pref;
220 mxhosts[nmx++] = bp;
3251a23d 221 n = strlen(bp);
bece62ab 222 bp += n;
3251a23d
EA
223 if (bp[-1] != '.')
224 {
225 *bp++ = '.';
226 n++;
227 }
228 *bp++ = '\0';
229 buflen -= n + 1;
8c0f18d2 230 }
035aca77
EA
231
232 /* sort the records */
233 for (i = 0; i < nmx; i++)
7e70d3c8 234 {
035aca77 235 for (j = i + 1; j < nmx; j++)
1d014a65 236 {
035aca77
EA
237 if (prefer[i] > prefer[j] ||
238 (prefer[i] == prefer[j] && weight[i] > weight[j]))
239 {
240 register int temp;
241 register char *temp1;
242
243 temp = prefer[i];
244 prefer[i] = prefer[j];
245 prefer[j] = temp;
246 temp1 = mxhosts[i];
247 mxhosts[i] = mxhosts[j];
248 mxhosts[j] = temp1;
249 temp = weight[i];
250 weight[i] = weight[j];
251 weight[j] = temp;
252 }
253 }
254 if (seenlocal && prefer[i] >= localpref)
255 {
256 /* truncate higher preference part of list */
257 nmx = i;
1d014a65 258 }
3fff2c44 259 }
035aca77
EA
260
261 if (nmx == 0)
7e70d3c8 262 {
035aca77
EA
263punt:
264 if (seenlocal &&
265 (!TryNullMXList || gethostbyname(host) == NULL))
7e70d3c8 266 {
035aca77
EA
267 /*
268 ** If we have deleted all MX entries, this is
269 ** an error -- we should NEVER send to a host that
270 ** has an MX, and this should have been caught
271 ** earlier in the config file.
272 **
273 ** Some sites prefer to go ahead and try the
274 ** A record anyway; that case is handled by
275 ** setting TryNullMXList. I believe this is a
276 ** bad idea, but it's up to you....
277 */
278
279 *rcode = EX_CONFIG;
4d33df93
EA
280 syserr("MX list for %s points back to %s",
281 host, MyHostName);
035aca77
EA
282 return -1;
283 }
d6f355ae
EA
284 strcpy(MXHostBuf, host);
285 mxhosts[0] = MXHostBuf;
286 if (host[0] == '[')
287 {
288 register char *p;
289
290 /* this may be an MX suppression-style address */
291 p = strchr(MXHostBuf, ']');
292 if (p != NULL)
293 {
294 *p = '\0';
295 if (inet_addr(&MXHostBuf[1]) != -1)
296 *p = ']';
297 else
d379b7a2
EA
298 {
299 trycanon = TRUE;
d6f355ae 300 mxhosts[0]++;
d379b7a2 301 }
d6f355ae
EA
302 }
303 }
ad21d9c2
EA
304 n = strlen(MXHostBuf);
305 bp = &MXHostBuf[n];
306 buflen = sizeof MXHostBuf - n - 1;
d379b7a2
EA
307 if (trycanon &&
308 getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
035aca77 309 {
035aca77 310 if (bp[-1] != '.')
7e70d3c8 311 {
035aca77
EA
312 *bp++ = '.';
313 *bp = '\0';
ad21d9c2 314 buflen--;
3fff2c44 315 }
3fff2c44 316 }
ad21d9c2 317 bp++;
035aca77 318 nmx = 1;
8c0f18d2 319 }
a2798604
EA
320
321 /* if we have a default lowest preference, include that */
ad21d9c2
EA
322 if (fallbackMX != NULL && !seenlocal && strlen(fallbackMX) < buflen)
323 {
324 strcpy(bp, fallbackMX);
325 mxhosts[nmx++] = bp;
326 bp += strlen(bp);
327 if (bp[-1] != '.')
328 {
329 *bp++ = '.';
330 *bp = '\0';
331 }
332 }
a2798604 333
7e70d3c8
EA
334 return (nmx);
335}
336\f/*
337** MXRAND -- create a randomizer for equal MX preferences
338**
339** If two MX hosts have equal preferences we want to randomize
340** the selection. But in order for signatures to be the same,
341** we need to randomize the same way each time. This function
342** computes a pseudo-random hash function from the host name.
343**
344** Parameters:
345** host -- the name of the host.
346**
347** Returns:
348** A random but repeatable value based on the host name.
349**
350** Side Effects:
351** none.
352*/
353
354mxrand(host)
355 register char *host;
356{
357 int hfunc;
358 static unsigned int seed;
359
360 if (seed == 0)
361 {
362 seed = (int) curtime() & 0xffff;
363 if (seed == 0)
364 seed++;
365 }
366
367 if (tTd(17, 9))
368 printf("mxrand(%s)", host);
369
370 hfunc = seed;
371 while (*host != '\0')
372 {
373 int c = *host++;
374
375 if (isascii(c) && isupper(c))
376 c = tolower(c);
589d913b 377 hfunc = ((hfunc << 1) ^ c) % 2003;
7e70d3c8
EA
378 }
379
380 hfunc &= 0xff;
381
382 if (tTd(17, 9))
383 printf(" = %d\n", hfunc);
384 return hfunc;
8c0f18d2 385}
07b49560
EA
386\f/*
387** GETCANONNAME -- get the canonical name for named host
388**
322eceee
EA
389** This algorithm tries to be smart about wildcard MX records.
390** This is hard to do because DNS doesn't tell is if we matched
391** against a wildcard or a specific MX.
392**
393** We always prefer A & CNAME records, since these are presumed
394** to be specific.
395**
396** If we match an MX in one pass and lose it in the next, we use
397** the old one. For example, consider an MX matching *.FOO.BAR.COM.
398** A hostname bletch.foo.bar.com will match against this MX, but
399** will stop matching when we try bletch.bar.com -- so we know
400** that bletch.foo.bar.com must have been right. This fails if
401** there was also an MX record matching *.BAR.COM, but there are
402** some things that just can't be fixed.
403**
07b49560
EA
404** Parameters:
405** host -- a buffer containing the name of the host.
406** This is a value-result parameter.
407** hbsize -- the size of the host buffer.
035aca77 408** trymx -- if set, try MX records as well as A and CNAME.
07b49560
EA
409**
410** Returns:
411** TRUE -- if the host matched.
412** FALSE -- otherwise.
07b49560 413*/
94ce122d 414
bbfde42f 415bool
035aca77 416getcanonname(host, hbsize, trymx)
b22f87a1
JB
417 char *host;
418 int hbsize;
035aca77 419 bool trymx;
b22f87a1 420{
fa3cad61 421 extern int h_errno;
94ce122d
EA
422 register u_char *eom, *ap;
423 register char *cp;
bece62ab 424 register int n;
b22f87a1 425 HEADER *hp;
bece62ab 426 querybuf answer;
a3934270 427 int ancount, qdcount;
94ce122d 428 int ret;
94ce122d 429 char **domain;
94ce122d 430 int type;
322eceee
EA
431 char **dp;
432 char *mxmatch;
433 bool amatch;
57c2a3b9 434 bool gotmx;
c63cdab6 435 int qtype;
fa8514f8 436 int loopcnt;
ab073a9b 437 char *xp;
49a282c6 438 char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)];
421d5c80 439 char *searchlist[MAXDNSRCH+2];
ab073a9b 440 extern char *gethostalias();
94ce122d
EA
441
442 if (tTd(8, 2))
443 printf("getcanonname(%s)\n", host);
444
445 if ((_res.options & RES_INIT) == 0 && res_init() == -1)
446 return (FALSE);
b22f87a1 447
19c9e482 448 /*
322eceee
EA
449 ** Initialize domain search list. If there is at least one
450 ** dot in the name, search the unmodified name first so we
451 ** find "vse.CS" in Czechoslovakia instead of in the local
452 ** domain (e.g., vse.CS.Berkeley.EDU).
453 **
454 ** Older versions of the resolver could create this
455 ** list by tearing apart the host name.
d51c0d9a
EA
456 */
457
fa8514f8 458 loopcnt = 0;
b104ca4a
EA
459cnameloop:
460 for (cp = host, n = 0; *cp; cp++)
461 if (*cp == '.')
462 n++;
463
ab073a9b
EA
464 if (n == 0 && (xp = gethostalias(host)) != NULL)
465 {
466 if (loopcnt++ > MAXCNAMEDEPTH)
467 {
468 syserr("loop in ${HOSTALIASES} file");
469 }
470 else
471 {
472 strncpy(host, xp, hbsize);
473 host[hbsize - 1] = '\0';
474 goto cnameloop;
475 }
476 }
477
322eceee
EA
478 dp = searchlist;
479 if (n > 0)
480 *dp++ = "";
57c2a3b9 481 if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
d51c0d9a 482 {
322eceee
EA
483 for (domain = _res.dnsrch; *domain != NULL; )
484 *dp++ = *domain++;
d51c0d9a 485 }
57c2a3b9
EA
486 else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
487 {
488 *dp++ = _res.defdname;
489 }
506ca31f
EA
490 else if (*cp == '.')
491 {
492 *cp = '\0';
493 }
322eceee 494 *dp = NULL;
d51c0d9a
EA
495
496 /*
322eceee 497 ** Now run through the search list for the name in question.
d51c0d9a
EA
498 */
499
322eceee 500 mxmatch = NULL;
c63cdab6 501 qtype = T_ANY;
322eceee 502
c63cdab6 503 for (dp = searchlist; *dp != NULL; )
94ce122d 504 {
57c2a3b9
EA
505 if (qtype == T_ANY)
506 gotmx = FALSE;
322eceee 507 if (tTd(8, 5))
ab54800d
EA
508 printf("getcanonname: trying %s.%s (%s)\n", host, *dp,
509 qtype == T_ANY ? "ANY" : qtype == T_A ? "A" :
510 qtype == T_MX ? "MX" : "???");
c63cdab6 511 ret = res_querydomain(host, *dp, C_IN, qtype,
bfbb993e 512 answer.qb2, sizeof(answer.qb2));
322eceee 513 if (ret <= 0)
94ce122d 514 {
1d136a1f 515 if (tTd(8, 7))
1c7897ef
EA
516 printf("\tNO: errno=%d, h_errno=%d\n",
517 errno, h_errno);
94ce122d 518
1c7897ef 519 if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
d51c0d9a 520 {
322eceee 521 /* the name server seems to be down */
94ce122d 522 h_errno = TRY_AGAIN;
4ae3ff58 523 return FALSE;
94ce122d 524 }
322eceee 525
2c8a024c 526 if (h_errno != HOST_NOT_FOUND)
c63cdab6 527 {
2c8a024c
EA
528 /* might have another type of interest */
529 if (qtype == T_ANY)
530 {
531 qtype = T_A;
532 continue;
533 }
035aca77 534 else if (qtype == T_A && !gotmx && trymx)
2c8a024c
EA
535 {
536 qtype = T_MX;
537 continue;
538 }
c63cdab6
EA
539 }
540
322eceee 541 if (mxmatch != NULL)
94ce122d 542 {
322eceee 543 /* we matched before -- use that one */
94ce122d
EA
544 break;
545 }
2c8a024c
EA
546
547 /* otherwise, try the next name */
548 dp++;
549 qtype = T_ANY;
322eceee 550 continue;
94ce122d 551 }
1d136a1f 552 else if (tTd(8, 7))
322eceee
EA
553 printf("\tYES\n");
554
4ae3ff58 555 /*
322eceee
EA
556 ** This might be a bogus match. Search for A or
557 ** CNAME records. If we don't have a matching
558 ** wild card MX record, we will accept MX as well.
4ae3ff58
EA
559 */
560
322eceee 561 hp = (HEADER *) &answer;
648eb46e 562 ap = (u_char *) &answer + HFIXEDSZ;
322eceee
EA
563 eom = (u_char *) &answer + ret;
564
565 /* skip question part of response -- we know what we asked */
566 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
94ce122d 567 {
322eceee
EA
568 if ((ret = dn_skipname(ap, eom)) < 0)
569 {
570 if (tTd(8, 20))
571 printf("qdcount failure (%d)\n",
572 ntohs(hp->qdcount));
573 return FALSE; /* ???XXX??? */
574 }
94ce122d 575 }
bece62ab 576
322eceee
EA
577 amatch = FALSE;
578 for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
94ce122d 579 {
322eceee 580 n = dn_expand((u_char *) &answer, eom, ap,
9f090fac 581 (RES_UNC_T) nbuf, sizeof nbuf);
322eceee
EA
582 if (n < 0)
583 break;
584 ap += n;
585 GETSHORT(type, ap);
648eb46e 586 ap += INT16SZ + INT32SZ;
322eceee
EA
587 GETSHORT(n, ap);
588 switch (type)
589 {
590 case T_MX:
57c2a3b9 591 gotmx = TRUE;
14553987 592 if (**dp != '\0')
322eceee
EA
593 {
594 /* got a match -- save that info */
14553987 595 if (trymx && mxmatch == NULL)
322eceee
EA
596 mxmatch = *dp;
597 continue;
598 }
599
600 /* exact MX matches are as good as an A match */
601 /* fall through */
602
603 case T_A:
604 /* good show */
605 amatch = TRUE;
606
607 /* continue in case a CNAME also exists */
608 continue;
609
610 case T_CNAME:
fa8514f8
KB
611 if (loopcnt++ > MAXCNAMEDEPTH)
612 {
ed3d2b02
EA
613 /*XXX should notify postmaster XXX*/
614 message("DNS failure: CNAME loop for %s",
fa8514f8 615 host);
ed3d2b02
EA
616 if (CurEnv->e_message == NULL)
617 {
618 char ebuf[MAXLINE];
619
620 sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s",
621 host);
622 CurEnv->e_message = newstr(ebuf);
623 }
624 h_errno = NO_RECOVERY;
625 return FALSE;
fa8514f8
KB
626 }
627
322eceee
EA
628 /* value points at name */
629 if ((ret = dn_expand((u_char *)&answer,
9f090fac 630 eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
322eceee
EA
631 break;
632 (void)strncpy(host, nbuf, hbsize); /* XXX */
633 host[hbsize - 1] = '\0';
f1770f93
EA
634
635 /*
636 ** RFC 1034 section 3.6 specifies that CNAME
637 ** should point at the canonical name -- but
638 ** urges software to try again anyway.
639 */
640
641 goto cnameloop;
322eceee
EA
642
643 default:
644 /* not a record of interest */
645 continue;
646 }
94ce122d 647 }
bece62ab 648
322eceee
EA
649 if (amatch)
650 {
651 /* got an A record and no CNAME */
652 mxmatch = *dp;
b22f87a1 653 break;
b22f87a1 654 }
c63cdab6
EA
655
656 /*
657 ** If this was a T_ANY query, we may have the info but
658 ** need an explicit query. Try T_A, then T_MX.
659 */
660
661 if (qtype == T_ANY)
662 qtype = T_A;
035aca77 663 else if (qtype == T_A && !gotmx && trymx)
c63cdab6
EA
664 qtype = T_MX;
665 else
666 {
667 /* really nothing in this domain; try the next */
668 qtype = T_ANY;
669 dp++;
670 }
b22f87a1 671 }
322eceee
EA
672
673 if (mxmatch == NULL)
674 return FALSE;
675
676 /* create matching name and return */
677 (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host,
678 *mxmatch == '\0' ? "" : ".",
679 MAXDNAME, mxmatch);
680 strncpy(host, nbuf, hbsize);
681 host[hbsize - 1] = '\0';
682 return TRUE;
b22f87a1 683}
ab073a9b
EA
684
685
686char *
687gethostalias(host)
688 char *host;
689{
690 char *fname;
691 FILE *fp;
692 register char *p;
693 char buf[MAXLINE];
694 static char hbuf[MAXDNAME];
695
696 fname = getenv("HOSTALIASES");
697 if (fname == NULL || (fp = fopen(fname, "r")) == NULL)
698 return NULL;
699 while (fgets(buf, sizeof buf, fp) != NULL)
700 {
701 for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
702 continue;
703 if (*p == 0)
704 {
705 /* syntax error */
706 continue;
707 }
708 *p++ = '\0';
709 if (strcasecmp(buf, host) == 0)
710 break;
711 }
712
713 if (feof(fp))
714 {
715 /* no match */
716 fclose(fp);
717 return NULL;
718 }
719
720 /* got a match; extract the equivalent name */
721 while (*p != '\0' && isascii(*p) && isspace(*p))
722 p++;
723 host = p;
724 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
725 p++;
726 *p = '\0';
727 strncpy(hbuf, host, sizeof hbuf - 1);
728 hbuf[sizeof hbuf - 1] = '\0';
729 return hbuf;
730}
731
3e13e52f
EA
732\f/*
733** MAILB_LOOKUP -- do DNS mailbox lookup
734*/
735
736#ifdef DNS_MAILB
737
738mailb_lookup(addr)
739 char *addr;
740{
741 /*
742 ** Convert addr to DNS form (user.host).
743 */
744
745 /* figure out how much space it needs */
746 atp = strchr(addr, '@');
747 if (atp == NULL)
748 atp = &addr(strlen(addr));
749 i = strlen(addr);
750 for (p = addr; (p = strchr(p, '.')) != NULL; p++)
751 {
752 if (p > atp)
753 break;
754 i++;
755 }
756 if (i < sizeof abuf)
757 bufp = abuf;
758 else
759 bufp = xalloc(i + 1);
760
761 lhsmode = TRUE;
762 for (p = addr, q = bufp; (c = *p++) != '\0'; )
763 {
764 if (c == '.' && lhsmode)
765 *q++ = '\\';
766 if (c == '@')
767 lhsmode = FALSE;
768 *q++ = c;
769 }
770 *q = '\0';
771
772 /*
773 ** Now do a MAILB lookup.
774 */
775
776retry:
777 if (res_query(bufp, C_IN, T_MAILB, (char *) &answer, sizeof answer < 0)
778 {
779 /* no match -- just continue as usual */
780 return FALSE;
781 }
782
783 /* find first satisfactory answer */
784 hp = (HEADER *)&answer;
785 ap = (u_char *)&answer + sizeof(HEADER);
786 eom = (u_char *)&answer + n;
787 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += n + QFIXEDSZ)
788 if ((n = dn_skipname(ap, eom)) < 0)
789 return FALSE;
790 for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
791 {
792 n = dn_expand((u_char *)&answer, eom, ap, (u_char *)bp, buflen);
793 if (n < 0)
794 break;
795 ap += n;
796 GETSHORT(type, ap);
797 ap += SHORTSIZE + LONGSIZE;
798 GETSHORT(n, ap);
799 switch (type)
800 {
801 case T_MR:
802 /* rename: try again */
803 i = dn_expand((u_char *) &answer, eom, ap,
804 (u_char) abuf, sizeof abuf);
805 if (i < 0)
806 break;
807 if (bufp != abuf)
808 {
809 free(bufp);
810 bufp = abuf;
811 }
812 goto retry;
813
814 case T_MB:
815 i = dn_expand((u_char *) &answer, eom, ap,
816 (u_char) hbuf, sizeof hbuf);
817 if (i < 0)
818 break;
819
820 /* hbuf now has the host to deliver to */
821 break;
822
823 case T_MG:
824 i = dn_expand((u_char *) &answer, eom, ap,
825 (u_char) gbuf, sizeof gbuf);
826 if (i < 0)
827 break;
828 AliasLevel++;
829 naddrs += sendtolist(ubuf, a, sendq, e);
830 AliasLevel--;
831 break;
832
833 case T_MINFO:
834 /* bleach */
835 XXX;
836 }
837
838
839
840 if (type != T_MX)
841 {
842 if (tTd(8, 8) || _res.options & RES_DEBUG)
843 printf("unexpected answer type %d, size %d\n",
844 type, n);
845 cp += n;
846 continue;
847 }
848 GETSHORT(pref, cp);
849 if ((n = dn_expand((u_char *)&answer, eom, cp,
850 (u_char *)bp, buflen)) < 0)
851 break;
852 cp += n;
853
854
855#endif /* DNS_MAILB */
a29056d8 856
da1f33f9
PL
857#else /* not NAMED_BIND */
858
859#include <netdb.h>
860
bbfde42f 861bool
035aca77 862getcanonname(host, hbsize, trymx)
da1f33f9
PL
863 char *host;
864 int hbsize;
035aca77 865 bool trymx;
da1f33f9
PL
866{
867 struct hostent *hp;
a09a49e9 868 char *p;
da1f33f9
PL
869
870 hp = gethostbyname(host);
871 if (hp == NULL)
bbfde42f 872 return (FALSE);
a09a49e9
EA
873 p = hp->h_name;
874 if (strchr(p, '.') == NULL)
875 {
876 /* first word is a short name -- try to find a long one */
877 char **ap;
878
879 for (ap = hp->h_aliases; *ap != NULL; ap++)
880 if (strchr(*ap, '.') != NULL)
881 break;
882 if (*ap != NULL)
883 p = *ap;
884 }
da1f33f9 885
a09a49e9 886 if (strlen(p) >= hbsize)
bbfde42f 887 return (FALSE);
da1f33f9 888
a09a49e9 889 (void) strcpy(host, p);
bbfde42f 890 return (TRUE);
da1f33f9
PL
891}
892
893#endif /* not NAMED_BIND */