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