1c2aa98e2SPeter Wemm /*
2*d39bd2c1SGregory Neil Shapiro * Copyright (c) 1998-2004, 2006, 2010, 2020-2023 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro * All rights reserved.
4c2aa98e2SPeter Wemm * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved.
5c2aa98e2SPeter Wemm * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved.
7c2aa98e2SPeter Wemm *
8c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm * the sendmail distribution.
11c2aa98e2SPeter Wemm *
12c2aa98e2SPeter Wemm */
13c2aa98e2SPeter Wemm
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15d0cef73dSGregory Neil Shapiro #include "map.h"
16c2aa98e2SPeter Wemm
17c2aa98e2SPeter Wemm #if NAMED_BIND
184313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name server)")
195b0945b5SGregory Neil Shapiro #else
204313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)")
215b0945b5SGregory Neil Shapiro #endif
22c2aa98e2SPeter Wemm
232fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
24c2aa98e2SPeter Wemm
252fb4f839SGregory Neil Shapiro #if NAMED_BIND
26c2aa98e2SPeter Wemm # include <arpa/inet.h>
27*d39bd2c1SGregory Neil Shapiro # include "sm_resolve.h"
285b0945b5SGregory Neil Shapiro # if DANE
295b0945b5SGregory Neil Shapiro # include <tls.h>
305b0945b5SGregory Neil Shapiro # ifndef SM_NEG_TTL
315b0945b5SGregory Neil Shapiro # define SM_NEG_TTL 60 /* "negative" TTL */
325b0945b5SGregory Neil Shapiro # endif
335b0945b5SGregory Neil Shapiro # endif
34c2aa98e2SPeter Wemm
35*d39bd2c1SGregory Neil Shapiro #if USE_EAI
36*d39bd2c1SGregory Neil Shapiro #include <unicode/uidna.h>
37*d39bd2c1SGregory Neil Shapiro #endif
38*d39bd2c1SGregory Neil Shapiro
3940266059SGregory Neil Shapiro
40c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE
41c2aa98e2SPeter Wemm # define MXHOSTBUFSIZE (128 * MAXMXHOSTS)
425b0945b5SGregory Neil Shapiro # endif
43c2aa98e2SPeter Wemm
44c2aa98e2SPeter Wemm static char MXHostBuf[MXHOSTBUFSIZE];
4540266059SGregory Neil Shapiro # if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
4640266059SGregory Neil Shapiro ERROR: _MXHOSTBUFSIZE is out of range
475b0945b5SGregory Neil Shapiro # endif
48c2aa98e2SPeter Wemm
49c2aa98e2SPeter Wemm # ifndef MAXDNSRCH
50c2aa98e2SPeter Wemm # define MAXDNSRCH 6 /* number of possible domains to search */
515b0945b5SGregory Neil Shapiro # endif
5206f25ae9SGregory Neil Shapiro
5306f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE
5406f25ae9SGregory Neil Shapiro # define RES_DNSRCH_VARIABLE _res.dnsrch
555b0945b5SGregory Neil Shapiro # endif
56c2aa98e2SPeter Wemm
57c2aa98e2SPeter Wemm # ifndef HFIXEDSZ
58c2aa98e2SPeter Wemm # define HFIXEDSZ 12 /* sizeof(HEADER) */
595b0945b5SGregory Neil Shapiro # endif
60c2aa98e2SPeter Wemm
61c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
62c2aa98e2SPeter Wemm
63c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415)
64c2aa98e2SPeter Wemm # define RES_UNC_T char *
655b0945b5SGregory Neil Shapiro # else
6640266059SGregory Neil Shapiro # define RES_UNC_T unsigned char *
675b0945b5SGregory Neil Shapiro # endif
6806f25ae9SGregory Neil Shapiro
6906f25ae9SGregory Neil Shapiro static int mxrand __P((char *));
7040266059SGregory Neil Shapiro static int fallbackmxrr __P((int, unsigned short *, char **));
7106f25ae9SGregory Neil Shapiro
725b0945b5SGregory Neil Shapiro # if DANE
735b0945b5SGregory Neil Shapiro
74*d39bd2c1SGregory Neil Shapiro static void tlsa_rr_print __P((const unsigned char *, unsigned int));
75*d39bd2c1SGregory Neil Shapiro
76*d39bd2c1SGregory Neil Shapiro static void
tlsa_rr_print(rr,len)77*d39bd2c1SGregory Neil Shapiro tlsa_rr_print(rr, len)
78*d39bd2c1SGregory Neil Shapiro const unsigned char *rr;
79*d39bd2c1SGregory Neil Shapiro unsigned int len;
80*d39bd2c1SGregory Neil Shapiro {
81*d39bd2c1SGregory Neil Shapiro unsigned int i, l;
82*d39bd2c1SGregory Neil Shapiro
83*d39bd2c1SGregory Neil Shapiro if (!tTd(8, 2))
84*d39bd2c1SGregory Neil Shapiro return;
85*d39bd2c1SGregory Neil Shapiro
86*d39bd2c1SGregory Neil Shapiro sm_dprintf("len=%u, %02x-%02x-%02x",
87*d39bd2c1SGregory Neil Shapiro len, (int)rr[0], (int)rr[1], (int)rr[2]);
88*d39bd2c1SGregory Neil Shapiro l = tTd(8, 8) ? len : 4;
89*d39bd2c1SGregory Neil Shapiro for (i = 3; i < l; i++)
90*d39bd2c1SGregory Neil Shapiro sm_dprintf(":%02X", (int)rr[i]);
91*d39bd2c1SGregory Neil Shapiro sm_dprintf("\n");
92*d39bd2c1SGregory Neil Shapiro }
93*d39bd2c1SGregory Neil Shapiro
94*d39bd2c1SGregory Neil Shapiro /*
95*d39bd2c1SGregory Neil Shapiro ** TLSA_RR_CMP -- Compare two TLSA RRs
96*d39bd2c1SGregory Neil Shapiro **
97*d39bd2c1SGregory Neil Shapiro ** Parameters:
98*d39bd2c1SGregory Neil Shapiro ** rr1 -- TLSA RR (entry to be added)
99*d39bd2c1SGregory Neil Shapiro ** l1 -- length of rr1
100*d39bd2c1SGregory Neil Shapiro ** rr2 -- TLSA RR
101*d39bd2c1SGregory Neil Shapiro ** l2 -- length of rr2
102*d39bd2c1SGregory Neil Shapiro **
103*d39bd2c1SGregory Neil Shapiro ** Returns:
104*d39bd2c1SGregory Neil Shapiro ** 0: rr1 == rr2
105*d39bd2c1SGregory Neil Shapiro ** 1: rr1 is unsupported
106*d39bd2c1SGregory Neil Shapiro */
107*d39bd2c1SGregory Neil Shapiro
108*d39bd2c1SGregory Neil Shapiro static int tlsa_rr_cmp __P((unsigned char *, int, unsigned char *, int));
109*d39bd2c1SGregory Neil Shapiro
110*d39bd2c1SGregory Neil Shapiro static int
tlsa_rr_cmp(rr1,l1,rr2,l2)111*d39bd2c1SGregory Neil Shapiro tlsa_rr_cmp(rr1, l1, rr2, l2)
112*d39bd2c1SGregory Neil Shapiro unsigned char *rr1;
113*d39bd2c1SGregory Neil Shapiro int l1;
114*d39bd2c1SGregory Neil Shapiro unsigned char *rr2;
115*d39bd2c1SGregory Neil Shapiro int l2;
116*d39bd2c1SGregory Neil Shapiro {
117*d39bd2c1SGregory Neil Shapiro /* temporary #if while investigating the implications of the alternative */
118*d39bd2c1SGregory Neil Shapiro #if FULL_COMPARE
119*d39bd2c1SGregory Neil Shapiro unsigned char r1, r2;
120*d39bd2c1SGregory Neil Shapiro int cmp;
121*d39bd2c1SGregory Neil Shapiro #endif /* FULL_COMPARE */
122*d39bd2c1SGregory Neil Shapiro
123*d39bd2c1SGregory Neil Shapiro SM_REQUIRE(NULL != rr1);
124*d39bd2c1SGregory Neil Shapiro SM_REQUIRE(NULL != rr2);
125*d39bd2c1SGregory Neil Shapiro SM_REQUIRE(l1 > 3);
126*d39bd2c1SGregory Neil Shapiro SM_REQUIRE(l2 > 3);
127*d39bd2c1SGregory Neil Shapiro
128*d39bd2c1SGregory Neil Shapiro #if FULL_COMPARE
129*d39bd2c1SGregory Neil Shapiro /*
130*d39bd2c1SGregory Neil Shapiro ** certificate usage
131*d39bd2c1SGregory Neil Shapiro ** 3: cert/fp must match
132*d39bd2c1SGregory Neil Shapiro ** 2: cert/fp must be trust anchor
133*d39bd2c1SGregory Neil Shapiro */
134*d39bd2c1SGregory Neil Shapiro
135*d39bd2c1SGregory Neil Shapiro /* preference[]: lower value: higher preference */
136*d39bd2c1SGregory Neil Shapiro r1 = rr1[0];
137*d39bd2c1SGregory Neil Shapiro r2 = rr2[0];
138*d39bd2c1SGregory Neil Shapiro if (r1 != r2)
139*d39bd2c1SGregory Neil Shapiro {
140*d39bd2c1SGregory Neil Shapiro int preference[] = { 3, 2, 1, 0 };
141*d39bd2c1SGregory Neil Shapiro
142*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
143*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
144*d39bd2c1SGregory Neil Shapiro return preference[r1] - preference[r2];
145*d39bd2c1SGregory Neil Shapiro }
146*d39bd2c1SGregory Neil Shapiro
147*d39bd2c1SGregory Neil Shapiro /*
148*d39bd2c1SGregory Neil Shapiro ** selector:
149*d39bd2c1SGregory Neil Shapiro ** 0: full cert
150*d39bd2c1SGregory Neil Shapiro ** 1: fp
151*d39bd2c1SGregory Neil Shapiro */
152*d39bd2c1SGregory Neil Shapiro
153*d39bd2c1SGregory Neil Shapiro r1 = rr1[1];
154*d39bd2c1SGregory Neil Shapiro r2 = rr2[1];
155*d39bd2c1SGregory Neil Shapiro if (r1 != r2)
156*d39bd2c1SGregory Neil Shapiro {
157*d39bd2c1SGregory Neil Shapiro int preference[] = { 1, 0 };
158*d39bd2c1SGregory Neil Shapiro
159*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
160*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
161*d39bd2c1SGregory Neil Shapiro return preference[r1] - preference[r2];
162*d39bd2c1SGregory Neil Shapiro }
163*d39bd2c1SGregory Neil Shapiro
164*d39bd2c1SGregory Neil Shapiro /*
165*d39bd2c1SGregory Neil Shapiro ** matching type:
166*d39bd2c1SGregory Neil Shapiro ** 0 -- Exact match
167*d39bd2c1SGregory Neil Shapiro ** 1 -- SHA-256 hash
168*d39bd2c1SGregory Neil Shapiro ** 2 -- SHA-512 hash
169*d39bd2c1SGregory Neil Shapiro */
170*d39bd2c1SGregory Neil Shapiro
171*d39bd2c1SGregory Neil Shapiro r1 = rr1[2];
172*d39bd2c1SGregory Neil Shapiro r2 = rr2[2];
173*d39bd2c1SGregory Neil Shapiro if (r1 != r2)
174*d39bd2c1SGregory Neil Shapiro {
175*d39bd2c1SGregory Neil Shapiro int preference[] = { 2, 0, 1 };
176*d39bd2c1SGregory Neil Shapiro
177*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
178*d39bd2c1SGregory Neil Shapiro SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
179*d39bd2c1SGregory Neil Shapiro return preference[r1] - preference[r2];
180*d39bd2c1SGregory Neil Shapiro }
181*d39bd2c1SGregory Neil Shapiro
182*d39bd2c1SGregory Neil Shapiro /* not the same length despite the same type? */
183*d39bd2c1SGregory Neil Shapiro if (l1 != l2)
184*d39bd2c1SGregory Neil Shapiro return 1;
185*d39bd2c1SGregory Neil Shapiro cmp = memcmp(rr1, rr2, l1);
186*d39bd2c1SGregory Neil Shapiro if (0 == cmp)
187*d39bd2c1SGregory Neil Shapiro return 0;
188*d39bd2c1SGregory Neil Shapiro return 1;
189*d39bd2c1SGregory Neil Shapiro #else /* FULL_COMPARE */
190*d39bd2c1SGregory Neil Shapiro /* identical? */
191*d39bd2c1SGregory Neil Shapiro if (l1 == l2 && 0 == memcmp(rr1, rr2, l1))
192*d39bd2c1SGregory Neil Shapiro return 0;
193*d39bd2c1SGregory Neil Shapiro
194*d39bd2c1SGregory Neil Shapiro /* new entry is unsupported? -> append */
195*d39bd2c1SGregory Neil Shapiro if (TLSA_UNSUPP == dane_tlsa_chk(rr1, l1, "", false))
196*d39bd2c1SGregory Neil Shapiro return 1;
197*d39bd2c1SGregory Neil Shapiro /* current entry is unsupported? -> insert new one */
198*d39bd2c1SGregory Neil Shapiro if (TLSA_UNSUPP == dane_tlsa_chk(rr2, l2, "", false))
199*d39bd2c1SGregory Neil Shapiro return -1;
200*d39bd2c1SGregory Neil Shapiro
201*d39bd2c1SGregory Neil Shapiro /* default: preserve order */
202*d39bd2c1SGregory Neil Shapiro return 1;
203*d39bd2c1SGregory Neil Shapiro #endif /* FULL_COMPARE */
204*d39bd2c1SGregory Neil Shapiro }
205*d39bd2c1SGregory Neil Shapiro
206*d39bd2c1SGregory Neil Shapiro /*
207*d39bd2c1SGregory Neil Shapiro ** TLSAINSERT -- Insert a TLSA RR
208*d39bd2c1SGregory Neil Shapiro **
209*d39bd2c1SGregory Neil Shapiro ** Parameters:
210*d39bd2c1SGregory Neil Shapiro ** dane_tlsa -- dane_tlsa entry
211*d39bd2c1SGregory Neil Shapiro ** rr -- TLSA RR
212*d39bd2c1SGregory Neil Shapiro ** pn -- (point to) number of entries
213*d39bd2c1SGregory Neil Shapiro **
214*d39bd2c1SGregory Neil Shapiro ** Returns:
215*d39bd2c1SGregory Neil Shapiro ** SM_SUCCESS: rr inserted
216*d39bd2c1SGregory Neil Shapiro ** SM_NOTDONE: rr not inserted: exists
217*d39bd2c1SGregory Neil Shapiro ** SM_FULL: rr not inserted: no space left
218*d39bd2c1SGregory Neil Shapiro */
219*d39bd2c1SGregory Neil Shapiro
220*d39bd2c1SGregory Neil Shapiro static int tlsainsert __P((dane_tlsa_P, RESOURCE_RECORD_T *, int *));
221*d39bd2c1SGregory Neil Shapiro
222*d39bd2c1SGregory Neil Shapiro static int
tlsainsert(dane_tlsa,rr,pn)223*d39bd2c1SGregory Neil Shapiro tlsainsert(dane_tlsa, rr, pn)
224*d39bd2c1SGregory Neil Shapiro dane_tlsa_P dane_tlsa;
225*d39bd2c1SGregory Neil Shapiro RESOURCE_RECORD_T *rr;
226*d39bd2c1SGregory Neil Shapiro int *pn;
227*d39bd2c1SGregory Neil Shapiro {
228*d39bd2c1SGregory Neil Shapiro int i, l1, ret;
229*d39bd2c1SGregory Neil Shapiro unsigned char *r1;
230*d39bd2c1SGregory Neil Shapiro
231*d39bd2c1SGregory Neil Shapiro SM_ASSERT(pn != NULL);
232*d39bd2c1SGregory Neil Shapiro SM_ASSERT(*pn <= MAX_TLSA_RR);
233*d39bd2c1SGregory Neil Shapiro r1 = rr->rr_u.rr_data;
234*d39bd2c1SGregory Neil Shapiro l1 = rr->rr_size;
235*d39bd2c1SGregory Neil Shapiro
236*d39bd2c1SGregory Neil Shapiro ret = SM_SUCCESS;
237*d39bd2c1SGregory Neil Shapiro for (i = 0; i < *pn; i++)
238*d39bd2c1SGregory Neil Shapiro {
239*d39bd2c1SGregory Neil Shapiro int r, j;
240*d39bd2c1SGregory Neil Shapiro
241*d39bd2c1SGregory Neil Shapiro r = tlsa_rr_cmp(r1, l1, dane_tlsa->dane_tlsa_rr[i],
242*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[i]);
243*d39bd2c1SGregory Neil Shapiro
244*d39bd2c1SGregory Neil Shapiro if (0 == r)
245*d39bd2c1SGregory Neil Shapiro {
246*d39bd2c1SGregory Neil Shapiro if (tTd(8, 80))
247*d39bd2c1SGregory Neil Shapiro sm_dprintf("func=tlsainsert, i=%d, n=%d, status=exists\n", i, *pn);
248*d39bd2c1SGregory Neil Shapiro ret = SM_NOTDONE;
249*d39bd2c1SGregory Neil Shapiro goto done;
250*d39bd2c1SGregory Neil Shapiro }
251*d39bd2c1SGregory Neil Shapiro if (r > 0)
252*d39bd2c1SGregory Neil Shapiro continue;
253*d39bd2c1SGregory Neil Shapiro
254*d39bd2c1SGregory Neil Shapiro if (*pn + 1 >= MAX_TLSA_RR)
255*d39bd2c1SGregory Neil Shapiro {
256*d39bd2c1SGregory Neil Shapiro j = MAX_TLSA_RR - 1;
257*d39bd2c1SGregory Neil Shapiro SM_FREE(dane_tlsa->dane_tlsa_rr[j]);
258*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[j] = 0;
259*d39bd2c1SGregory Neil Shapiro }
260*d39bd2c1SGregory Neil Shapiro else
261*d39bd2c1SGregory Neil Shapiro (*pn)++;
262*d39bd2c1SGregory Neil Shapiro
263*d39bd2c1SGregory Neil Shapiro for (j = MAX_TLSA_RR - 2; j >= i; j--)
264*d39bd2c1SGregory Neil Shapiro {
265*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_rr[j + 1] = dane_tlsa->dane_tlsa_rr[j];
266*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[j + 1] = dane_tlsa->dane_tlsa_len[j];
267*d39bd2c1SGregory Neil Shapiro }
268*d39bd2c1SGregory Neil Shapiro SM_ASSERT(i < MAX_TLSA_RR);
269*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_rr[i] = r1;
270*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[i] = l1;
271*d39bd2c1SGregory Neil Shapiro if (tTd(8, 80))
272*d39bd2c1SGregory Neil Shapiro sm_dprintf("func=tlsainsert, i=%d, n=%d, status=inserted\n", i, *pn);
273*d39bd2c1SGregory Neil Shapiro goto added;
274*d39bd2c1SGregory Neil Shapiro }
275*d39bd2c1SGregory Neil Shapiro
276*d39bd2c1SGregory Neil Shapiro if (*pn + 1 <= MAX_TLSA_RR)
277*d39bd2c1SGregory Neil Shapiro {
278*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_rr[*pn] = r1;
279*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[*pn] = l1;
280*d39bd2c1SGregory Neil Shapiro (*pn)++;
281*d39bd2c1SGregory Neil Shapiro if (tTd(8, 80))
282*d39bd2c1SGregory Neil Shapiro sm_dprintf("func=tlsainsert, n=%d, status=appended\n", *pn);
283*d39bd2c1SGregory Neil Shapiro }
284*d39bd2c1SGregory Neil Shapiro else
285*d39bd2c1SGregory Neil Shapiro {
286*d39bd2c1SGregory Neil Shapiro if (tTd(8, 80))
287*d39bd2c1SGregory Neil Shapiro sm_dprintf("func=tlsainsert, n=%d, status=full\n", *pn);
288*d39bd2c1SGregory Neil Shapiro return SM_FULL;
289*d39bd2c1SGregory Neil Shapiro }
290*d39bd2c1SGregory Neil Shapiro
291*d39bd2c1SGregory Neil Shapiro added:
292*d39bd2c1SGregory Neil Shapiro /* hack: instead of copying the data, just "take it over" */
293*d39bd2c1SGregory Neil Shapiro rr->rr_u.rr_data = NULL;
294*d39bd2c1SGregory Neil Shapiro done:
295*d39bd2c1SGregory Neil Shapiro return ret;
296*d39bd2c1SGregory Neil Shapiro }
297*d39bd2c1SGregory Neil Shapiro
2985b0945b5SGregory Neil Shapiro /*
2995b0945b5SGregory Neil Shapiro ** TLSAADD -- add TLSA records to dane_tlsa entry
3005b0945b5SGregory Neil Shapiro **
3015b0945b5SGregory Neil Shapiro ** Parameters:
3025b0945b5SGregory Neil Shapiro ** name -- key for stab entry (for debugging output)
3035b0945b5SGregory Neil Shapiro ** dr -- DNS reply
3045b0945b5SGregory Neil Shapiro ** dane_tlsa -- dane_tlsa entry
3055b0945b5SGregory Neil Shapiro ** dnsrc -- DNS lookup return code (h_errno)
306*d39bd2c1SGregory Neil Shapiro ** nr -- current number of TLSA records in dane_tlsa entry
3075b0945b5SGregory Neil Shapiro ** pttl -- (pointer to) TTL (in/out)
3085b0945b5SGregory Neil Shapiro ** level -- recursion level (CNAMEs)
3095b0945b5SGregory Neil Shapiro **
3105b0945b5SGregory Neil Shapiro ** Returns:
3115b0945b5SGregory Neil Shapiro ** new number of TLSA records
312*d39bd2c1SGregory Neil Shapiro **
313*d39bd2c1SGregory Neil Shapiro ** NOTE: the array for TLSA RRs could be "full" which is not
314*d39bd2c1SGregory Neil Shapiro ** handled well (yet).
3155b0945b5SGregory Neil Shapiro */
3165b0945b5SGregory Neil Shapiro
3175b0945b5SGregory Neil Shapiro static int tlsaadd __P((const char *, DNS_REPLY_T *, dane_tlsa_P, int, int,
3185b0945b5SGregory Neil Shapiro unsigned int *, int));
3195b0945b5SGregory Neil Shapiro
3205b0945b5SGregory Neil Shapiro static int
tlsaadd(name,dr,dane_tlsa,dnsrc,nr,pttl,level)321*d39bd2c1SGregory Neil Shapiro tlsaadd(name, dr, dane_tlsa, dnsrc, nr, pttl, level)
3225b0945b5SGregory Neil Shapiro const char *name;
3235b0945b5SGregory Neil Shapiro DNS_REPLY_T *dr;
3245b0945b5SGregory Neil Shapiro dane_tlsa_P dane_tlsa;
3255b0945b5SGregory Neil Shapiro int dnsrc;
326*d39bd2c1SGregory Neil Shapiro int nr;
3275b0945b5SGregory Neil Shapiro unsigned int *pttl;
3285b0945b5SGregory Neil Shapiro int level;
3295b0945b5SGregory Neil Shapiro {
3305b0945b5SGregory Neil Shapiro RESOURCE_RECORD_T *rr;
3315b0945b5SGregory Neil Shapiro unsigned int ttl;
3325b0945b5SGregory Neil Shapiro int nprev;
3335b0945b5SGregory Neil Shapiro
3345b0945b5SGregory Neil Shapiro if (dnsrc != 0)
3355b0945b5SGregory Neil Shapiro {
3365b0945b5SGregory Neil Shapiro if (tTd(8, 2))
337*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd, name=%s, prev=%d, dnsrc=%d\n",
3385b0945b5SGregory Neil Shapiro name, dane_tlsa->dane_tlsa_dnsrc, dnsrc);
3395b0945b5SGregory Neil Shapiro
3405b0945b5SGregory Neil Shapiro /* check previous error and keep the "most important" one? */
3415b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_dnsrc = dnsrc;
3425b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
3435b0945b5SGregory Neil Shapiro if (tTd(8, 110))
3445b0945b5SGregory Neil Shapiro *pttl = tTdlevel(8)-110; /* how to make this an option? */
3455b0945b5SGregory Neil Shapiro else
3465b0945b5SGregory Neil Shapiro # endif
3472fb4f839SGregory Neil Shapiro /* "else" in #if code above */
3482fb4f839SGregory Neil Shapiro *pttl = SM_NEG_TTL;
349*d39bd2c1SGregory Neil Shapiro return nr;
3505b0945b5SGregory Neil Shapiro }
3515b0945b5SGregory Neil Shapiro if (dr == NULL)
352*d39bd2c1SGregory Neil Shapiro return nr;
3535b0945b5SGregory Neil Shapiro if (dr->dns_r_h.ad != 1 && Dane == DANE_SECURE) /* not secure? */
354*d39bd2c1SGregory Neil Shapiro return nr;
3555b0945b5SGregory Neil Shapiro ttl = *pttl;
3565b0945b5SGregory Neil Shapiro
3575b0945b5SGregory Neil Shapiro /* first: try to find TLSA records */
358*d39bd2c1SGregory Neil Shapiro nprev = nr;
359*d39bd2c1SGregory Neil Shapiro for (rr = dr->dns_r_head; rr != NULL; rr = rr->rr_next)
3605b0945b5SGregory Neil Shapiro {
361*d39bd2c1SGregory Neil Shapiro int tlsa_chk, r;
3625b0945b5SGregory Neil Shapiro
3635b0945b5SGregory Neil Shapiro if (rr->rr_type != T_TLSA)
3645b0945b5SGregory Neil Shapiro {
3655b0945b5SGregory Neil Shapiro if (rr->rr_type != T_CNAME && tTd(8, 8))
366*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, type=%s\n", name,
3675b0945b5SGregory Neil Shapiro dns_type_to_string(rr->rr_type));
3685b0945b5SGregory Neil Shapiro continue;
3695b0945b5SGregory Neil Shapiro }
3705b0945b5SGregory Neil Shapiro tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data, rr->rr_size, name,
3715b0945b5SGregory Neil Shapiro true);
372*d39bd2c1SGregory Neil Shapiro if (TLSA_UNSUPP == tlsa_chk)
373*d39bd2c1SGregory Neil Shapiro TLSA_SET_FL(dane_tlsa, TLSAFLUNS);
3745b0945b5SGregory Neil Shapiro if (!TLSA_IS_VALID(tlsa_chk))
3755b0945b5SGregory Neil Shapiro continue;
376*d39bd2c1SGregory Neil Shapiro if (TLSA_IS_SUPPORTED(tlsa_chk))
377*d39bd2c1SGregory Neil Shapiro TLSA_SET_FL(dane_tlsa, TLSAFLSUP);
3785b0945b5SGregory Neil Shapiro
3795b0945b5SGregory Neil Shapiro /*
380*d39bd2c1SGregory Neil Shapiro ** Note: rr_u.rr_data might be NULL after tlsainsert()
381*d39bd2c1SGregory Neil Shapiro ** for nice debug output: print the data into a string
382*d39bd2c1SGregory Neil Shapiro ** and then use it after tlsainsert().
3835b0945b5SGregory Neil Shapiro */
3845b0945b5SGregory Neil Shapiro
3855b0945b5SGregory Neil Shapiro if (tTd(8, 2))
3865b0945b5SGregory Neil Shapiro {
387*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, nr=%d, ", name, nr);
388*d39bd2c1SGregory Neil Shapiro tlsa_rr_print(rr->rr_u.rr_data, rr->rr_size);
3895b0945b5SGregory Neil Shapiro }
390*d39bd2c1SGregory Neil Shapiro r = tlsainsert(dane_tlsa, rr, &nr);
391*d39bd2c1SGregory Neil Shapiro if (SM_FULL == r)
392*d39bd2c1SGregory Neil Shapiro TLSA_SET_FL(dane_tlsa, TLSAFL2MANY);
393*d39bd2c1SGregory Neil Shapiro if (tTd(8, 2))
394*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsainsert=%d, nr=%d\n", r, nr);
3955b0945b5SGregory Neil Shapiro
3965b0945b5SGregory Neil Shapiro /* require some minimum TTL? */
3975b0945b5SGregory Neil Shapiro if (ttl > rr->rr_ttl && rr->rr_ttl > 0)
3985b0945b5SGregory Neil Shapiro ttl = rr->rr_ttl;
399*d39bd2c1SGregory Neil Shapiro }
4005b0945b5SGregory Neil Shapiro
401*d39bd2c1SGregory Neil Shapiro if (tTd(8, 2))
402*d39bd2c1SGregory Neil Shapiro {
403*d39bd2c1SGregory Neil Shapiro unsigned int ui;
404*d39bd2c1SGregory Neil Shapiro
405*d39bd2c1SGregory Neil Shapiro SM_ASSERT(nr <= MAX_TLSA_RR);
406*d39bd2c1SGregory Neil Shapiro for (ui = 0; ui < (unsigned int)nr; ui++)
407*d39bd2c1SGregory Neil Shapiro {
408*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, ui=%u, ", name, ui);
409*d39bd2c1SGregory Neil Shapiro tlsa_rr_print(dane_tlsa->dane_tlsa_rr[ui],
410*d39bd2c1SGregory Neil Shapiro dane_tlsa->dane_tlsa_len[ui]);
411*d39bd2c1SGregory Neil Shapiro }
412*d39bd2c1SGregory Neil Shapiro }
413*d39bd2c1SGregory Neil Shapiro
414*d39bd2c1SGregory Neil Shapiro if (TLSA_IS_FL(dane_tlsa, TLSAFL2MANY))
415*d39bd2c1SGregory Neil Shapiro {
416*d39bd2c1SGregory Neil Shapiro if (tTd(8, 20))
417*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, rr=%p, nr=%d, toomany=%d\n", name, rr, nr, TLSA_IS_FL(dane_tlsa, TLSAFL2MANY));
4185b0945b5SGregory Neil Shapiro }
4195b0945b5SGregory Neil Shapiro
4205b0945b5SGregory Neil Shapiro /* second: check for CNAME records, but only if no TLSA RR was added */
421*d39bd2c1SGregory Neil Shapiro for (rr = dr->dns_r_head; rr != NULL && nprev == nr; rr = rr->rr_next)
4225b0945b5SGregory Neil Shapiro {
4235b0945b5SGregory Neil Shapiro DNS_REPLY_T *drc;
4245b0945b5SGregory Neil Shapiro int err, herr;
4255b0945b5SGregory Neil Shapiro
4265b0945b5SGregory Neil Shapiro if (rr->rr_type != T_CNAME)
4275b0945b5SGregory Neil Shapiro continue;
4285b0945b5SGregory Neil Shapiro if (level > 1)
4295b0945b5SGregory Neil Shapiro {
4305b0945b5SGregory Neil Shapiro if (tTd(8, 2))
431*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, CNAME=%s, level=%d\n",
4325b0945b5SGregory Neil Shapiro name, rr->rr_u.rr_txt, level);
4335b0945b5SGregory Neil Shapiro continue;
4345b0945b5SGregory Neil Shapiro }
4355b0945b5SGregory Neil Shapiro
4365b0945b5SGregory Neil Shapiro drc = dns_lookup_int(rr->rr_u.rr_txt, C_IN, T_TLSA, 0, 0,
437*d39bd2c1SGregory Neil Shapiro Dane == DANE_SECURE ? SM_RES_DNSSEC : 0,
4385b0945b5SGregory Neil Shapiro RR_RAW, &err, &herr);
4395b0945b5SGregory Neil Shapiro
4405b0945b5SGregory Neil Shapiro if (tTd(8, 2))
441*d39bd2c1SGregory Neil Shapiro sm_dprintf("tlsaadd: name=%s, CNAME=%s, level=%d, dr=%p, ad=%d, err=%d, herr=%d\n",
4425b0945b5SGregory Neil Shapiro name, rr->rr_u.rr_txt, level,
4435b0945b5SGregory Neil Shapiro (void *)drc, drc != NULL ? drc->dns_r_h.ad : -1,
4445b0945b5SGregory Neil Shapiro err, herr);
445*d39bd2c1SGregory Neil Shapiro nprev = nr = tlsaadd(name, drc, dane_tlsa, herr, nr, pttl,
4465b0945b5SGregory Neil Shapiro level + 1);
4475b0945b5SGregory Neil Shapiro dns_free_data(drc);
4485b0945b5SGregory Neil Shapiro drc = NULL;
4495b0945b5SGregory Neil Shapiro }
4505b0945b5SGregory Neil Shapiro
451*d39bd2c1SGregory Neil Shapiro if (TLSA_IS_FL(dane_tlsa, TLSAFLUNS) &&
452*d39bd2c1SGregory Neil Shapiro !TLSA_IS_FL(dane_tlsa, TLSAFLSUP) && LogLevel > 9)
453*d39bd2c1SGregory Neil Shapiro {
454*d39bd2c1SGregory Neil Shapiro sm_syslog(LOG_NOTICE, NOQID,
455*d39bd2c1SGregory Neil Shapiro "TLSA=%s, records=%d%s",
456*d39bd2c1SGregory Neil Shapiro name, nr, ONLYUNSUPTLSARR);
457*d39bd2c1SGregory Neil Shapiro }
4585b0945b5SGregory Neil Shapiro *pttl = ttl;
459*d39bd2c1SGregory Neil Shapiro return nr;
4605b0945b5SGregory Neil Shapiro }
4615b0945b5SGregory Neil Shapiro
4625b0945b5SGregory Neil Shapiro /*
4635b0945b5SGregory Neil Shapiro ** GETTLSA -- get TLSA records for named host using DNS
4645b0945b5SGregory Neil Shapiro **
4655b0945b5SGregory Neil Shapiro ** Parameters:
4665b0945b5SGregory Neil Shapiro ** host -- host
4675b0945b5SGregory Neil Shapiro ** name -- name for stab entry key (if NULL: host)
4685b0945b5SGregory Neil Shapiro ** pste -- (pointer to) stab entry (output)
4695b0945b5SGregory Neil Shapiro ** flags -- TLSAFL*
4705b0945b5SGregory Neil Shapiro ** mxttl -- TTL of MX (or host)
471*d39bd2c1SGregory Neil Shapiro ** port -- port number used in TLSA queries (_PORT._tcp.)
4725b0945b5SGregory Neil Shapiro **
4735b0945b5SGregory Neil Shapiro ** Returns:
4745b0945b5SGregory Neil Shapiro ** The number of TLSA records found.
4755b0945b5SGregory Neil Shapiro ** <0 if there is an internal failure.
4765b0945b5SGregory Neil Shapiro **
4775b0945b5SGregory Neil Shapiro ** Side effects:
4785b0945b5SGregory Neil Shapiro ** Enters TLSA RRs into stab().
4795b0945b5SGregory Neil Shapiro ** If the DNS lookup fails temporarily, an "empty" entry is
4805b0945b5SGregory Neil Shapiro ** created with that DNS error code.
4815b0945b5SGregory Neil Shapiro */
4825b0945b5SGregory Neil Shapiro
4835b0945b5SGregory Neil Shapiro int
gettlsa(host,name,pste,flags,mxttl,port)4845b0945b5SGregory Neil Shapiro gettlsa(host, name, pste, flags, mxttl, port)
4855b0945b5SGregory Neil Shapiro char *host;
4865b0945b5SGregory Neil Shapiro char *name;
4875b0945b5SGregory Neil Shapiro STAB **pste;
4885b0945b5SGregory Neil Shapiro unsigned long flags;
4895b0945b5SGregory Neil Shapiro unsigned int mxttl;
4905b0945b5SGregory Neil Shapiro unsigned int port;
4915b0945b5SGregory Neil Shapiro {
4925b0945b5SGregory Neil Shapiro DNS_REPLY_T *dr;
4935b0945b5SGregory Neil Shapiro dane_tlsa_P dane_tlsa;
4945b0945b5SGregory Neil Shapiro STAB *ste;
4955b0945b5SGregory Neil Shapiro time_t now;
4965b0945b5SGregory Neil Shapiro unsigned int ttl;
4975b0945b5SGregory Neil Shapiro int n_rrs, len, err, herr;
498*d39bd2c1SGregory Neil Shapiro bool isrname, expired;
4995b0945b5SGregory Neil Shapiro char nbuf[MAXDNAME];
5005b0945b5SGregory Neil Shapiro char key[MAXDNAME];
5015b0945b5SGregory Neil Shapiro
5025b0945b5SGregory Neil Shapiro SM_REQUIRE(host != NULL);
5035b0945b5SGregory Neil Shapiro if (pste != NULL)
5045b0945b5SGregory Neil Shapiro *pste = NULL;
5055b0945b5SGregory Neil Shapiro if ('\0' == *host)
5065b0945b5SGregory Neil Shapiro return 0;
5075b0945b5SGregory Neil Shapiro
508*d39bd2c1SGregory Neil Shapiro expired = false;
5095b0945b5SGregory Neil Shapiro isrname = NULL == name;
5105b0945b5SGregory Neil Shapiro if (isrname)
5115b0945b5SGregory Neil Shapiro name = host;
512*d39bd2c1SGregory Neil Shapiro
513*d39bd2c1SGregory Neil Shapiro /*
514*d39bd2c1SGregory Neil Shapiro ** If host->MX lookup was not secure then do not look up TLSA RRs.
515*d39bd2c1SGregory Neil Shapiro ** Note: this is currently a hack: TLSAFLADMX is used as input flag,
516*d39bd2c1SGregory Neil Shapiro ** it is (SHOULD!) NOT stored in dane_tlsa->dane_tlsa_flags
517*d39bd2c1SGregory Neil Shapiro */
518*d39bd2c1SGregory Neil Shapiro
519*d39bd2c1SGregory Neil Shapiro if (DANE_SECURE == Dane && 0 == (TLSAFLADMX & flags) &&
520*d39bd2c1SGregory Neil Shapiro 0 != (TLSAFLNEW & flags))
521*d39bd2c1SGregory Neil Shapiro {
522*d39bd2c1SGregory Neil Shapiro if (tTd(8, 2))
523*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, flags=%#lx, no ad but Dane=Secure\n",
524*d39bd2c1SGregory Neil Shapiro host, flags);
525*d39bd2c1SGregory Neil Shapiro return 0;
526*d39bd2c1SGregory Neil Shapiro }
527*d39bd2c1SGregory Neil Shapiro
5285b0945b5SGregory Neil Shapiro now = 0;
5295b0945b5SGregory Neil Shapiro n_rrs = 0;
5305b0945b5SGregory Neil Shapiro dr = NULL;
5315b0945b5SGregory Neil Shapiro dane_tlsa = NULL;
5325b0945b5SGregory Neil Shapiro len = strlen(name);
5335b0945b5SGregory Neil Shapiro if (len > 1 && name[len - 1] == '.')
5345b0945b5SGregory Neil Shapiro {
5355b0945b5SGregory Neil Shapiro len--;
5365b0945b5SGregory Neil Shapiro name[len] = '\0';
5375b0945b5SGregory Neil Shapiro }
5385b0945b5SGregory Neil Shapiro else
5395b0945b5SGregory Neil Shapiro len = -1;
540*d39bd2c1SGregory Neil Shapiro if (0 == port || tTd(66, 101))
5415b0945b5SGregory Neil Shapiro port = 25;
542*d39bd2c1SGregory Neil Shapiro (void) sm_snprintf(key, sizeof(key), "_%u.%s", port, name);
5435b0945b5SGregory Neil Shapiro ste = stab(key, ST_TLSA_RR, ST_FIND);
5445b0945b5SGregory Neil Shapiro if (tTd(8, 2))
545*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, %s, ste=%p, pste=%p, flags=%#lx, port=%d\n",
5465b0945b5SGregory Neil Shapiro host, isrname ? "" : name, (void *)ste, (void *)pste,
5475b0945b5SGregory Neil Shapiro flags, port);
5485b0945b5SGregory Neil Shapiro
5495b0945b5SGregory Neil Shapiro if (ste != NULL)
5505b0945b5SGregory Neil Shapiro dane_tlsa = ste->s_tlsa;
5515b0945b5SGregory Neil Shapiro
552*d39bd2c1SGregory Neil Shapiro #if 0
553*d39bd2c1SGregory Neil Shapiro // /* Do not reload TLSA RRs if the MX RRs were not securely retrieved. */
554*d39bd2c1SGregory Neil Shapiro // if (pste != NULL
555*d39bd2c1SGregory Neil Shapiro // && dane_tlsa != NULL && TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX)
556*d39bd2c1SGregory Neil Shapiro // && DANE_SECURE == Dane)
557*d39bd2c1SGregory Neil Shapiro // goto end;
558*d39bd2c1SGregory Neil Shapiro #endif
5595b0945b5SGregory Neil Shapiro if (ste != NULL)
5605b0945b5SGregory Neil Shapiro {
5615b0945b5SGregory Neil Shapiro SM_ASSERT(dane_tlsa != NULL);
5625b0945b5SGregory Neil Shapiro now = curtime();
563*d39bd2c1SGregory Neil Shapiro if (tTd(8, 20))
564*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, found-ste=%p, ste_flags=%#lx, expired=%d\n", host, ste, ste->s_tlsa->dane_tlsa_flags, dane_tlsa->dane_tlsa_exp <= now);
5655b0945b5SGregory Neil Shapiro if (dane_tlsa->dane_tlsa_exp <= now
5665b0945b5SGregory Neil Shapiro && 0 == (TLSAFLNOEXP & flags))
567*d39bd2c1SGregory Neil Shapiro {
5685b0945b5SGregory Neil Shapiro dane_tlsa_clr(dane_tlsa);
569*d39bd2c1SGregory Neil Shapiro expired = true;
570*d39bd2c1SGregory Neil Shapiro }
5715b0945b5SGregory Neil Shapiro else
5725b0945b5SGregory Neil Shapiro {
5735b0945b5SGregory Neil Shapiro n_rrs = dane_tlsa->dane_tlsa_n;
5745b0945b5SGregory Neil Shapiro goto end;
5755b0945b5SGregory Neil Shapiro }
5765b0945b5SGregory Neil Shapiro }
5775b0945b5SGregory Neil Shapiro
578*d39bd2c1SGregory Neil Shapiro /* get entries if none exist yet? */
579*d39bd2c1SGregory Neil Shapiro if ((0 == (TLSAFLNEW & flags)) && !expired)
580*d39bd2c1SGregory Neil Shapiro goto end;
581*d39bd2c1SGregory Neil Shapiro
5825b0945b5SGregory Neil Shapiro if (dane_tlsa == NULL)
5835b0945b5SGregory Neil Shapiro {
5845b0945b5SGregory Neil Shapiro dane_tlsa = (dane_tlsa_P) sm_malloc(sizeof(*dane_tlsa));
5855b0945b5SGregory Neil Shapiro if (dane_tlsa == NULL)
5865b0945b5SGregory Neil Shapiro {
5875b0945b5SGregory Neil Shapiro n_rrs = -ENOMEM;
5885b0945b5SGregory Neil Shapiro goto end;
5895b0945b5SGregory Neil Shapiro }
5905b0945b5SGregory Neil Shapiro memset(dane_tlsa, '\0', sizeof(*dane_tlsa));
5915b0945b5SGregory Neil Shapiro }
5925b0945b5SGregory Neil Shapiro
5935b0945b5SGregory Neil Shapiro /* There are flags to store -- just set those, do nothing else. */
5945b0945b5SGregory Neil Shapiro if (TLSA_STORE_FL(flags))
5955b0945b5SGregory Neil Shapiro {
5965b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_flags = flags;
5975b0945b5SGregory Neil Shapiro ttl = mxttl > 0 ? mxttl: SM_DEFAULT_TTL;
5985b0945b5SGregory Neil Shapiro goto done;
5995b0945b5SGregory Neil Shapiro }
6005b0945b5SGregory Neil Shapiro
6015b0945b5SGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "_%u._tcp.%s", port, host);
6025b0945b5SGregory Neil Shapiro dr = dns_lookup_int(nbuf, C_IN, T_TLSA, 0, 0,
603*d39bd2c1SGregory Neil Shapiro (TLSAFLADMX & flags) ? SM_RES_DNSSEC : 0,
6045b0945b5SGregory Neil Shapiro RR_RAW, &err, &herr);
6055b0945b5SGregory Neil Shapiro if (tTd(8, 2))
606*d39bd2c1SGregory Neil Shapiro {
607*d39bd2c1SGregory Neil Shapiro #if 0
608*d39bd2c1SGregory Neil Shapiro /* disabled -- what to do with these two counters? log them "somewhere"? */
609*d39bd2c1SGregory Neil Shapiro // if (NULL != dr && tTd(8, 12))
610*d39bd2c1SGregory Neil Shapiro // {
611*d39bd2c1SGregory Neil Shapiro // RESOURCE_RECORD_T *rr;
612*d39bd2c1SGregory Neil Shapiro // unsigned int ntlsarrs, usable;
613*d39bd2c1SGregory Neil Shapiro //
614*d39bd2c1SGregory Neil Shapiro // ntlsarrs = usable = 0;
615*d39bd2c1SGregory Neil Shapiro // for (rr = dr->dns_r_head; rr != NULL; rr = rr->rr_next)
616*d39bd2c1SGregory Neil Shapiro // {
617*d39bd2c1SGregory Neil Shapiro // int tlsa_chk;
618*d39bd2c1SGregory Neil Shapiro //
619*d39bd2c1SGregory Neil Shapiro // if (rr->rr_type != T_TLSA)
620*d39bd2c1SGregory Neil Shapiro // continue;
621*d39bd2c1SGregory Neil Shapiro // ++ntlsarrs;
622*d39bd2c1SGregory Neil Shapiro // tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data,
623*d39bd2c1SGregory Neil Shapiro // rr->rr_size, name, false);
624*d39bd2c1SGregory Neil Shapiro // if (TLSA_IS_SUPPORTED(tlsa_chk))
625*d39bd2c1SGregory Neil Shapiro // ++usable;
626*d39bd2c1SGregory Neil Shapiro //
627*d39bd2c1SGregory Neil Shapiro // }
628*d39bd2c1SGregory Neil Shapiro // sm_dprintf("gettlsa: host=%s, ntlsarrs=%u, usable\%u\n", host, ntlsarrs, usable);
629*d39bd2c1SGregory Neil Shapiro // }
630*d39bd2c1SGregory Neil Shapiro #endif /* 0 */
631*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, dr=%p, ad=%d, err=%d, herr=%d\n",
632*d39bd2c1SGregory Neil Shapiro host, (void *)dr,
633*d39bd2c1SGregory Neil Shapiro dr != NULL ? dr->dns_r_h.ad : -1, err, herr);
634*d39bd2c1SGregory Neil Shapiro }
6355b0945b5SGregory Neil Shapiro ttl = UINT_MAX;
6365b0945b5SGregory Neil Shapiro n_rrs = tlsaadd(key, dr, dane_tlsa, herr, n_rrs, &ttl, 0);
6375b0945b5SGregory Neil Shapiro
6385b0945b5SGregory Neil Shapiro /* no valid entries found? */
6395b0945b5SGregory Neil Shapiro if (n_rrs == 0 && !TLSA_RR_TEMPFAIL(dane_tlsa))
6405b0945b5SGregory Neil Shapiro {
6415b0945b5SGregory Neil Shapiro if (tTd(8, 2))
642*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, n_rrs=%d, herr=%d, status=NOT_ADDED\n",
6435b0945b5SGregory Neil Shapiro host, n_rrs, dane_tlsa->dane_tlsa_dnsrc);
6445b0945b5SGregory Neil Shapiro goto cleanup;
6455b0945b5SGregory Neil Shapiro }
6465b0945b5SGregory Neil Shapiro
6475b0945b5SGregory Neil Shapiro done:
6485b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_n = n_rrs;
6495b0945b5SGregory Neil Shapiro if (!isrname)
6505b0945b5SGregory Neil Shapiro {
6515b0945b5SGregory Neil Shapiro SM_FREE(dane_tlsa->dane_tlsa_sni);
6525b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_sni = sm_strdup(host);
6535b0945b5SGregory Neil Shapiro }
6545b0945b5SGregory Neil Shapiro if (NULL == ste)
6555b0945b5SGregory Neil Shapiro {
6565b0945b5SGregory Neil Shapiro ste = stab(key, ST_TLSA_RR, ST_ENTER);
6575b0945b5SGregory Neil Shapiro if (NULL == ste)
6585b0945b5SGregory Neil Shapiro goto error;
6595b0945b5SGregory Neil Shapiro }
6605b0945b5SGregory Neil Shapiro ste->s_tlsa = dane_tlsa;
6615b0945b5SGregory Neil Shapiro if (now == 0)
6625b0945b5SGregory Neil Shapiro now = curtime();
6635b0945b5SGregory Neil Shapiro dane_tlsa->dane_tlsa_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
6645b0945b5SGregory Neil Shapiro dns_free_data(dr);
6655b0945b5SGregory Neil Shapiro dr = NULL;
6665b0945b5SGregory Neil Shapiro goto end;
6675b0945b5SGregory Neil Shapiro
6685b0945b5SGregory Neil Shapiro error:
6695b0945b5SGregory Neil Shapiro if (tTd(8, 2))
670*d39bd2c1SGregory Neil Shapiro sm_dprintf("gettlsa: host=%s, key=%s, status=error\n", host, key);
6715b0945b5SGregory Neil Shapiro n_rrs = -1;
6725b0945b5SGregory Neil Shapiro cleanup:
6735b0945b5SGregory Neil Shapiro if (NULL == ste)
6745b0945b5SGregory Neil Shapiro dane_tlsa_free(dane_tlsa);
6755b0945b5SGregory Neil Shapiro dns_free_data(dr);
6765b0945b5SGregory Neil Shapiro dr = NULL;
6775b0945b5SGregory Neil Shapiro
6785b0945b5SGregory Neil Shapiro end:
6795b0945b5SGregory Neil Shapiro if (pste != NULL && ste != NULL)
6805b0945b5SGregory Neil Shapiro *pste = ste;
6815b0945b5SGregory Neil Shapiro if (len > 0)
6825b0945b5SGregory Neil Shapiro host[len] = '.';
6835b0945b5SGregory Neil Shapiro return n_rrs;
6845b0945b5SGregory Neil Shapiro }
6855b0945b5SGregory Neil Shapiro # endif /* DANE */
6865b0945b5SGregory Neil Shapiro
68740266059SGregory Neil Shapiro /*
68840266059SGregory Neil Shapiro ** GETFALLBACKMXRR -- get MX resource records for fallback MX host.
68940266059SGregory Neil Shapiro **
69040266059SGregory Neil Shapiro ** We have to initialize this once before doing anything else.
69140266059SGregory Neil Shapiro ** Moreover, we have to repeat this from time to time to avoid
69240266059SGregory Neil Shapiro ** stale data, e.g., in persistent queue runners.
69340266059SGregory Neil Shapiro ** This should be done in a parent process so the child
69440266059SGregory Neil Shapiro ** processes have the right data.
69540266059SGregory Neil Shapiro **
69640266059SGregory Neil Shapiro ** Parameters:
69740266059SGregory Neil Shapiro ** host -- the name of the fallback MX host.
69840266059SGregory Neil Shapiro **
69940266059SGregory Neil Shapiro ** Returns:
70040266059SGregory Neil Shapiro ** number of MX records.
70140266059SGregory Neil Shapiro **
70240266059SGregory Neil Shapiro ** Side Effects:
703e92d3f3fSGregory Neil Shapiro ** Populates NumFallbackMXHosts and fbhosts.
70440266059SGregory Neil Shapiro ** Sets renewal time (based on TTL).
70540266059SGregory Neil Shapiro */
70640266059SGregory Neil Shapiro
707e92d3f3fSGregory Neil Shapiro int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */
70840266059SGregory Neil Shapiro static char *fbhosts[MAXMXHOSTS + 1];
70940266059SGregory Neil Shapiro
71040266059SGregory Neil Shapiro int
getfallbackmxrr(host)71140266059SGregory Neil Shapiro getfallbackmxrr(host)
71240266059SGregory Neil Shapiro char *host;
71340266059SGregory Neil Shapiro {
71440266059SGregory Neil Shapiro int i, rcode;
71540266059SGregory Neil Shapiro int ttl;
71640266059SGregory Neil Shapiro static time_t renew = 0;
71740266059SGregory Neil Shapiro
71840266059SGregory Neil Shapiro # if 0
71940266059SGregory Neil Shapiro /* This is currently done before this function is called. */
7202fb4f839SGregory Neil Shapiro if (SM_IS_EMPTY(host))
72140266059SGregory Neil Shapiro return 0;
72240266059SGregory Neil Shapiro # endif /* 0 */
723e92d3f3fSGregory Neil Shapiro if (NumFallbackMXHosts > 0 && renew > curtime())
724e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts;
7255b0945b5SGregory Neil Shapiro
726*d39bd2c1SGregory Neil Shapiro /*
727*d39bd2c1SGregory Neil Shapiro ** For DANE we need to invoke getmxrr() to get the TLSA RRs.
728*d39bd2c1SGregory Neil Shapiro ** Hack: don't do that if its not a FQHN (e.g., [localhost])
729*d39bd2c1SGregory Neil Shapiro ** This also triggers for IPv4 addresses, but not IPv6!
730*d39bd2c1SGregory Neil Shapiro */
731*d39bd2c1SGregory Neil Shapiro
732*d39bd2c1SGregory Neil Shapiro if (host[0] == '[' && (!Dane || strchr(host, '.') == NULL))
73340266059SGregory Neil Shapiro {
73440266059SGregory Neil Shapiro fbhosts[0] = host;
735e92d3f3fSGregory Neil Shapiro NumFallbackMXHosts = 1;
73640266059SGregory Neil Shapiro }
73740266059SGregory Neil Shapiro else
73840266059SGregory Neil Shapiro {
73940266059SGregory Neil Shapiro /* free old data */
740e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++)
74140266059SGregory Neil Shapiro sm_free(fbhosts[i]);
74240266059SGregory Neil Shapiro
7435b0945b5SGregory Neil Shapiro /*
7445b0945b5SGregory Neil Shapiro ** Get new data.
7455b0945b5SGregory Neil Shapiro ** Note: passing 0 as port is not correct but we cannot
7465b0945b5SGregory Neil Shapiro ** determine the port number as there is no mailer.
7475b0945b5SGregory Neil Shapiro */
7485b0945b5SGregory Neil Shapiro
7495b0945b5SGregory Neil Shapiro NumFallbackMXHosts = getmxrr(host, fbhosts, NULL,
7505b0945b5SGregory Neil Shapiro # if DANE
7515b0945b5SGregory Neil Shapiro (DANE_SECURE == Dane) ? ISAD :
7525b0945b5SGregory Neil Shapiro # endif
753*d39bd2c1SGregory Neil Shapiro 0, &rcode, &ttl, 0, NULL);
75440266059SGregory Neil Shapiro renew = curtime() + ttl;
755e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts; i++)
75640266059SGregory Neil Shapiro fbhosts[i] = newstr(fbhosts[i]);
75740266059SGregory Neil Shapiro }
7585b0945b5SGregory Neil Shapiro if (NumFallbackMXHosts == NULLMX)
7595b0945b5SGregory Neil Shapiro NumFallbackMXHosts = 0;
760e92d3f3fSGregory Neil Shapiro return NumFallbackMXHosts;
76140266059SGregory Neil Shapiro }
76240266059SGregory Neil Shapiro
76340266059SGregory Neil Shapiro /*
76440266059SGregory Neil Shapiro ** FALLBACKMXRR -- add MX resource records for fallback MX host to list.
76540266059SGregory Neil Shapiro **
76640266059SGregory Neil Shapiro ** Parameters:
76740266059SGregory Neil Shapiro ** nmx -- current number of MX records.
76840266059SGregory Neil Shapiro ** prefs -- array of preferences.
76940266059SGregory Neil Shapiro ** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
77040266059SGregory Neil Shapiro **
77140266059SGregory Neil Shapiro ** Returns:
77240266059SGregory Neil Shapiro ** new number of MX records.
77340266059SGregory Neil Shapiro **
77440266059SGregory Neil Shapiro ** Side Effects:
775e92d3f3fSGregory Neil Shapiro ** If FallbackMX was set, it appends the MX records for
77640266059SGregory Neil Shapiro ** that host to mxhosts (and modifies prefs accordingly).
77740266059SGregory Neil Shapiro */
77840266059SGregory Neil Shapiro
77940266059SGregory Neil Shapiro static int
fallbackmxrr(nmx,prefs,mxhosts)78040266059SGregory Neil Shapiro fallbackmxrr(nmx, prefs, mxhosts)
78140266059SGregory Neil Shapiro int nmx;
78240266059SGregory Neil Shapiro unsigned short *prefs;
78340266059SGregory Neil Shapiro char **mxhosts;
78440266059SGregory Neil Shapiro {
78540266059SGregory Neil Shapiro int i;
78640266059SGregory Neil Shapiro
787e92d3f3fSGregory Neil Shapiro for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++)
78840266059SGregory Neil Shapiro {
78940266059SGregory Neil Shapiro if (nmx > 0)
79040266059SGregory Neil Shapiro prefs[nmx] = prefs[nmx - 1] + 1;
79140266059SGregory Neil Shapiro else
79240266059SGregory Neil Shapiro prefs[nmx] = 0;
79340266059SGregory Neil Shapiro mxhosts[nmx++] = fbhosts[i];
79440266059SGregory Neil Shapiro }
79540266059SGregory Neil Shapiro return nmx;
79640266059SGregory Neil Shapiro }
79740266059SGregory Neil Shapiro
7982fb4f839SGregory Neil Shapiro # if USE_EAI
7992fb4f839SGregory Neil Shapiro
8002fb4f839SGregory Neil Shapiro /*
8012fb4f839SGregory Neil Shapiro ** HN2ALABEL -- convert hostname in U-label format to A-label format
8022fb4f839SGregory Neil Shapiro **
8032fb4f839SGregory Neil Shapiro ** Parameters:
8042fb4f839SGregory Neil Shapiro ** hostname -- hostname in U-label format
8052fb4f839SGregory Neil Shapiro **
8062fb4f839SGregory Neil Shapiro ** Returns:
8072fb4f839SGregory Neil Shapiro ** hostname in A-label format in a local static buffer.
8082fb4f839SGregory Neil Shapiro ** It must be copied before the function is called again.
8092fb4f839SGregory Neil Shapiro */
8102fb4f839SGregory Neil Shapiro
8112fb4f839SGregory Neil Shapiro const char *
hn2alabel(hostname)8122fb4f839SGregory Neil Shapiro hn2alabel(hostname)
8132fb4f839SGregory Neil Shapiro const char *hostname;
8142fb4f839SGregory Neil Shapiro {
8152fb4f839SGregory Neil Shapiro UErrorCode error = U_ZERO_ERROR;
8162fb4f839SGregory Neil Shapiro UIDNAInfo info = UIDNA_INFO_INITIALIZER;
8172fb4f839SGregory Neil Shapiro UIDNA *idna;
8182fb4f839SGregory Neil Shapiro static char buf[MAXNAME_I]; /* XXX ??? */
8192fb4f839SGregory Neil Shapiro
820*d39bd2c1SGregory Neil Shapiro if (str_is_print(hostname))
8212fb4f839SGregory Neil Shapiro return hostname;
8222fb4f839SGregory Neil Shapiro idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error);
8232fb4f839SGregory Neil Shapiro (void) uidna_nameToASCII_UTF8(idna, hostname, strlen(hostname),
8242fb4f839SGregory Neil Shapiro buf, sizeof(buf) - 1,
8252fb4f839SGregory Neil Shapiro &info, &error);
8262fb4f839SGregory Neil Shapiro uidna_close(idna);
8272fb4f839SGregory Neil Shapiro return buf;
8282fb4f839SGregory Neil Shapiro }
8292fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
8302fb4f839SGregory Neil Shapiro
83140266059SGregory Neil Shapiro /*
832c2aa98e2SPeter Wemm ** GETMXRR -- get MX resource records for a domain
833c2aa98e2SPeter Wemm **
834c2aa98e2SPeter Wemm ** Parameters:
8352fb4f839SGregory Neil Shapiro ** host -- the name of the host to MX [must be x]
836c2aa98e2SPeter Wemm ** mxhosts -- a pointer to a return buffer of MX records.
83706f25ae9SGregory Neil Shapiro ** mxprefs -- a pointer to a return buffer of MX preferences.
83806f25ae9SGregory Neil Shapiro ** If NULL, don't try to populate.
8395b0945b5SGregory Neil Shapiro ** flags -- flags:
840*d39bd2c1SGregory Neil Shapiro ** DROPLOCALHOST -- If true, all MX records less preferred
841c2aa98e2SPeter Wemm ** than the local host (as determined by $=w) will
842c2aa98e2SPeter Wemm ** be discarded.
8435b0945b5SGregory Neil Shapiro ** TRYFALLBACK -- add also fallback MX host?
8445b0945b5SGregory Neil Shapiro ** ISAD -- host lookup was secure?
845c2aa98e2SPeter Wemm ** rcode -- a pointer to an EX_ status code.
84640266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL).
847*d39bd2c1SGregory Neil Shapiro ** port -- port number used in TLSA queries (_PORT._tcp.)
848*d39bd2c1SGregory Neil Shapiro ** pad -- (output parameter, pointer to) AD flag (can be NULL)
849c2aa98e2SPeter Wemm **
850c2aa98e2SPeter Wemm ** Returns:
851c2aa98e2SPeter Wemm ** The number of MX records found.
852c2aa98e2SPeter Wemm ** -1 if there is an internal failure.
853c2aa98e2SPeter Wemm ** If no MX records are found, mxhosts[0] is set to host
854c2aa98e2SPeter Wemm ** and 1 is returned.
85540266059SGregory Neil Shapiro **
85640266059SGregory Neil Shapiro ** Side Effects:
85740266059SGregory Neil Shapiro ** The entries made for mxhosts point to a static array
85840266059SGregory Neil Shapiro ** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
85940266059SGregory Neil Shapiro ** if it must be preserved across calls to this function.
860c2aa98e2SPeter Wemm */
861c2aa98e2SPeter Wemm
862c2aa98e2SPeter Wemm int
getmxrr(host,mxhosts,mxprefs,flags,rcode,pttl,port,pad)863*d39bd2c1SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port, pad)
864c2aa98e2SPeter Wemm char *host;
865c2aa98e2SPeter Wemm char **mxhosts;
86640266059SGregory Neil Shapiro unsigned short *mxprefs;
8675b0945b5SGregory Neil Shapiro unsigned int flags;
868c2aa98e2SPeter Wemm int *rcode;
86940266059SGregory Neil Shapiro int *pttl;
8705b0945b5SGregory Neil Shapiro int port;
871*d39bd2c1SGregory Neil Shapiro int *pad;
872c2aa98e2SPeter Wemm {
87340266059SGregory Neil Shapiro register unsigned char *eom, *cp;
874c2aa98e2SPeter Wemm register int i, j, n;
875c2aa98e2SPeter Wemm int nmx = 0;
876c2aa98e2SPeter Wemm register char *bp;
877c2aa98e2SPeter Wemm HEADER *hp;
878c2aa98e2SPeter Wemm querybuf answer;
879c2aa98e2SPeter Wemm int ancount, qdcount, buflen;
88040266059SGregory Neil Shapiro bool seenlocal = false;
88140266059SGregory Neil Shapiro unsigned short pref, type;
88240266059SGregory Neil Shapiro unsigned short localpref = 256;
883e92d3f3fSGregory Neil Shapiro char *fallbackMX = FallbackMX;
88440266059SGregory Neil Shapiro bool trycanon = false;
88540266059SGregory Neil Shapiro unsigned short *prefs;
886b6bacd31SGregory Neil Shapiro int (*resfunc) __P((const char *, int, int, u_char *, int));
88740266059SGregory Neil Shapiro unsigned short prefer[MAXMXHOSTS];
888c2aa98e2SPeter Wemm int weight[MAXMXHOSTS];
88940266059SGregory Neil Shapiro int ttl = 0;
8905b0945b5SGregory Neil Shapiro bool ad;
8915b0945b5SGregory Neil Shapiro bool seennullmx = false;
8922fb4f839SGregory Neil Shapiro extern int res_query __P((const char *, int, int, u_char *, int));
8932fb4f839SGregory Neil Shapiro extern int res_search __P((const char *, int, int , u_char *, int));
8945b0945b5SGregory Neil Shapiro # if DANE
8955b0945b5SGregory Neil Shapiro bool cname2mx;
8962fb4f839SGregory Neil Shapiro char qname[MAXNAME]; /* EAI: copy of host: ok? */
8975b0945b5SGregory Neil Shapiro unsigned long old_options = 0;
8985b0945b5SGregory Neil Shapiro # endif
899c2aa98e2SPeter Wemm
900c2aa98e2SPeter Wemm if (tTd(8, 2))
9015b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr(%s, droplocalhost=%d, flags=%X, port=%d)\n",
9025b0945b5SGregory Neil Shapiro host, (flags & DROPLOCALHOST) != 0, flags, port);
9035b0945b5SGregory Neil Shapiro ad = (flags & ISAD) != 0;
90413d88268SGregory Neil Shapiro *rcode = EX_OK;
90513d88268SGregory Neil Shapiro if (pttl != NULL)
90613d88268SGregory Neil Shapiro *pttl = SM_DEFAULT_TTL;
907a7ec597cSGregory Neil Shapiro if (*host == '\0')
908a7ec597cSGregory Neil Shapiro return 0;
9095b0945b5SGregory Neil Shapiro # if DANE
9105b0945b5SGregory Neil Shapiro cname2mx = false;
9115b0945b5SGregory Neil Shapiro qname[0] = '\0';
9125b0945b5SGregory Neil Shapiro old_options = _res.options;
9135b0945b5SGregory Neil Shapiro if (ad)
9145b0945b5SGregory Neil Shapiro _res.options |= SM_RES_DNSSEC;
9155b0945b5SGregory Neil Shapiro # endif
916c2aa98e2SPeter Wemm
9175b0945b5SGregory Neil Shapiro if ((fallbackMX != NULL && (flags & DROPLOCALHOST) != 0 &&
9185b0945b5SGregory Neil Shapiro wordinclass(fallbackMX, 'w')) || (flags & TRYFALLBACK) == 0)
919c2aa98e2SPeter Wemm {
920c2aa98e2SPeter Wemm /* don't use fallback for this pass */
921c2aa98e2SPeter Wemm fallbackMX = NULL;
922c2aa98e2SPeter Wemm }
923c2aa98e2SPeter Wemm
92406f25ae9SGregory Neil Shapiro if (mxprefs != NULL)
92506f25ae9SGregory Neil Shapiro prefs = mxprefs;
92606f25ae9SGregory Neil Shapiro else
92706f25ae9SGregory Neil Shapiro prefs = prefer;
92806f25ae9SGregory Neil Shapiro
929c2aa98e2SPeter Wemm /* efficiency hack -- numeric or non-MX lookups */
930c2aa98e2SPeter Wemm if (host[0] == '[')
931c2aa98e2SPeter Wemm goto punt;
932c2aa98e2SPeter Wemm
9335b0945b5SGregory Neil Shapiro # if DANE
9345b0945b5SGregory Neil Shapiro /*
9355b0945b5SGregory Neil Shapiro ** NOTE: This only works if nocanonify is used,
9365b0945b5SGregory Neil Shapiro ** otherwise the name is already rewritten.
9375b0945b5SGregory Neil Shapiro */
9385b0945b5SGregory Neil Shapiro
9395b0945b5SGregory Neil Shapiro /* always or only when "needed"? */
9405b0945b5SGregory Neil Shapiro if (DANE_ALWAYS == Dane || (ad && DANE_SECURE == Dane))
9415b0945b5SGregory Neil Shapiro (void) sm_strlcpy(qname, host, sizeof(qname));
9425b0945b5SGregory Neil Shapiro # endif /* DANE */
9435b0945b5SGregory Neil Shapiro
9442fb4f839SGregory Neil Shapiro # if USE_EAI
945*d39bd2c1SGregory Neil Shapiro if (!str_is_print(host))
9465b0945b5SGregory Neil Shapiro {
9472fb4f839SGregory Neil Shapiro /* XXX memory leak? */
9482fb4f839SGregory Neil Shapiro host = sm_rpool_strdup_x(CurEnv->e_rpool, hn2alabel(host));
9495b0945b5SGregory Neil Shapiro }
9502fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
9515b0945b5SGregory Neil Shapiro
952c2aa98e2SPeter Wemm /*
953c2aa98e2SPeter Wemm ** If we don't have MX records in our host switch, don't
954c2aa98e2SPeter Wemm ** try for MX records. Note that this really isn't "right",
955c2aa98e2SPeter Wemm ** since we might be set up to try NIS first and then DNS;
956c2aa98e2SPeter Wemm ** if the host is found in NIS we really shouldn't be doing
957c2aa98e2SPeter Wemm ** MX lookups. However, that should be a degenerate case.
958c2aa98e2SPeter Wemm */
959c2aa98e2SPeter Wemm
960c2aa98e2SPeter Wemm if (!UseNameServer)
961c2aa98e2SPeter Wemm goto punt;
962c2aa98e2SPeter Wemm if (HasWildcardMX && ConfigLevel >= 6)
963c2aa98e2SPeter Wemm resfunc = res_query;
964c2aa98e2SPeter Wemm else
965c2aa98e2SPeter Wemm resfunc = res_search;
9665b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
9675b0945b5SGregory Neil Shapiro if (tTd(8, 110))
9685b0945b5SGregory Neil Shapiro resfunc = tstdns_search;
9695b0945b5SGregory Neil Shapiro # endif
970c2aa98e2SPeter Wemm
971c2aa98e2SPeter Wemm errno = 0;
9725b0945b5SGregory Neil Shapiro hp = (HEADER *)&answer;
97340266059SGregory Neil Shapiro n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
97440266059SGregory Neil Shapiro sizeof(answer));
975c2aa98e2SPeter Wemm if (n < 0)
976c2aa98e2SPeter Wemm {
977c2aa98e2SPeter Wemm if (tTd(8, 1))
9785b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
9795b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed (errno=%d (%s), h_errno=%d (%s))\n",
9805b0945b5SGregory Neil Shapiro host, errno, strerror(errno),
9815b0945b5SGregory Neil Shapiro h_errno, herrno2txt(h_errno));
9825b0945b5SGregory Neil Shapiro # else
9835b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: res_search(%s) failed, h_errno=%d\n",
9845b0945b5SGregory Neil Shapiro host, h_errno);
9855b0945b5SGregory Neil Shapiro # endif
986c2aa98e2SPeter Wemm switch (h_errno)
987c2aa98e2SPeter Wemm {
988c2aa98e2SPeter Wemm case NO_DATA:
98940266059SGregory Neil Shapiro trycanon = true;
99006f25ae9SGregory Neil Shapiro /* FALLTHROUGH */
991c2aa98e2SPeter Wemm
992c2aa98e2SPeter Wemm case NO_RECOVERY:
993c2aa98e2SPeter Wemm /* no MX data on this host */
994c2aa98e2SPeter Wemm goto punt;
995c2aa98e2SPeter Wemm
996c2aa98e2SPeter Wemm case HOST_NOT_FOUND:
997c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH
9985b0945b5SGregory Neil Shapiro case 0: /* Ultrix resolver returns failure w/ h_errno=0 */
9995b0945b5SGregory Neil Shapiro # endif
1000c2aa98e2SPeter Wemm /* host doesn't exist in DNS; might be in /etc/hosts */
100140266059SGregory Neil Shapiro trycanon = true;
1002c2aa98e2SPeter Wemm *rcode = EX_NOHOST;
1003c2aa98e2SPeter Wemm goto punt;
1004c2aa98e2SPeter Wemm
1005c2aa98e2SPeter Wemm case TRY_AGAIN:
1006c2aa98e2SPeter Wemm case -1:
1007c2aa98e2SPeter Wemm /* couldn't connect to the name server */
1008c2aa98e2SPeter Wemm if (fallbackMX != NULL)
1009c2aa98e2SPeter Wemm {
1010c2aa98e2SPeter Wemm /* name server is hosed -- push to fallback */
10115b0945b5SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts);
10125b0945b5SGregory Neil Shapiro goto done;
1013c2aa98e2SPeter Wemm }
1014c2aa98e2SPeter Wemm /* it might come up later; better queue it up */
1015c2aa98e2SPeter Wemm *rcode = EX_TEMPFAIL;
1016c2aa98e2SPeter Wemm break;
1017c2aa98e2SPeter Wemm
1018c2aa98e2SPeter Wemm default:
101940266059SGregory Neil Shapiro syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
1020c2aa98e2SPeter Wemm host, h_errno);
1021c2aa98e2SPeter Wemm *rcode = EX_OSERR;
1022c2aa98e2SPeter Wemm break;
1023c2aa98e2SPeter Wemm }
1024c2aa98e2SPeter Wemm
1025c2aa98e2SPeter Wemm /* irreconcilable differences */
10265b0945b5SGregory Neil Shapiro goto error;
1027c2aa98e2SPeter Wemm }
1028c2aa98e2SPeter Wemm
10295b0945b5SGregory Neil Shapiro ad = ad && hp->ad;
10305b0945b5SGregory Neil Shapiro if (tTd(8, 2))
10315b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr(%s), hp=%p, ad=%d\n", host, (void*)hp, ad);
1032*d39bd2c1SGregory Neil Shapiro if (pad != NULL)
1033*d39bd2c1SGregory Neil Shapiro *pad = ad;
10345b0945b5SGregory Neil Shapiro
1035c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */
1036c2aa98e2SPeter Wemm if (n > sizeof(answer))
1037c2aa98e2SPeter Wemm n = sizeof(answer);
1038c2aa98e2SPeter Wemm
1039c2aa98e2SPeter Wemm /* find first satisfactory answer */
104040266059SGregory Neil Shapiro cp = (unsigned char *)&answer + HFIXEDSZ;
104140266059SGregory Neil Shapiro eom = (unsigned char *)&answer + n;
10425b0945b5SGregory Neil Shapiro
104340266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount);
104406f25ae9SGregory Neil Shapiro qdcount--;
104506f25ae9SGregory Neil Shapiro cp += n + QFIXEDSZ)
104606f25ae9SGregory Neil Shapiro {
1047c2aa98e2SPeter Wemm if ((n = dn_skipname(cp, eom)) < 0)
1048c2aa98e2SPeter Wemm goto punt;
104906f25ae9SGregory Neil Shapiro }
105040266059SGregory Neil Shapiro
105140266059SGregory Neil Shapiro /* NOTE: see definition of MXHostBuf! */
1052c2aa98e2SPeter Wemm buflen = sizeof(MXHostBuf) - 1;
105340266059SGregory Neil Shapiro SM_ASSERT(buflen > 0);
1054c2aa98e2SPeter Wemm bp = MXHostBuf;
105540266059SGregory Neil Shapiro ancount = ntohs((unsigned short) hp->ancount);
105640266059SGregory Neil Shapiro
105740266059SGregory Neil Shapiro /* See RFC 1035 for layout of RRs. */
1058e92d3f3fSGregory Neil Shapiro /* XXX leave room for FallbackMX ? */
1059c2aa98e2SPeter Wemm while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
1060c2aa98e2SPeter Wemm {
106140266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp,
106240266059SGregory Neil Shapiro (RES_UNC_T) bp, buflen)) < 0)
1063c2aa98e2SPeter Wemm break;
1064c2aa98e2SPeter Wemm cp += n;
1065c2aa98e2SPeter Wemm GETSHORT(type, cp);
106640266059SGregory Neil Shapiro cp += INT16SZ; /* skip over class */
106740266059SGregory Neil Shapiro GETLONG(ttl, cp);
106840266059SGregory Neil Shapiro GETSHORT(n, cp); /* rdlength */
10695b0945b5SGregory Neil Shapiro # if DANE
10705b0945b5SGregory Neil Shapiro if (type == T_CNAME)
10715b0945b5SGregory Neil Shapiro cname2mx = true;
10725b0945b5SGregory Neil Shapiro # endif
1073c2aa98e2SPeter Wemm if (type != T_MX)
1074c2aa98e2SPeter Wemm {
10755b0945b5SGregory Neil Shapiro if ((tTd(8, 8) || _res.options & RES_DEBUG)
10765b0945b5SGregory Neil Shapiro # if DANE
10775b0945b5SGregory Neil Shapiro && type != T_RRSIG
10785b0945b5SGregory Neil Shapiro # endif
10795b0945b5SGregory Neil Shapiro )
10805b0945b5SGregory Neil Shapiro sm_dprintf("unexpected answer type %s, size %d\n",
10815b0945b5SGregory Neil Shapiro dns_type_to_string(type), n);
1082c2aa98e2SPeter Wemm cp += n;
1083c2aa98e2SPeter Wemm continue;
1084c2aa98e2SPeter Wemm }
1085c2aa98e2SPeter Wemm GETSHORT(pref, cp);
108640266059SGregory Neil Shapiro if ((n = dn_expand((unsigned char *)&answer, eom, cp,
1087c2aa98e2SPeter Wemm (RES_UNC_T) bp, buflen)) < 0)
1088c2aa98e2SPeter Wemm break;
1089c2aa98e2SPeter Wemm cp += n;
109040266059SGregory Neil Shapiro n = strlen(bp);
10915b0945b5SGregory Neil Shapiro
10925b0945b5SGregory Neil Shapiro /* Support for RFC7505 "MX 0 ." */
10935b0945b5SGregory Neil Shapiro if (pref == 0 && *bp == '\0')
10945b0945b5SGregory Neil Shapiro seennullmx = true;
10955b0945b5SGregory Neil Shapiro
1096c2aa98e2SPeter Wemm if (wordinclass(bp, 'w'))
1097c2aa98e2SPeter Wemm {
1098c2aa98e2SPeter Wemm if (tTd(8, 3))
109940266059SGregory Neil Shapiro sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
1100c2aa98e2SPeter Wemm bp, pref);
11015b0945b5SGregory Neil Shapiro if ((flags & DROPLOCALHOST) != 0)
1102c2aa98e2SPeter Wemm {
1103c2aa98e2SPeter Wemm if (!seenlocal || pref < localpref)
1104c2aa98e2SPeter Wemm localpref = pref;
110540266059SGregory Neil Shapiro seenlocal = true;
1106c2aa98e2SPeter Wemm continue;
1107c2aa98e2SPeter Wemm }
1108c2aa98e2SPeter Wemm weight[nmx] = 0;
1109c2aa98e2SPeter Wemm }
1110c2aa98e2SPeter Wemm else
1111c2aa98e2SPeter Wemm weight[nmx] = mxrand(bp);
111206f25ae9SGregory Neil Shapiro prefs[nmx] = pref;
1113c2aa98e2SPeter Wemm mxhosts[nmx++] = bp;
11145b0945b5SGregory Neil Shapiro # if DANE
11155b0945b5SGregory Neil Shapiro if (CHK_DANE(Dane) && port >= 0)
11165b0945b5SGregory Neil Shapiro {
11175b0945b5SGregory Neil Shapiro int nrr;
11185b0945b5SGregory Neil Shapiro unsigned long flags;
11195b0945b5SGregory Neil Shapiro
1120*d39bd2c1SGregory Neil Shapiro flags = TLSAFLNEW;
1121*d39bd2c1SGregory Neil Shapiro if (pad != NULL && *pad)
1122*d39bd2c1SGregory Neil Shapiro flags |= TLSAFLADMX;
1123*d39bd2c1SGregory Neil Shapiro if (tTd(8, 20))
1124*d39bd2c1SGregory Neil Shapiro sm_dprintf("getmxrr: 1: host=%s, mx=%s, flags=%#lx\n", host, bp, flags);
11255b0945b5SGregory Neil Shapiro nrr = gettlsa(bp, NULL, NULL, flags, ttl, port);
11265b0945b5SGregory Neil Shapiro
11275b0945b5SGregory Neil Shapiro /* Only check qname if no TLSA RRs were found */
11285b0945b5SGregory Neil Shapiro if (0 == nrr && cname2mx && '\0' != qname[0] &&
11295b0945b5SGregory Neil Shapiro strcmp(qname, bp))
1130*d39bd2c1SGregory Neil Shapiro {
1131*d39bd2c1SGregory Neil Shapiro if (tTd(8, 20))
1132*d39bd2c1SGregory Neil Shapiro sm_dprintf("getmxrr: 2: host=%s, qname=%s, flags=%#lx\n", host, qname, flags);
11335b0945b5SGregory Neil Shapiro gettlsa(qname, bp, NULL, flags, ttl, port);
11345b0945b5SGregory Neil Shapiro /* XXX is this the right ad flag? */
11355b0945b5SGregory Neil Shapiro }
1136*d39bd2c1SGregory Neil Shapiro }
11375b0945b5SGregory Neil Shapiro # endif
11385b0945b5SGregory Neil Shapiro
11395b0945b5SGregory Neil Shapiro /*
11405b0945b5SGregory Neil Shapiro ** Note: n can be 0 for something like:
11415b0945b5SGregory Neil Shapiro ** host MX 0 .
11425b0945b5SGregory Neil Shapiro ** See RFC 7505
11435b0945b5SGregory Neil Shapiro */
11445b0945b5SGregory Neil Shapiro
1145c2aa98e2SPeter Wemm bp += n;
11465b0945b5SGregory Neil Shapiro if (0 == n || bp[-1] != '.')
1147c2aa98e2SPeter Wemm {
1148c2aa98e2SPeter Wemm *bp++ = '.';
1149c2aa98e2SPeter Wemm n++;
1150c2aa98e2SPeter Wemm }
1151c2aa98e2SPeter Wemm *bp++ = '\0';
115240266059SGregory Neil Shapiro if (buflen < n + 1)
115340266059SGregory Neil Shapiro {
115440266059SGregory Neil Shapiro /* don't want to wrap buflen */
115540266059SGregory Neil Shapiro break;
115640266059SGregory Neil Shapiro }
1157c2aa98e2SPeter Wemm buflen -= n + 1;
1158c2aa98e2SPeter Wemm }
1159c2aa98e2SPeter Wemm
11605b0945b5SGregory Neil Shapiro /* Support for RFC7505 "MX 0 ." */
11615b0945b5SGregory Neil Shapiro if (seennullmx && nmx == 1)
11625b0945b5SGregory Neil Shapiro {
11635b0945b5SGregory Neil Shapiro if (tTd(8, 4))
11645b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr: Null MX record found, domain doesn't accept mail (RFC7505)\n");
11655b0945b5SGregory Neil Shapiro *rcode = EX_UNAVAILABLE;
11665b0945b5SGregory Neil Shapiro return NULLMX;
11675b0945b5SGregory Neil Shapiro }
11685b0945b5SGregory Neil Shapiro
116940266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */
117040266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL)
117140266059SGregory Neil Shapiro *pttl = ttl;
117240266059SGregory Neil Shapiro
1173c2aa98e2SPeter Wemm /* sort the records */
1174c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++)
1175c2aa98e2SPeter Wemm {
1176c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++)
1177c2aa98e2SPeter Wemm {
117806f25ae9SGregory Neil Shapiro if (prefs[i] > prefs[j] ||
117906f25ae9SGregory Neil Shapiro (prefs[i] == prefs[j] && weight[i] > weight[j]))
1180c2aa98e2SPeter Wemm {
1181c2aa98e2SPeter Wemm register int temp;
1182c2aa98e2SPeter Wemm register char *temp1;
1183c2aa98e2SPeter Wemm
118406f25ae9SGregory Neil Shapiro temp = prefs[i];
118506f25ae9SGregory Neil Shapiro prefs[i] = prefs[j];
118606f25ae9SGregory Neil Shapiro prefs[j] = temp;
1187c2aa98e2SPeter Wemm temp1 = mxhosts[i];
1188c2aa98e2SPeter Wemm mxhosts[i] = mxhosts[j];
1189c2aa98e2SPeter Wemm mxhosts[j] = temp1;
1190c2aa98e2SPeter Wemm temp = weight[i];
1191c2aa98e2SPeter Wemm weight[i] = weight[j];
1192c2aa98e2SPeter Wemm weight[j] = temp;
1193c2aa98e2SPeter Wemm }
1194c2aa98e2SPeter Wemm }
119506f25ae9SGregory Neil Shapiro if (seenlocal && prefs[i] >= localpref)
1196c2aa98e2SPeter Wemm {
1197c2aa98e2SPeter Wemm /* truncate higher preference part of list */
1198c2aa98e2SPeter Wemm nmx = i;
1199c2aa98e2SPeter Wemm }
1200c2aa98e2SPeter Wemm }
1201c2aa98e2SPeter Wemm
1202c2aa98e2SPeter Wemm /* delete duplicates from list (yes, some bozos have duplicates) */
1203c2aa98e2SPeter Wemm for (i = 0; i < nmx - 1; )
1204c2aa98e2SPeter Wemm {
12052fb4f839SGregory Neil Shapiro if (!SM_STRCASEEQ(mxhosts[i], mxhosts[i + 1]))
1206c2aa98e2SPeter Wemm i++;
1207c2aa98e2SPeter Wemm else
1208c2aa98e2SPeter Wemm {
1209c2aa98e2SPeter Wemm /* compress out duplicate */
1210c2aa98e2SPeter Wemm for (j = i + 1; j < nmx; j++)
121106f25ae9SGregory Neil Shapiro {
1212c2aa98e2SPeter Wemm mxhosts[j] = mxhosts[j + 1];
121306f25ae9SGregory Neil Shapiro prefs[j] = prefs[j + 1];
121406f25ae9SGregory Neil Shapiro }
1215c2aa98e2SPeter Wemm nmx--;
1216c2aa98e2SPeter Wemm }
1217c2aa98e2SPeter Wemm }
1218c2aa98e2SPeter Wemm
1219c2aa98e2SPeter Wemm if (nmx == 0)
1220c2aa98e2SPeter Wemm {
1221c2aa98e2SPeter Wemm punt:
122206f25ae9SGregory Neil Shapiro if (seenlocal)
1223c2aa98e2SPeter Wemm {
122406f25ae9SGregory Neil Shapiro struct hostent *h = NULL;
122506f25ae9SGregory Neil Shapiro
1226c2aa98e2SPeter Wemm /*
1227c2aa98e2SPeter Wemm ** If we have deleted all MX entries, this is
1228c2aa98e2SPeter Wemm ** an error -- we should NEVER send to a host that
1229c2aa98e2SPeter Wemm ** has an MX, and this should have been caught
1230c2aa98e2SPeter Wemm ** earlier in the config file.
1231c2aa98e2SPeter Wemm **
1232c2aa98e2SPeter Wemm ** Some sites prefer to go ahead and try the
1233c2aa98e2SPeter Wemm ** A record anyway; that case is handled by
1234c2aa98e2SPeter Wemm ** setting TryNullMXList. I believe this is a
1235c2aa98e2SPeter Wemm ** bad idea, but it's up to you....
1236c2aa98e2SPeter Wemm */
1237c2aa98e2SPeter Wemm
123806f25ae9SGregory Neil Shapiro if (TryNullMXList)
123906f25ae9SGregory Neil Shapiro {
1240602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0);
124106f25ae9SGregory Neil Shapiro errno = 0;
124206f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET);
124306f25ae9SGregory Neil Shapiro if (h == NULL)
124406f25ae9SGregory Neil Shapiro {
124506f25ae9SGregory Neil Shapiro if (errno == ETIMEDOUT ||
124606f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN ||
124706f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED &&
124806f25ae9SGregory Neil Shapiro UseNameServer))
124906f25ae9SGregory Neil Shapiro {
125006f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL;
12515b0945b5SGregory Neil Shapiro goto error;
125206f25ae9SGregory Neil Shapiro }
125306f25ae9SGregory Neil Shapiro # if NETINET6
1254602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(0);
125506f25ae9SGregory Neil Shapiro errno = 0;
125606f25ae9SGregory Neil Shapiro h = sm_gethostbyname(host, AF_INET6);
125706f25ae9SGregory Neil Shapiro if (h == NULL &&
125806f25ae9SGregory Neil Shapiro (errno == ETIMEDOUT ||
125906f25ae9SGregory Neil Shapiro h_errno == TRY_AGAIN ||
126006f25ae9SGregory Neil Shapiro (errno == ECONNREFUSED &&
126106f25ae9SGregory Neil Shapiro UseNameServer)))
126206f25ae9SGregory Neil Shapiro {
126306f25ae9SGregory Neil Shapiro *rcode = EX_TEMPFAIL;
12645b0945b5SGregory Neil Shapiro goto error;
126506f25ae9SGregory Neil Shapiro }
126606f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
126706f25ae9SGregory Neil Shapiro }
126806f25ae9SGregory Neil Shapiro }
126906f25ae9SGregory Neil Shapiro
127006f25ae9SGregory Neil Shapiro if (h == NULL)
127106f25ae9SGregory Neil Shapiro {
1272c2aa98e2SPeter Wemm *rcode = EX_CONFIG;
1273c2aa98e2SPeter Wemm syserr("MX list for %s points back to %s",
1274c2aa98e2SPeter Wemm host, MyHostName);
12755b0945b5SGregory Neil Shapiro goto error;
1276c2aa98e2SPeter Wemm }
127740266059SGregory Neil Shapiro # if NETINET6
1278193538b7SGregory Neil Shapiro freehostent(h);
1279af9557fdSGregory Neil Shapiro h = NULL;
12805b0945b5SGregory Neil Shapiro # endif
128106f25ae9SGregory Neil Shapiro }
1282d0cef73dSGregory Neil Shapiro if (strlen(host) >= sizeof(MXHostBuf))
1283c2aa98e2SPeter Wemm {
1284c2aa98e2SPeter Wemm *rcode = EX_CONFIG;
1285c2aa98e2SPeter Wemm syserr("Host name %s too long",
1286c2aa98e2SPeter Wemm shortenstring(host, MAXSHORTSTR));
12875b0945b5SGregory Neil Shapiro goto error;
1288c2aa98e2SPeter Wemm }
1289d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf));
1290c2aa98e2SPeter Wemm mxhosts[0] = MXHostBuf;
129106f25ae9SGregory Neil Shapiro prefs[0] = 0;
1292c2aa98e2SPeter Wemm if (host[0] == '[')
1293c2aa98e2SPeter Wemm {
1294c2aa98e2SPeter Wemm register char *p;
129506f25ae9SGregory Neil Shapiro # if NETINET6
129606f25ae9SGregory Neil Shapiro struct sockaddr_in6 tmp6;
12975b0945b5SGregory Neil Shapiro # endif
1298c2aa98e2SPeter Wemm
1299c2aa98e2SPeter Wemm /* this may be an MX suppression-style address */
1300c2aa98e2SPeter Wemm p = strchr(MXHostBuf, ']');
1301c2aa98e2SPeter Wemm if (p != NULL)
1302c2aa98e2SPeter Wemm {
1303c2aa98e2SPeter Wemm *p = '\0';
130406f25ae9SGregory Neil Shapiro
1305c2aa98e2SPeter Wemm if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
1306c2aa98e2SPeter Wemm {
1307c2aa98e2SPeter Wemm nmx++;
1308c2aa98e2SPeter Wemm *p = ']';
1309c2aa98e2SPeter Wemm }
131006f25ae9SGregory Neil Shapiro # if NETINET6
131140266059SGregory Neil Shapiro else if (anynet_pton(AF_INET6, &MXHostBuf[1],
131206f25ae9SGregory Neil Shapiro &tmp6.sin6_addr) == 1)
131306f25ae9SGregory Neil Shapiro {
131406f25ae9SGregory Neil Shapiro nmx++;
131506f25ae9SGregory Neil Shapiro *p = ']';
131606f25ae9SGregory Neil Shapiro }
131706f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
1318c2aa98e2SPeter Wemm else
1319c2aa98e2SPeter Wemm {
13202fb4f839SGregory Neil Shapiro # if USE_EAI
13212fb4f839SGregory Neil Shapiro char *hn;
13222fb4f839SGregory Neil Shapiro
13232fb4f839SGregory Neil Shapiro hn = MXHostBuf + 1;
1324*d39bd2c1SGregory Neil Shapiro if (!str_is_print(hn))
13252fb4f839SGregory Neil Shapiro {
13262fb4f839SGregory Neil Shapiro const char *ahn;
13272fb4f839SGregory Neil Shapiro
13282fb4f839SGregory Neil Shapiro ahn = hn2alabel(hn);
13292fb4f839SGregory Neil Shapiro if (strlen(ahn) >= sizeof(MXHostBuf) - 1)
13302fb4f839SGregory Neil Shapiro {
13312fb4f839SGregory Neil Shapiro *rcode = EX_CONFIG;
13322fb4f839SGregory Neil Shapiro syserr("Encoded host name %s too long",
13332fb4f839SGregory Neil Shapiro shortenstring(ahn, MAXSHORTSTR));
13342fb4f839SGregory Neil Shapiro goto error;
13352fb4f839SGregory Neil Shapiro }
13362fb4f839SGregory Neil Shapiro (void) sm_strlcpy(hn, ahn, sizeof(MXHostBuf) - 1);
13372fb4f839SGregory Neil Shapiro }
13382fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
133940266059SGregory Neil Shapiro trycanon = true;
1340c2aa98e2SPeter Wemm mxhosts[0]++;
1341c2aa98e2SPeter Wemm }
1342c2aa98e2SPeter Wemm }
1343c2aa98e2SPeter Wemm }
1344c2aa98e2SPeter Wemm if (trycanon &&
13455b0945b5SGregory Neil Shapiro (n = getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false,
13465b0945b5SGregory Neil Shapiro pttl)) != HOST_NOTFOUND)
1347c2aa98e2SPeter Wemm {
134840266059SGregory Neil Shapiro /* XXX MXHostBuf == "" ? is that possible? */
1349c2aa98e2SPeter Wemm bp = &MXHostBuf[strlen(MXHostBuf)];
1350c2aa98e2SPeter Wemm if (bp[-1] != '.')
1351c2aa98e2SPeter Wemm {
1352c2aa98e2SPeter Wemm *bp++ = '.';
1353c2aa98e2SPeter Wemm *bp = '\0';
1354c2aa98e2SPeter Wemm }
1355c2aa98e2SPeter Wemm nmx = 1;
13565b0945b5SGregory Neil Shapiro # if DANE
13575b0945b5SGregory Neil Shapiro if (tTd(8, 3))
13585b0945b5SGregory Neil Shapiro sm_dprintf("getmxrr=%s, getcanonname=%d\n",
13595b0945b5SGregory Neil Shapiro mxhosts[0], n);
13605b0945b5SGregory Neil Shapiro if (CHK_DANE(Dane) && port >= 0)
13615b0945b5SGregory Neil Shapiro {
13625b0945b5SGregory Neil Shapiro int nrr;
13635b0945b5SGregory Neil Shapiro unsigned long flags;
13645b0945b5SGregory Neil Shapiro unsigned int cttl;
13655b0945b5SGregory Neil Shapiro
13665b0945b5SGregory Neil Shapiro if (pttl != NULL)
13675b0945b5SGregory Neil Shapiro cttl = *pttl;
13685b0945b5SGregory Neil Shapiro else if (ttl > 0)
13695b0945b5SGregory Neil Shapiro cttl = ttl;
13705b0945b5SGregory Neil Shapiro else
13715b0945b5SGregory Neil Shapiro cttl = SM_DEFAULT_TTL;
13725b0945b5SGregory Neil Shapiro
1373*d39bd2c1SGregory Neil Shapiro flags = TLSAFLNEW;
1374*d39bd2c1SGregory Neil Shapiro if (ad && HOST_SECURE == n)
1375*d39bd2c1SGregory Neil Shapiro {
1376*d39bd2c1SGregory Neil Shapiro flags |= TLSAFLADMX;
1377*d39bd2c1SGregory Neil Shapiro if (pad != NULL)
1378*d39bd2c1SGregory Neil Shapiro *pad = ad;
1379*d39bd2c1SGregory Neil Shapiro }
1380*d39bd2c1SGregory Neil Shapiro if (TTD(8, 20))
1381*d39bd2c1SGregory Neil Shapiro sm_dprintf("getmxrr: 3: host=%s, mx=%s, flags=%#lx, ad=%d\n",
1382*d39bd2c1SGregory Neil Shapiro host, mxhosts[0], flags, ad);
13835b0945b5SGregory Neil Shapiro nrr = gettlsa(mxhosts[0], NULL, NULL, flags,
13845b0945b5SGregory Neil Shapiro cttl, port);
13855b0945b5SGregory Neil Shapiro
13865b0945b5SGregory Neil Shapiro /*
13875b0945b5SGregory Neil Shapiro ** Only check qname if no TLSA RRs were found
13885b0945b5SGregory Neil Shapiro ** XXX: what about (temp) DNS errors?
13895b0945b5SGregory Neil Shapiro */
13905b0945b5SGregory Neil Shapiro
13915b0945b5SGregory Neil Shapiro if (0 == nrr && '\0' != qname[0] &&
13925b0945b5SGregory Neil Shapiro strcmp(qname, mxhosts[0]))
1393*d39bd2c1SGregory Neil Shapiro {
13945b0945b5SGregory Neil Shapiro gettlsa(qname, mxhosts[0], NULL, flags,
13955b0945b5SGregory Neil Shapiro cttl, port);
1396*d39bd2c1SGregory Neil Shapiro if (tTd(8, 20))
1397*d39bd2c1SGregory Neil Shapiro sm_dprintf("getmxrr: 4: host=%s, qname=%s, flags=%#lx\n", host, qname, flags);
13985b0945b5SGregory Neil Shapiro /* XXX is this the right ad flag? */
13995b0945b5SGregory Neil Shapiro }
1400*d39bd2c1SGregory Neil Shapiro }
14015b0945b5SGregory Neil Shapiro # endif
1402c2aa98e2SPeter Wemm }
1403c2aa98e2SPeter Wemm }
1404c2aa98e2SPeter Wemm
1405c2aa98e2SPeter Wemm /* if we have a default lowest preference, include that */
1406c2aa98e2SPeter Wemm if (fallbackMX != NULL && !seenlocal)
140706f25ae9SGregory Neil Shapiro {
1408*d39bd2c1SGregory Neil Shapiro /* TODO: DNSSEC status of fallbacks */
140940266059SGregory Neil Shapiro nmx = fallbackmxrr(nmx, prefs, mxhosts);
141006f25ae9SGregory Neil Shapiro }
14115b0945b5SGregory Neil Shapiro done:
14125b0945b5SGregory Neil Shapiro # if DANE
14135b0945b5SGregory Neil Shapiro _res.options = old_options;
14145b0945b5SGregory Neil Shapiro # endif
141506f25ae9SGregory Neil Shapiro return nmx;
14165b0945b5SGregory Neil Shapiro
14175b0945b5SGregory Neil Shapiro error:
14185b0945b5SGregory Neil Shapiro # if DANE
14195b0945b5SGregory Neil Shapiro _res.options = old_options;
14205b0945b5SGregory Neil Shapiro # endif
14215b0945b5SGregory Neil Shapiro return -1;
1422c2aa98e2SPeter Wemm }
14235b0945b5SGregory Neil Shapiro
142440266059SGregory Neil Shapiro /*
1425c2aa98e2SPeter Wemm ** MXRAND -- create a randomizer for equal MX preferences
1426c2aa98e2SPeter Wemm **
1427c2aa98e2SPeter Wemm ** If two MX hosts have equal preferences we want to randomize
1428c2aa98e2SPeter Wemm ** the selection. But in order for signatures to be the same,
1429c2aa98e2SPeter Wemm ** we need to randomize the same way each time. This function
1430c2aa98e2SPeter Wemm ** computes a pseudo-random hash function from the host name.
1431c2aa98e2SPeter Wemm **
1432c2aa98e2SPeter Wemm ** Parameters:
1433c2aa98e2SPeter Wemm ** host -- the name of the host.
1434c2aa98e2SPeter Wemm **
1435c2aa98e2SPeter Wemm ** Returns:
1436c2aa98e2SPeter Wemm ** A random but repeatable value based on the host name.
1437c2aa98e2SPeter Wemm */
1438c2aa98e2SPeter Wemm
143906f25ae9SGregory Neil Shapiro static int
mxrand(host)1440c2aa98e2SPeter Wemm mxrand(host)
1441c2aa98e2SPeter Wemm register char *host;
1442c2aa98e2SPeter Wemm {
1443c2aa98e2SPeter Wemm int hfunc;
1444c2aa98e2SPeter Wemm static unsigned int seed;
1445c2aa98e2SPeter Wemm
1446c2aa98e2SPeter Wemm if (seed == 0)
1447c2aa98e2SPeter Wemm {
1448c2aa98e2SPeter Wemm seed = (int) curtime() & 0xffff;
1449c2aa98e2SPeter Wemm if (seed == 0)
1450c2aa98e2SPeter Wemm seed++;
1451c2aa98e2SPeter Wemm }
1452c2aa98e2SPeter Wemm
1453c2aa98e2SPeter Wemm if (tTd(17, 9))
145440266059SGregory Neil Shapiro sm_dprintf("mxrand(%s)", host);
1455c2aa98e2SPeter Wemm
1456c2aa98e2SPeter Wemm hfunc = seed;
1457c2aa98e2SPeter Wemm while (*host != '\0')
1458c2aa98e2SPeter Wemm {
1459c2aa98e2SPeter Wemm int c = *host++;
1460c2aa98e2SPeter Wemm
1461c2aa98e2SPeter Wemm if (isascii(c) && isupper(c))
1462c2aa98e2SPeter Wemm c = tolower(c);
1463c2aa98e2SPeter Wemm hfunc = ((hfunc << 1) ^ c) % 2003;
1464c2aa98e2SPeter Wemm }
1465c2aa98e2SPeter Wemm
1466c2aa98e2SPeter Wemm hfunc &= 0xff;
1467c2aa98e2SPeter Wemm hfunc++;
1468c2aa98e2SPeter Wemm
1469c2aa98e2SPeter Wemm if (tTd(17, 9))
147040266059SGregory Neil Shapiro sm_dprintf(" = %d\n", hfunc);
1471c2aa98e2SPeter Wemm return hfunc;
1472c2aa98e2SPeter Wemm }
147340266059SGregory Neil Shapiro /*
1474c2aa98e2SPeter Wemm ** BESTMX -- find the best MX for a name
1475c2aa98e2SPeter Wemm **
1476c2aa98e2SPeter Wemm ** This is really a hack, but I don't see any obvious way
1477c2aa98e2SPeter Wemm ** to generalize it at the moment.
1478c2aa98e2SPeter Wemm */
1479c2aa98e2SPeter Wemm
1480c2aa98e2SPeter Wemm /* ARGSUSED3 */
1481c2aa98e2SPeter Wemm char *
bestmx_map_lookup(map,name,av,statp)1482c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp)
1483c2aa98e2SPeter Wemm MAP *map;
1484c2aa98e2SPeter Wemm char *name;
1485c2aa98e2SPeter Wemm char **av;
1486c2aa98e2SPeter Wemm int *statp;
1487c2aa98e2SPeter Wemm {
1488c2aa98e2SPeter Wemm int nmx;
1489c2aa98e2SPeter Wemm int saveopts = _res.options;
149040266059SGregory Neil Shapiro int i;
149140266059SGregory Neil Shapiro ssize_t len = 0;
149240266059SGregory Neil Shapiro char *result;
1493c2aa98e2SPeter Wemm char *mxhosts[MAXMXHOSTS + 1];
149440266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
149540266059SGregory Neil Shapiro char *buf;
14965b0945b5SGregory Neil Shapiro # else
149740266059SGregory Neil Shapiro char *p;
1498065a643dSPeter Wemm char buf[PSBUFSIZE / 2];
14995b0945b5SGregory Neil Shapiro # endif
1500c2aa98e2SPeter Wemm
1501c2aa98e2SPeter Wemm _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
1502*d39bd2c1SGregory Neil Shapiro nmx = getmxrr(name, mxhosts, NULL, 0, statp, NULL, -1, NULL);
1503c2aa98e2SPeter Wemm _res.options = saveopts;
1504c2aa98e2SPeter Wemm if (nmx <= 0)
1505c2aa98e2SPeter Wemm return NULL;
1506c2aa98e2SPeter Wemm if (bitset(MF_MATCHONLY, map->map_mflags))
1507c2aa98e2SPeter Wemm return map_rewrite(map, name, strlen(name), NULL);
1508c2aa98e2SPeter Wemm if ((map->map_coldelim == '\0') || (nmx == 1))
1509c2aa98e2SPeter Wemm return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
1510c2aa98e2SPeter Wemm
1511c2aa98e2SPeter Wemm /*
1512c2aa98e2SPeter Wemm ** We were given a -z flag (return all MXs) and there are multiple
1513c2aa98e2SPeter Wemm ** ones. We need to build them all into a list.
1514c2aa98e2SPeter Wemm */
151540266059SGregory Neil Shapiro
151640266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
151740266059SGregory Neil Shapiro for (i = 0; i < nmx; i++)
151840266059SGregory Neil Shapiro {
151940266059SGregory Neil Shapiro if (strchr(mxhosts[i], map->map_coldelim) != NULL)
152040266059SGregory Neil Shapiro {
152140266059SGregory Neil Shapiro syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
152240266059SGregory Neil Shapiro mxhosts[i], map->map_coldelim);
152340266059SGregory Neil Shapiro return NULL;
152440266059SGregory Neil Shapiro }
152540266059SGregory Neil Shapiro len += strlen(mxhosts[i]) + 1;
152640266059SGregory Neil Shapiro if (len < 0)
152740266059SGregory Neil Shapiro {
152840266059SGregory Neil Shapiro len -= strlen(mxhosts[i]) + 1;
152940266059SGregory Neil Shapiro break;
153040266059SGregory Neil Shapiro }
153140266059SGregory Neil Shapiro }
153240266059SGregory Neil Shapiro buf = (char *) sm_malloc(len);
153340266059SGregory Neil Shapiro if (buf == NULL)
153440266059SGregory Neil Shapiro {
153540266059SGregory Neil Shapiro *statp = EX_UNAVAILABLE;
153640266059SGregory Neil Shapiro return NULL;
153740266059SGregory Neil Shapiro }
153840266059SGregory Neil Shapiro *buf = '\0';
153940266059SGregory Neil Shapiro for (i = 0; i < nmx; i++)
154040266059SGregory Neil Shapiro {
154140266059SGregory Neil Shapiro int end;
154240266059SGregory Neil Shapiro
154340266059SGregory Neil Shapiro end = sm_strlcat(buf, mxhosts[i], len);
154440266059SGregory Neil Shapiro if (i != nmx && end + 1 < len)
154540266059SGregory Neil Shapiro {
154640266059SGregory Neil Shapiro buf[end] = map->map_coldelim;
154740266059SGregory Neil Shapiro buf[end + 1] = '\0';
154840266059SGregory Neil Shapiro }
154940266059SGregory Neil Shapiro }
155040266059SGregory Neil Shapiro
155140266059SGregory Neil Shapiro /* Cleanly truncate for rulesets */
155240266059SGregory Neil Shapiro truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
155340266059SGregory Neil Shapiro # else /* _FFR_BESTMX_BETTER_TRUNCATION */
1554c2aa98e2SPeter Wemm p = buf;
1555c2aa98e2SPeter Wemm for (i = 0; i < nmx; i++)
1556c2aa98e2SPeter Wemm {
155740266059SGregory Neil Shapiro size_t slen;
1558c2aa98e2SPeter Wemm
1559c2aa98e2SPeter Wemm if (strchr(mxhosts[i], map->map_coldelim) != NULL)
1560c2aa98e2SPeter Wemm {
1561c2aa98e2SPeter Wemm syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
1562c2aa98e2SPeter Wemm mxhosts[i], map->map_coldelim);
1563c2aa98e2SPeter Wemm return NULL;
1564c2aa98e2SPeter Wemm }
1565c2aa98e2SPeter Wemm slen = strlen(mxhosts[i]);
1566d0cef73dSGregory Neil Shapiro if (len + slen + 2 > sizeof(buf))
1567c2aa98e2SPeter Wemm break;
1568c2aa98e2SPeter Wemm if (i > 0)
1569c2aa98e2SPeter Wemm {
1570c2aa98e2SPeter Wemm *p++ = map->map_coldelim;
1571c2aa98e2SPeter Wemm len++;
1572c2aa98e2SPeter Wemm }
1573d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len);
1574c2aa98e2SPeter Wemm p += slen;
1575c2aa98e2SPeter Wemm len += slen;
1576c2aa98e2SPeter Wemm }
157740266059SGregory Neil Shapiro # endif /* _FFR_BESTMX_BETTER_TRUNCATION */
157840266059SGregory Neil Shapiro
157940266059SGregory Neil Shapiro result = map_rewrite(map, buf, len, av);
158040266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
158140266059SGregory Neil Shapiro sm_free(buf);
15825b0945b5SGregory Neil Shapiro # endif
158340266059SGregory Neil Shapiro return result;
1584c2aa98e2SPeter Wemm }
158540266059SGregory Neil Shapiro /*
1586c2aa98e2SPeter Wemm ** DNS_GETCANONNAME -- get the canonical name for named host using DNS
1587c2aa98e2SPeter Wemm **
1588c2aa98e2SPeter Wemm ** This algorithm tries to be smart about wildcard MX records.
1589c2aa98e2SPeter Wemm ** This is hard to do because DNS doesn't tell is if we matched
1590c2aa98e2SPeter Wemm ** against a wildcard or a specific MX.
1591c2aa98e2SPeter Wemm **
1592c2aa98e2SPeter Wemm ** We always prefer A & CNAME records, since these are presumed
1593c2aa98e2SPeter Wemm ** to be specific.
1594c2aa98e2SPeter Wemm **
1595c2aa98e2SPeter Wemm ** If we match an MX in one pass and lose it in the next, we use
1596c2aa98e2SPeter Wemm ** the old one. For example, consider an MX matching *.FOO.BAR.COM.
1597c2aa98e2SPeter Wemm ** A hostname bletch.foo.bar.com will match against this MX, but
1598c2aa98e2SPeter Wemm ** will stop matching when we try bletch.bar.com -- so we know
1599c2aa98e2SPeter Wemm ** that bletch.foo.bar.com must have been right. This fails if
1600c2aa98e2SPeter Wemm ** there was also an MX record matching *.BAR.COM, but there are
1601c2aa98e2SPeter Wemm ** some things that just can't be fixed.
1602c2aa98e2SPeter Wemm **
1603c2aa98e2SPeter Wemm ** Parameters:
1604c2aa98e2SPeter Wemm ** host -- a buffer containing the name of the host.
1605c2aa98e2SPeter Wemm ** This is a value-result parameter.
1606c2aa98e2SPeter Wemm ** hbsize -- the size of the host buffer.
1607c2aa98e2SPeter Wemm ** trymx -- if set, try MX records as well as A and CNAME.
1608c2aa98e2SPeter Wemm ** statp -- pointer to place to store status.
160940266059SGregory Neil Shapiro ** pttl -- pointer to return TTL (can be NULL).
1610c2aa98e2SPeter Wemm **
1611c2aa98e2SPeter Wemm ** Returns:
16125b0945b5SGregory Neil Shapiro ** >0 -- if the host was found.
16135b0945b5SGregory Neil Shapiro ** 0 -- otherwise.
1614c2aa98e2SPeter Wemm */
1615c2aa98e2SPeter Wemm
16165b0945b5SGregory Neil Shapiro int
dns_getcanonname(host,hbsize,trymx,statp,pttl)161740266059SGregory Neil Shapiro dns_getcanonname(host, hbsize, trymx, statp, pttl)
1618c2aa98e2SPeter Wemm char *host;
1619c2aa98e2SPeter Wemm int hbsize;
1620c2aa98e2SPeter Wemm bool trymx;
1621c2aa98e2SPeter Wemm int *statp;
162240266059SGregory Neil Shapiro int *pttl;
1623c2aa98e2SPeter Wemm {
162440266059SGregory Neil Shapiro register unsigned char *eom, *ap;
1625c2aa98e2SPeter Wemm register char *cp;
1626c2aa98e2SPeter Wemm register int n;
1627c2aa98e2SPeter Wemm HEADER *hp;
1628c2aa98e2SPeter Wemm querybuf answer;
16295b0945b5SGregory Neil Shapiro int ancount, qdcount, ret, type, qtype, initial, loopcnt, ttl, sli;
1630c2aa98e2SPeter Wemm char **domain;
16315b0945b5SGregory Neil Shapiro char *dp;
1632c2aa98e2SPeter Wemm char *mxmatch;
16335b0945b5SGregory Neil Shapiro bool amatch, gotmx, ad;
163440266059SGregory Neil Shapiro char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
16355b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
16365b0945b5SGregory Neil Shapiro # define ADDSL 1 /* NameSearchList may add another entry to searchlist! */
16375b0945b5SGregory Neil Shapiro # else
16385b0945b5SGregory Neil Shapiro # define ADDSL 0
16395b0945b5SGregory Neil Shapiro # endif
16405b0945b5SGregory Neil Shapiro char *searchlist[MAXDNSRCH + 2 + ADDSL];
16415b0945b5SGregory Neil Shapiro # define SLSIZE SM_ARRAY_SIZE(searchlist)
16425b0945b5SGregory Neil Shapiro int (*resqdomain) __P((const char *, const char *, int, int, unsigned char *, int));
16435b0945b5SGregory Neil Shapiro # if DANE
16445b0945b5SGregory Neil Shapiro unsigned long old_options = 0;
16455b0945b5SGregory Neil Shapiro # endif
1646c2aa98e2SPeter Wemm
16475b0945b5SGregory Neil Shapiro ttl = 0;
16485b0945b5SGregory Neil Shapiro gotmx = false;
16495b0945b5SGregory Neil Shapiro ad = true;
1650c2aa98e2SPeter Wemm if (tTd(8, 2))
165140266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
1652c2aa98e2SPeter Wemm
1653c2aa98e2SPeter Wemm if ((_res.options & RES_INIT) == 0 && res_init() == -1)
1654c2aa98e2SPeter Wemm {
1655c2aa98e2SPeter Wemm *statp = EX_UNAVAILABLE;
16565b0945b5SGregory Neil Shapiro return HOST_NOTFOUND;
1657c2aa98e2SPeter Wemm }
1658c2aa98e2SPeter Wemm
16595b0945b5SGregory Neil Shapiro # if DANE
16605b0945b5SGregory Neil Shapiro old_options = _res.options;
16615b0945b5SGregory Neil Shapiro if (DANE_SECURE == Dane)
16625b0945b5SGregory Neil Shapiro _res.options |= SM_RES_DNSSEC;
16635b0945b5SGregory Neil Shapiro # endif
16645b0945b5SGregory Neil Shapiro
1665193538b7SGregory Neil Shapiro *statp = EX_OK;
16665b0945b5SGregory Neil Shapiro resqdomain = res_querydomain;
16675b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
16685b0945b5SGregory Neil Shapiro if (tTd(8, 110))
16695b0945b5SGregory Neil Shapiro resqdomain = tstdns_querydomain;
16705b0945b5SGregory Neil Shapiro # endif
1671193538b7SGregory Neil Shapiro
1672c2aa98e2SPeter Wemm /*
1673c2aa98e2SPeter Wemm ** Initialize domain search list. If there is at least one
1674c2aa98e2SPeter Wemm ** dot in the name, search the unmodified name first so we
1675c2aa98e2SPeter Wemm ** find "vse.CS" in Czechoslovakia instead of in the local
167606f25ae9SGregory Neil Shapiro ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no
167706f25ae9SGregory Neil Shapiro ** longer a country named Czechoslovakia but this type of problem
167806f25ae9SGregory Neil Shapiro ** is still present.
1679c2aa98e2SPeter Wemm **
1680c2aa98e2SPeter Wemm ** Older versions of the resolver could create this
1681c2aa98e2SPeter Wemm ** list by tearing apart the host name.
1682c2aa98e2SPeter Wemm */
1683c2aa98e2SPeter Wemm
1684c2aa98e2SPeter Wemm loopcnt = 0;
1685c2aa98e2SPeter Wemm cnameloop:
1686c2aa98e2SPeter Wemm /* Check for dots in the name */
1687c2aa98e2SPeter Wemm for (cp = host, n = 0; *cp != '\0'; cp++)
1688c2aa98e2SPeter Wemm if (*cp == '.')
1689c2aa98e2SPeter Wemm n++;
1690c2aa98e2SPeter Wemm
1691c2aa98e2SPeter Wemm /*
1692c2aa98e2SPeter Wemm ** Build the search list.
1693c2aa98e2SPeter Wemm ** If there is at least one dot in name, start with a null
1694c2aa98e2SPeter Wemm ** domain to search the unmodified name first.
1695c2aa98e2SPeter Wemm ** If name does not end with a dot and search up local domain
1696c2aa98e2SPeter Wemm ** tree desired, append each local domain component to the
1697c2aa98e2SPeter Wemm ** search list; if name contains no dots and default domain
1698c2aa98e2SPeter Wemm ** name is desired, append default domain name to search list;
1699c2aa98e2SPeter Wemm ** else if name ends in a dot, remove that dot.
1700c2aa98e2SPeter Wemm */
1701c2aa98e2SPeter Wemm
17025b0945b5SGregory Neil Shapiro sli = 0;
1703c2aa98e2SPeter Wemm if (n > 0)
17045b0945b5SGregory Neil Shapiro searchlist[sli++] = "";
17055b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
17065b0945b5SGregory Neil Shapiro if (NameSearchList != NULL)
17075b0945b5SGregory Neil Shapiro {
17085b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE);
17095b0945b5SGregory Neil Shapiro searchlist[sli++] = NameSearchList;
17105b0945b5SGregory Neil Shapiro }
17115b0945b5SGregory Neil Shapiro # endif
1712c2aa98e2SPeter Wemm if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
1713c2aa98e2SPeter Wemm {
171406f25ae9SGregory Neil Shapiro /* make sure there are less than MAXDNSRCH domains */
171506f25ae9SGregory Neil Shapiro for (domain = RES_DNSRCH_VARIABLE, ret = 0;
17165b0945b5SGregory Neil Shapiro *domain != NULL && ret < MAXDNSRCH && sli < SLSIZE;
171706f25ae9SGregory Neil Shapiro ret++)
17185b0945b5SGregory Neil Shapiro searchlist[sli++] = *domain++;
1719c2aa98e2SPeter Wemm }
1720c2aa98e2SPeter Wemm else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
1721c2aa98e2SPeter Wemm {
17225b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE);
17235b0945b5SGregory Neil Shapiro searchlist[sli++] = _res.defdname;
1724c2aa98e2SPeter Wemm }
1725c2aa98e2SPeter Wemm else if (*cp == '.')
1726c2aa98e2SPeter Wemm {
1727c2aa98e2SPeter Wemm *cp = '\0';
1728c2aa98e2SPeter Wemm }
17295b0945b5SGregory Neil Shapiro SM_ASSERT(sli < SLSIZE);
17305b0945b5SGregory Neil Shapiro searchlist[sli] = NULL;
1731c2aa98e2SPeter Wemm
1732c2aa98e2SPeter Wemm /*
1733c2aa98e2SPeter Wemm ** Now loop through the search list, appending each domain in turn
1734c2aa98e2SPeter Wemm ** name and searching for a match.
1735c2aa98e2SPeter Wemm */
1736c2aa98e2SPeter Wemm
1737c2aa98e2SPeter Wemm mxmatch = NULL;
1738a7ec597cSGregory Neil Shapiro initial = T_A;
1739a7ec597cSGregory Neil Shapiro # if NETINET6
1740a7ec597cSGregory Neil Shapiro if (InetMode == AF_INET6)
1741a7ec597cSGregory Neil Shapiro initial = T_AAAA;
17425b0945b5SGregory Neil Shapiro # endif
1743a7ec597cSGregory Neil Shapiro qtype = initial;
1744c2aa98e2SPeter Wemm
17455b0945b5SGregory Neil Shapiro for (sli = 0; sli < SLSIZE; )
1746c2aa98e2SPeter Wemm {
17475b0945b5SGregory Neil Shapiro dp = searchlist[sli];
17485b0945b5SGregory Neil Shapiro if (NULL == dp)
17495b0945b5SGregory Neil Shapiro break;
1750a7ec597cSGregory Neil Shapiro if (qtype == initial)
175140266059SGregory Neil Shapiro gotmx = false;
1752c2aa98e2SPeter Wemm if (tTd(8, 5))
175340266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
17545b0945b5SGregory Neil Shapiro host, dp,
175506f25ae9SGregory Neil Shapiro # if NETINET6
175606f25ae9SGregory Neil Shapiro qtype == T_AAAA ? "AAAA" :
17575b0945b5SGregory Neil Shapiro # endif
175806f25ae9SGregory Neil Shapiro qtype == T_A ? "A" :
175906f25ae9SGregory Neil Shapiro qtype == T_MX ? "MX" :
176006f25ae9SGregory Neil Shapiro "???");
1761193538b7SGregory Neil Shapiro errno = 0;
17625b0945b5SGregory Neil Shapiro hp = (HEADER *) &answer;
17635b0945b5SGregory Neil Shapiro ret = (*resqdomain)(host, dp, C_IN, qtype,
1764c2aa98e2SPeter Wemm answer.qb2, sizeof(answer.qb2));
1765c2aa98e2SPeter Wemm if (ret <= 0)
1766c2aa98e2SPeter Wemm {
176740266059SGregory Neil Shapiro int save_errno = errno;
1768c2aa98e2SPeter Wemm
176940266059SGregory Neil Shapiro if (tTd(8, 7))
177040266059SGregory Neil Shapiro sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
177140266059SGregory Neil Shapiro save_errno, h_errno);
177240266059SGregory Neil Shapiro
177340266059SGregory Neil Shapiro if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
1774c2aa98e2SPeter Wemm {
1775193538b7SGregory Neil Shapiro /*
177640266059SGregory Neil Shapiro ** the name server seems to be down or broken.
1777193538b7SGregory Neil Shapiro */
1778193538b7SGregory Neil Shapiro
1779602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(TRY_AGAIN);
17805b0945b5SGregory Neil Shapiro if (*dp == '\0')
1781605302a5SGregory Neil Shapiro {
1782605302a5SGregory Neil Shapiro if (*statp == EX_OK)
1783605302a5SGregory Neil Shapiro *statp = EX_TEMPFAIL;
1784605302a5SGregory Neil Shapiro goto nexttype;
1785605302a5SGregory Neil Shapiro }
1786c2aa98e2SPeter Wemm *statp = EX_TEMPFAIL;
178706f25ae9SGregory Neil Shapiro
1788602a2b1bSGregory Neil Shapiro if (WorkAroundBrokenAAAA)
1789602a2b1bSGregory Neil Shapiro {
1790193538b7SGregory Neil Shapiro /*
1791193538b7SGregory Neil Shapiro ** Only return if not TRY_AGAIN as an
1792193538b7SGregory Neil Shapiro ** attempt with a different qtype may
1793193538b7SGregory Neil Shapiro ** succeed (res_querydomain() calls
1794193538b7SGregory Neil Shapiro ** res_query() calls res_send() which
1795193538b7SGregory Neil Shapiro ** sets errno to ETIMEDOUT if the
1796193538b7SGregory Neil Shapiro ** nameservers could be contacted but
1797193538b7SGregory Neil Shapiro ** didn't give an answer).
1798193538b7SGregory Neil Shapiro */
1799193538b7SGregory Neil Shapiro
180040266059SGregory Neil Shapiro if (save_errno != ETIMEDOUT)
18015b0945b5SGregory Neil Shapiro goto error;
1802602a2b1bSGregory Neil Shapiro }
180340266059SGregory Neil Shapiro else
18045b0945b5SGregory Neil Shapiro goto error;
1805c2aa98e2SPeter Wemm }
1806c2aa98e2SPeter Wemm
1807605302a5SGregory Neil Shapiro nexttype:
1808c2aa98e2SPeter Wemm if (h_errno != HOST_NOT_FOUND)
1809c2aa98e2SPeter Wemm {
1810c2aa98e2SPeter Wemm /* might have another type of interest */
181106f25ae9SGregory Neil Shapiro # if NETINET6
181240266059SGregory Neil Shapiro if (qtype == T_AAAA)
181306f25ae9SGregory Neil Shapiro {
1814c2aa98e2SPeter Wemm qtype = T_A;
1815c2aa98e2SPeter Wemm continue;
1816c2aa98e2SPeter Wemm }
181740266059SGregory Neil Shapiro else
181806f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
181940266059SGregory Neil Shapiro if (qtype == T_A && !gotmx &&
18205b0945b5SGregory Neil Shapiro (trymx || *dp == '\0'))
1821c2aa98e2SPeter Wemm {
1822c2aa98e2SPeter Wemm qtype = T_MX;
1823c2aa98e2SPeter Wemm continue;
1824c2aa98e2SPeter Wemm }
1825c2aa98e2SPeter Wemm }
1826c2aa98e2SPeter Wemm
1827c2aa98e2SPeter Wemm /* definite no -- try the next domain */
18285b0945b5SGregory Neil Shapiro sli++;
1829a7ec597cSGregory Neil Shapiro qtype = initial;
1830c2aa98e2SPeter Wemm continue;
1831c2aa98e2SPeter Wemm }
1832c2aa98e2SPeter Wemm else if (tTd(8, 7))
183340266059SGregory Neil Shapiro sm_dprintf("\tYES\n");
1834c2aa98e2SPeter Wemm
1835c2aa98e2SPeter Wemm /* avoid problems after truncation in tcp packets */
1836c2aa98e2SPeter Wemm if (ret > sizeof(answer))
1837c2aa98e2SPeter Wemm ret = sizeof(answer);
1838af9557fdSGregory Neil Shapiro SM_ASSERT(ret >= 0);
1839c2aa98e2SPeter Wemm
1840c2aa98e2SPeter Wemm /*
1841c2aa98e2SPeter Wemm ** Appear to have a match. Confirm it by searching for A or
1842c2aa98e2SPeter Wemm ** CNAME records. If we don't have a local domain
1843c2aa98e2SPeter Wemm ** wild card MX record, we will accept MX as well.
1844c2aa98e2SPeter Wemm */
1845c2aa98e2SPeter Wemm
184640266059SGregory Neil Shapiro ap = (unsigned char *) &answer + HFIXEDSZ;
184740266059SGregory Neil Shapiro eom = (unsigned char *) &answer + ret;
1848c2aa98e2SPeter Wemm
18495b0945b5SGregory Neil Shapiro if (0 == hp->ad)
18505b0945b5SGregory Neil Shapiro ad = false;
18515b0945b5SGregory Neil Shapiro
1852c2aa98e2SPeter Wemm /* skip question part of response -- we know what we asked */
185340266059SGregory Neil Shapiro for (qdcount = ntohs((unsigned short) hp->qdcount);
185406f25ae9SGregory Neil Shapiro qdcount--;
185506f25ae9SGregory Neil Shapiro ap += ret + QFIXEDSZ)
1856c2aa98e2SPeter Wemm {
1857c2aa98e2SPeter Wemm if ((ret = dn_skipname(ap, eom)) < 0)
1858c2aa98e2SPeter Wemm {
1859c2aa98e2SPeter Wemm if (tTd(8, 20))
186040266059SGregory Neil Shapiro sm_dprintf("qdcount failure (%d)\n",
186140266059SGregory Neil Shapiro ntohs((unsigned short) hp->qdcount));
1862c2aa98e2SPeter Wemm *statp = EX_SOFTWARE;
18635b0945b5SGregory Neil Shapiro goto error;
1864c2aa98e2SPeter Wemm }
1865c2aa98e2SPeter Wemm }
1866c2aa98e2SPeter Wemm
186740266059SGregory Neil Shapiro amatch = false;
186840266059SGregory Neil Shapiro for (ancount = ntohs((unsigned short) hp->ancount);
186906f25ae9SGregory Neil Shapiro --ancount >= 0 && ap < eom;
1870c2aa98e2SPeter Wemm ap += n)
1871c2aa98e2SPeter Wemm {
187240266059SGregory Neil Shapiro n = dn_expand((unsigned char *) &answer, eom, ap,
1873d0cef73dSGregory Neil Shapiro (RES_UNC_T) nbuf, sizeof(nbuf));
1874c2aa98e2SPeter Wemm if (n < 0)
1875c2aa98e2SPeter Wemm break;
1876c2aa98e2SPeter Wemm ap += n;
1877c2aa98e2SPeter Wemm GETSHORT(type, ap);
187840266059SGregory Neil Shapiro ap += INT16SZ; /* skip over class */
187940266059SGregory Neil Shapiro GETLONG(ttl, ap);
188040266059SGregory Neil Shapiro GETSHORT(n, ap); /* rdlength */
1881c2aa98e2SPeter Wemm switch (type)
1882c2aa98e2SPeter Wemm {
1883c2aa98e2SPeter Wemm case T_MX:
188440266059SGregory Neil Shapiro gotmx = true;
18855b0945b5SGregory Neil Shapiro if (*dp != '\0' && HasWildcardMX)
1886c2aa98e2SPeter Wemm {
1887c2aa98e2SPeter Wemm /*
1888c2aa98e2SPeter Wemm ** If we are using MX matches and have
1889c2aa98e2SPeter Wemm ** not yet gotten one, save this one
1890c2aa98e2SPeter Wemm ** but keep searching for an A or
1891c2aa98e2SPeter Wemm ** CNAME match.
1892c2aa98e2SPeter Wemm */
1893c2aa98e2SPeter Wemm
1894c2aa98e2SPeter Wemm if (trymx && mxmatch == NULL)
18955b0945b5SGregory Neil Shapiro mxmatch = dp;
1896c2aa98e2SPeter Wemm continue;
1897c2aa98e2SPeter Wemm }
1898c2aa98e2SPeter Wemm
1899c2aa98e2SPeter Wemm /*
1900c2aa98e2SPeter Wemm ** If we did not append a domain name, this
1901c2aa98e2SPeter Wemm ** must have been a canonical name to start
1902c2aa98e2SPeter Wemm ** with. Even if we did append a domain name,
1903c2aa98e2SPeter Wemm ** in the absence of a wildcard MX this must
1904c2aa98e2SPeter Wemm ** still be a real MX match.
1905c2aa98e2SPeter Wemm ** Such MX matches are as good as an A match,
1906c2aa98e2SPeter Wemm ** fall through.
1907c2aa98e2SPeter Wemm */
190806f25ae9SGregory Neil Shapiro /* FALLTHROUGH */
190906f25ae9SGregory Neil Shapiro
191006f25ae9SGregory Neil Shapiro # if NETINET6
191106f25ae9SGregory Neil Shapiro case T_AAAA:
19125b0945b5SGregory Neil Shapiro # endif
1913c2aa98e2SPeter Wemm case T_A:
1914c2aa98e2SPeter Wemm /* Flag that a good match was found */
191540266059SGregory Neil Shapiro amatch = true;
1916c2aa98e2SPeter Wemm
1917c2aa98e2SPeter Wemm /* continue in case a CNAME also exists */
1918c2aa98e2SPeter Wemm continue;
1919c2aa98e2SPeter Wemm
1920c2aa98e2SPeter Wemm case T_CNAME:
1921c2aa98e2SPeter Wemm if (DontExpandCnames)
1922c2aa98e2SPeter Wemm {
1923c2aa98e2SPeter Wemm /* got CNAME -- guaranteed canonical */
192440266059SGregory Neil Shapiro amatch = true;
1925c2aa98e2SPeter Wemm break;
1926c2aa98e2SPeter Wemm }
1927c2aa98e2SPeter Wemm
1928c2aa98e2SPeter Wemm if (loopcnt++ > MAXCNAMEDEPTH)
1929c2aa98e2SPeter Wemm {
1930c2aa98e2SPeter Wemm /*XXX should notify postmaster XXX*/
1931c2aa98e2SPeter Wemm message("DNS failure: CNAME loop for %s",
1932c2aa98e2SPeter Wemm host);
1933c2aa98e2SPeter Wemm if (CurEnv->e_message == NULL)
1934c2aa98e2SPeter Wemm {
1935c2aa98e2SPeter Wemm char ebuf[MAXLINE];
1936c2aa98e2SPeter Wemm
193740266059SGregory Neil Shapiro (void) sm_snprintf(ebuf,
1938d0cef73dSGregory Neil Shapiro sizeof(ebuf),
1939c2aa98e2SPeter Wemm "Deferred: DNS failure: CNAME loop for %.100s",
1940c2aa98e2SPeter Wemm host);
194140266059SGregory Neil Shapiro CurEnv->e_message =
194240266059SGregory Neil Shapiro sm_rpool_strdup_x(
194340266059SGregory Neil Shapiro CurEnv->e_rpool, ebuf);
1944c2aa98e2SPeter Wemm }
1945602a2b1bSGregory Neil Shapiro SM_SET_H_ERRNO(NO_RECOVERY);
1946c2aa98e2SPeter Wemm *statp = EX_CONFIG;
19475b0945b5SGregory Neil Shapiro goto error;
1948c2aa98e2SPeter Wemm }
1949c2aa98e2SPeter Wemm
1950c2aa98e2SPeter Wemm /* value points at name */
195140266059SGregory Neil Shapiro if ((ret = dn_expand((unsigned char *)&answer,
195240266059SGregory Neil Shapiro eom, ap, (RES_UNC_T) nbuf,
195340266059SGregory Neil Shapiro sizeof(nbuf))) < 0)
1954c2aa98e2SPeter Wemm break;
195540266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize);
1956c2aa98e2SPeter Wemm
1957c2aa98e2SPeter Wemm /*
1958c2aa98e2SPeter Wemm ** RFC 1034 section 3.6 specifies that CNAME
1959c2aa98e2SPeter Wemm ** should point at the canonical name -- but
1960c2aa98e2SPeter Wemm ** urges software to try again anyway.
1961c2aa98e2SPeter Wemm */
1962c2aa98e2SPeter Wemm
1963c2aa98e2SPeter Wemm goto cnameloop;
1964c2aa98e2SPeter Wemm
1965c2aa98e2SPeter Wemm default:
1966c2aa98e2SPeter Wemm /* not a record of interest */
1967c2aa98e2SPeter Wemm continue;
1968c2aa98e2SPeter Wemm }
1969c2aa98e2SPeter Wemm }
1970c2aa98e2SPeter Wemm
1971c2aa98e2SPeter Wemm if (amatch)
1972c2aa98e2SPeter Wemm {
1973c2aa98e2SPeter Wemm /*
1974c2aa98e2SPeter Wemm ** Got a good match -- either an A, CNAME, or an
1975c2aa98e2SPeter Wemm ** exact MX record. Save it and get out of here.
1976c2aa98e2SPeter Wemm */
1977c2aa98e2SPeter Wemm
19785b0945b5SGregory Neil Shapiro mxmatch = dp;
1979c2aa98e2SPeter Wemm break;
1980c2aa98e2SPeter Wemm }
1981c2aa98e2SPeter Wemm
1982c2aa98e2SPeter Wemm /*
1983c2aa98e2SPeter Wemm ** Nothing definitive yet.
1984c2aa98e2SPeter Wemm ** If this was a T_A query and we haven't yet found a MX
1985c2aa98e2SPeter Wemm ** match, try T_MX if allowed to do so.
1986c2aa98e2SPeter Wemm ** Otherwise, try the next domain.
1987c2aa98e2SPeter Wemm */
1988c2aa98e2SPeter Wemm
198906f25ae9SGregory Neil Shapiro # if NETINET6
199040266059SGregory Neil Shapiro if (qtype == T_AAAA)
1991c2aa98e2SPeter Wemm qtype = T_A;
199240266059SGregory Neil Shapiro else
19935b0945b5SGregory Neil Shapiro # endif
19945b0945b5SGregory Neil Shapiro if (qtype == T_A && !gotmx && (trymx || *dp == '\0'))
1995c2aa98e2SPeter Wemm qtype = T_MX;
1996c2aa98e2SPeter Wemm else
1997c2aa98e2SPeter Wemm {
1998a7ec597cSGregory Neil Shapiro qtype = initial;
19995b0945b5SGregory Neil Shapiro sli++;
2000c2aa98e2SPeter Wemm }
2001c2aa98e2SPeter Wemm }
2002c2aa98e2SPeter Wemm
2003c2aa98e2SPeter Wemm /* if nothing was found, we are done */
2004c2aa98e2SPeter Wemm if (mxmatch == NULL)
2005c2aa98e2SPeter Wemm {
2006193538b7SGregory Neil Shapiro if (*statp == EX_OK)
2007c2aa98e2SPeter Wemm *statp = EX_NOHOST;
20085b0945b5SGregory Neil Shapiro goto error;
2009c2aa98e2SPeter Wemm }
2010c2aa98e2SPeter Wemm
2011c2aa98e2SPeter Wemm /*
2012c2aa98e2SPeter Wemm ** Create canonical name and return.
2013c2aa98e2SPeter Wemm ** If saved domain name is null, name was already canonical.
2014c2aa98e2SPeter Wemm ** Otherwise append the saved domain name.
2015c2aa98e2SPeter Wemm */
2016c2aa98e2SPeter Wemm
2017d0cef73dSGregory Neil Shapiro (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host,
2018c2aa98e2SPeter Wemm *mxmatch == '\0' ? "" : ".",
2019c2aa98e2SPeter Wemm MAXDNAME, mxmatch);
202040266059SGregory Neil Shapiro (void) sm_strlcpy(host, nbuf, hbsize);
2021c2aa98e2SPeter Wemm if (tTd(8, 5))
202240266059SGregory Neil Shapiro sm_dprintf("dns_getcanonname: %s\n", host);
2023c2aa98e2SPeter Wemm *statp = EX_OK;
202440266059SGregory Neil Shapiro
202540266059SGregory Neil Shapiro /* return only one TTL entry, that should be sufficient */
202640266059SGregory Neil Shapiro if (ttl > 0 && pttl != NULL)
202740266059SGregory Neil Shapiro *pttl = ttl;
20285b0945b5SGregory Neil Shapiro # if DANE
20295b0945b5SGregory Neil Shapiro _res.options = old_options;
20305b0945b5SGregory Neil Shapiro # endif
20315b0945b5SGregory Neil Shapiro return ad ? HOST_SECURE : HOST_OK;
20325b0945b5SGregory Neil Shapiro
20335b0945b5SGregory Neil Shapiro error:
20345b0945b5SGregory Neil Shapiro # if DANE
20355b0945b5SGregory Neil Shapiro _res.options = old_options;
20365b0945b5SGregory Neil Shapiro # endif
20375b0945b5SGregory Neil Shapiro return HOST_NOTFOUND;
2038c2aa98e2SPeter Wemm }
20395b0945b5SGregory Neil Shapiro
2040c2aa98e2SPeter Wemm #endif /* NAMED_BIND */
2041