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