1ebd1706eSgtb /*
2ae5b046dSsemery * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3ebd1706eSgtb * Use is subject to license terms.
4ebd1706eSgtb */
5ebd1706eSgtb
6505d05c7Sgtb /*
7505d05c7Sgtb * lib/krb5/os/dnsglue.c
8505d05c7Sgtb *
9505d05c7Sgtb * Copyright 2004 by the Massachusetts Institute of Technology.
10505d05c7Sgtb * All Rights Reserved.
11505d05c7Sgtb *
12505d05c7Sgtb * Export of this software from the United States of America may
13505d05c7Sgtb * require a specific license from the United States Government.
14505d05c7Sgtb * It is the responsibility of any person or organization contemplating
15505d05c7Sgtb * export to obtain such a license before exporting.
16*55fea89dSDan Cross *
17505d05c7Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18505d05c7Sgtb * distribute this software and its documentation for any purpose and
19505d05c7Sgtb * without fee is hereby granted, provided that the above copyright
20505d05c7Sgtb * notice appear in all copies and that both that copyright notice and
21505d05c7Sgtb * this permission notice appear in supporting documentation, and that
22505d05c7Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining
23505d05c7Sgtb * to distribution of the software without specific, written prior
24505d05c7Sgtb * permission. Furthermore if you modify this software you must label
25505d05c7Sgtb * your software as modified software and not distribute it in such a
26505d05c7Sgtb * fashion that it might be confused with the original M.I.T. software.
27505d05c7Sgtb * M.I.T. makes no representations about the suitability of
28505d05c7Sgtb * this software for any purpose. It is provided "as is" without express
29505d05c7Sgtb * or implied warranty.
30*55fea89dSDan Cross *
31505d05c7Sgtb */
32159d09a2SMark Phalan #include "autoconf.h"
33505d05c7Sgtb #ifdef KRB5_DNS_LOOKUP
34505d05c7Sgtb
35505d05c7Sgtb #include "dnsglue.h"
36505d05c7Sgtb
37159d09a2SMark Phalan /*
38159d09a2SMark Phalan * Only use res_ninit() if there's also a res_ndestroy(), to avoid
39159d09a2SMark Phalan * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
40159d09a2SMark Phalan * 5.x). While we're at it, make sure res_nsearch() is there too.
41159d09a2SMark Phalan *
42159d09a2SMark Phalan * In any case, it is probable that platforms having broken
43159d09a2SMark Phalan * res_ninit() will have thread safety hacks for res_init() and _res.
44159d09a2SMark Phalan */
45159d09a2SMark Phalan #if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH
46159d09a2SMark Phalan #define USE_RES_NINIT 1
47159d09a2SMark Phalan #endif
48159d09a2SMark Phalan
49505d05c7Sgtb /*
50505d05c7Sgtb * Opaque handle
51505d05c7Sgtb */
52505d05c7Sgtb struct krb5int_dns_state {
53505d05c7Sgtb int nclass;
54505d05c7Sgtb int ntype;
55505d05c7Sgtb void *ansp;
56505d05c7Sgtb int anslen;
57505d05c7Sgtb int ansmax;
58505d05c7Sgtb #if HAVE_NS_INITPARSE
59505d05c7Sgtb int cur_ans;
60505d05c7Sgtb ns_msg msg;
61505d05c7Sgtb #else
62505d05c7Sgtb unsigned char *ptr;
63505d05c7Sgtb unsigned short nanswers;
64505d05c7Sgtb #endif
65505d05c7Sgtb };
66505d05c7Sgtb
67505d05c7Sgtb #if !HAVE_NS_INITPARSE
68505d05c7Sgtb static int initparse(struct krb5int_dns_state *);
69505d05c7Sgtb #endif
70505d05c7Sgtb
71505d05c7Sgtb /*
72505d05c7Sgtb * krb5int_dns_init()
73505d05c7Sgtb *
74159d09a2SMark Phalan * Initialize an opaque handle. Do name lookup and initial parsing of
75505d05c7Sgtb * reply, skipping question section. Prepare to iterate over answer
76505d05c7Sgtb * section. Returns -1 on error, 0 on success.
77505d05c7Sgtb */
78505d05c7Sgtb int
krb5int_dns_init(struct krb5int_dns_state ** dsp,char * host,int nclass,int ntype)79505d05c7Sgtb krb5int_dns_init(struct krb5int_dns_state **dsp,
80505d05c7Sgtb char *host, int nclass, int ntype)
81505d05c7Sgtb {
82159d09a2SMark Phalan #if USE_RES_NINIT
83505d05c7Sgtb struct __res_state statbuf;
84505d05c7Sgtb #endif
85505d05c7Sgtb struct krb5int_dns_state *ds;
86505d05c7Sgtb int len, ret;
87505d05c7Sgtb size_t nextincr, maxincr;
88505d05c7Sgtb unsigned char *p;
89505d05c7Sgtb
90505d05c7Sgtb *dsp = ds = malloc(sizeof(*ds));
91505d05c7Sgtb if (ds == NULL)
92505d05c7Sgtb return -1;
93505d05c7Sgtb
94505d05c7Sgtb ret = -1;
95505d05c7Sgtb ds->nclass = nclass;
96505d05c7Sgtb ds->ntype = ntype;
97505d05c7Sgtb ds->ansp = NULL;
98505d05c7Sgtb ds->anslen = 0;
99505d05c7Sgtb ds->ansmax = 0;
100505d05c7Sgtb nextincr = 2048;
101505d05c7Sgtb maxincr = INT_MAX;
102505d05c7Sgtb
103505d05c7Sgtb #if HAVE_NS_INITPARSE
104505d05c7Sgtb ds->cur_ans = 0;
105505d05c7Sgtb #endif
106505d05c7Sgtb
107159d09a2SMark Phalan #if USE_RES_NINIT
108ae5b046dSsemery memset(&statbuf, 0, sizeof(statbuf));
109505d05c7Sgtb ret = res_ninit(&statbuf);
110159d09a2SMark Phalan #else
111159d09a2SMark Phalan ret = res_init();
112159d09a2SMark Phalan #endif
113505d05c7Sgtb if (ret < 0)
114505d05c7Sgtb return -1;
115505d05c7Sgtb
116505d05c7Sgtb do {
117505d05c7Sgtb p = (ds->ansp == NULL)
118505d05c7Sgtb ? malloc(nextincr) : realloc(ds->ansp, nextincr);
119505d05c7Sgtb
120505d05c7Sgtb if (p == NULL && ds->ansp != NULL) {
121505d05c7Sgtb ret = -1;
122505d05c7Sgtb goto errout;
123505d05c7Sgtb }
124505d05c7Sgtb ds->ansp = p;
125505d05c7Sgtb ds->ansmax = nextincr;
126505d05c7Sgtb
127159d09a2SMark Phalan #if USE_RES_NINIT
128505d05c7Sgtb len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
129505d05c7Sgtb ds->ansp, ds->ansmax);
130505d05c7Sgtb #else
131505d05c7Sgtb len = res_search(host, ds->nclass, ds->ntype,
132505d05c7Sgtb ds->ansp, ds->ansmax);
133505d05c7Sgtb #endif
134505d05c7Sgtb if (len > maxincr) {
135505d05c7Sgtb ret = -1;
136505d05c7Sgtb goto errout;
137505d05c7Sgtb }
138505d05c7Sgtb while (nextincr < len)
139505d05c7Sgtb nextincr *= 2;
140505d05c7Sgtb if (len < 0 || nextincr > maxincr) {
141505d05c7Sgtb ret = -1;
142505d05c7Sgtb goto errout;
143505d05c7Sgtb }
144505d05c7Sgtb } while (len > ds->ansmax);
145505d05c7Sgtb
146505d05c7Sgtb ds->anslen = len;
147505d05c7Sgtb #if HAVE_NS_INITPARSE
148505d05c7Sgtb ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
149505d05c7Sgtb #else
150505d05c7Sgtb ret = initparse(ds);
151505d05c7Sgtb #endif
152505d05c7Sgtb if (ret < 0)
153505d05c7Sgtb goto errout;
154505d05c7Sgtb
155505d05c7Sgtb ret = 0;
156505d05c7Sgtb
157505d05c7Sgtb errout:
158159d09a2SMark Phalan #if USE_RES_NINIT
159505d05c7Sgtb res_ndestroy(&statbuf);
160505d05c7Sgtb #endif
161505d05c7Sgtb if (ret < 0) {
162505d05c7Sgtb if (ds->ansp != NULL) {
163505d05c7Sgtb free(ds->ansp);
164505d05c7Sgtb ds->ansp = NULL;
165505d05c7Sgtb }
166505d05c7Sgtb }
167505d05c7Sgtb
168505d05c7Sgtb return ret;
169505d05c7Sgtb }
170505d05c7Sgtb
171505d05c7Sgtb #if HAVE_NS_INITPARSE
172505d05c7Sgtb /*
173505d05c7Sgtb * krb5int_dns_nextans - get next matching answer record
174505d05c7Sgtb *
175505d05c7Sgtb * Sets pp to NULL if no more records. Returns -1 on error, 0 on
176505d05c7Sgtb * success.
177505d05c7Sgtb */
178505d05c7Sgtb int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)179505d05c7Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds,
180505d05c7Sgtb const unsigned char **pp, int *lenp)
181505d05c7Sgtb {
182505d05c7Sgtb int len;
183505d05c7Sgtb ns_rr rr;
184505d05c7Sgtb
185505d05c7Sgtb *pp = NULL;
186505d05c7Sgtb *lenp = 0;
187505d05c7Sgtb while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
188505d05c7Sgtb len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
189505d05c7Sgtb if (len < 0)
190505d05c7Sgtb return -1;
191505d05c7Sgtb ds->cur_ans++;
192505d05c7Sgtb if (ds->nclass == ns_rr_class(rr)
193505d05c7Sgtb && ds->ntype == ns_rr_type(rr)) {
194505d05c7Sgtb *pp = ns_rr_rdata(rr);
195505d05c7Sgtb *lenp = ns_rr_rdlen(rr);
196505d05c7Sgtb return 0;
197505d05c7Sgtb }
198505d05c7Sgtb }
199505d05c7Sgtb return 0;
200505d05c7Sgtb }
201505d05c7Sgtb #endif
202505d05c7Sgtb
203505d05c7Sgtb /*
204505d05c7Sgtb * krb5int_dns_expand - wrapper for dn_expand()
205505d05c7Sgtb */
krb5int_dns_expand(struct krb5int_dns_state * ds,const unsigned char * p,char * buf,int len)206505d05c7Sgtb int krb5int_dns_expand(struct krb5int_dns_state *ds,
207505d05c7Sgtb const unsigned char *p,
208505d05c7Sgtb char *buf, int len)
209505d05c7Sgtb {
210505d05c7Sgtb
211505d05c7Sgtb #if HAVE_NS_NAME_UNCOMPRESS
212505d05c7Sgtb return ns_name_uncompress(ds->ansp,
213505d05c7Sgtb (unsigned char *)ds->ansp + ds->anslen,
214505d05c7Sgtb p, buf, (size_t)len);
215505d05c7Sgtb #else
216505d05c7Sgtb return dn_expand(ds->ansp,
217505d05c7Sgtb (unsigned char *)ds->ansp + ds->anslen,
218505d05c7Sgtb p, buf, len);
219505d05c7Sgtb #endif
220505d05c7Sgtb }
221505d05c7Sgtb
222505d05c7Sgtb /*
223505d05c7Sgtb * Free stuff.
224505d05c7Sgtb */
225505d05c7Sgtb void
krb5int_dns_fini(struct krb5int_dns_state * ds)226505d05c7Sgtb krb5int_dns_fini(struct krb5int_dns_state *ds)
227505d05c7Sgtb {
228505d05c7Sgtb if (ds == NULL)
229505d05c7Sgtb return;
230505d05c7Sgtb if (ds->ansp != NULL)
231505d05c7Sgtb free(ds->ansp);
232505d05c7Sgtb free(ds);
233505d05c7Sgtb }
234505d05c7Sgtb
235505d05c7Sgtb /*
236505d05c7Sgtb * Compat routines for BIND 4
237505d05c7Sgtb */
238505d05c7Sgtb #if !HAVE_NS_INITPARSE
239505d05c7Sgtb
240505d05c7Sgtb /*
241505d05c7Sgtb * initparse
242505d05c7Sgtb *
243505d05c7Sgtb * Skip header and question section of reply. Set a pointer to the
244505d05c7Sgtb * beginning of the answer section, and prepare to iterate over
245505d05c7Sgtb * answer records.
246505d05c7Sgtb */
247505d05c7Sgtb static int
initparse(struct krb5int_dns_state * ds)248505d05c7Sgtb initparse(struct krb5int_dns_state *ds)
249505d05c7Sgtb {
250505d05c7Sgtb HEADER *hdr;
251505d05c7Sgtb unsigned char *p;
252505d05c7Sgtb unsigned short nqueries, nanswers;
253505d05c7Sgtb int len;
254505d05c7Sgtb #if !HAVE_DN_SKIPNAME
255505d05c7Sgtb char host[MAXDNAME];
256505d05c7Sgtb #endif
257505d05c7Sgtb
258505d05c7Sgtb if (ds->anslen < sizeof(HEADER))
259505d05c7Sgtb return -1;
260505d05c7Sgtb
261505d05c7Sgtb hdr = (HEADER *)ds->ansp;
262505d05c7Sgtb p = ds->ansp;
263505d05c7Sgtb nqueries = ntohs((unsigned short)hdr->qdcount);
264505d05c7Sgtb nanswers = ntohs((unsigned short)hdr->ancount);
265505d05c7Sgtb p += sizeof(HEADER);
266505d05c7Sgtb
267505d05c7Sgtb /*
268505d05c7Sgtb * Skip query records.
269505d05c7Sgtb */
270505d05c7Sgtb while (nqueries--) {
271505d05c7Sgtb #if HAVE_DN_SKIPNAME
272505d05c7Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
273505d05c7Sgtb #else
274505d05c7Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
275505d05c7Sgtb p, host, sizeof(host));
276505d05c7Sgtb #endif
277505d05c7Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
278505d05c7Sgtb return -1;
279505d05c7Sgtb p += len + 4;
280505d05c7Sgtb }
281505d05c7Sgtb ds->ptr = p;
282505d05c7Sgtb ds->nanswers = nanswers;
283505d05c7Sgtb return 0;
284505d05c7Sgtb }
285505d05c7Sgtb
286505d05c7Sgtb /*
287505d05c7Sgtb * krb5int_dns_nextans() - get next answer record
288505d05c7Sgtb *
289505d05c7Sgtb * Sets pp to NULL if no more records.
290505d05c7Sgtb */
291505d05c7Sgtb int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)292505d05c7Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds,
293505d05c7Sgtb const unsigned char **pp, int *lenp)
294505d05c7Sgtb {
295505d05c7Sgtb int len;
296505d05c7Sgtb unsigned char *p;
297505d05c7Sgtb unsigned short ntype, nclass, rdlen;
298505d05c7Sgtb #if !HAVE_DN_SKIPNAME
299505d05c7Sgtb char host[MAXDNAME];
300505d05c7Sgtb #endif
301505d05c7Sgtb
302505d05c7Sgtb *pp = NULL;
303505d05c7Sgtb *lenp = 0;
304505d05c7Sgtb p = ds->ptr;
305505d05c7Sgtb
306505d05c7Sgtb while (ds->nanswers--) {
307505d05c7Sgtb #if HAVE_DN_SKIPNAME
308505d05c7Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
309505d05c7Sgtb #else
310505d05c7Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
311505d05c7Sgtb p, host, sizeof(host));
312505d05c7Sgtb #endif
313505d05c7Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
314505d05c7Sgtb return -1;
315505d05c7Sgtb p += len;
316505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
317505d05c7Sgtb /* Also skip 4 bytes of TTL */
318505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
319505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
320505d05c7Sgtb
321505d05c7Sgtb if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
322505d05c7Sgtb return -1;
323159d09a2SMark Phalan /* Solaris Kerberos - resync */
324159d09a2SMark Phalan #if 0
325159d09a2SMark Phalan if (rdlen > INT_MAX)
326159d09a2SMark Phalan return -1;
327159d09a2SMark Phalan #endif
328505d05c7Sgtb if (nclass == ds->nclass && ntype == ds->ntype) {
329505d05c7Sgtb *pp = p;
330505d05c7Sgtb *lenp = rdlen;
331505d05c7Sgtb ds->ptr = p + rdlen;
332505d05c7Sgtb return 0;
333505d05c7Sgtb }
334505d05c7Sgtb p += rdlen;
335505d05c7Sgtb }
336505d05c7Sgtb return 0;
337505d05c7Sgtb out:
338505d05c7Sgtb return -1;
339505d05c7Sgtb }
340505d05c7Sgtb
341505d05c7Sgtb #endif
342505d05c7Sgtb
343505d05c7Sgtb #endif /* KRB5_DNS_LOOKUP */
344