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