Commit | Line | Data |
---|---|---|
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 | 13 | static char sccsid[] = "@(#)domain.c 8.30 (Berkeley) %G% (with name server)"; |
a29056d8 | 14 | #else |
c85d44ca | 15 | static 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 |
25 | typedef union |
26 | { | |
27 | HEADER qb1; | |
a0c4b056 | 28 | u_char qb2[PACKETSZ]; |
8c0f18d2 JB |
29 | } querybuf; |
30 | ||
96091294 | 31 | static 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 | 74 | getmxrr(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 |
263 | punt: |
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 | ||
354 | mxrand(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 | 415 | bool |
035aca77 | 416 | getcanonname(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 |
459 | cnameloop: |
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 | ||
686 | char * | |
687 | gethostalias(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 | ||
738 | mailb_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 | ||
776 | retry: | |
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 | 861 | bool |
035aca77 | 862 | getcanonname(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 */ |