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