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