1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 /*
8  * lib/krb5/os/dnsglue.c
9  *
10  * Copyright 2004 by the Massachusetts Institute of Technology.
11  * All Rights Reserved.
12  *
13  * Export of this software from the United States of America may
14  *   require a specific license from the United States Government.
15  *   It is the responsibility of any person or organization contemplating
16  *   export to obtain such a license before exporting.
17  *
18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19  * distribute this software and its documentation for any purpose and
20  * without fee is hereby granted, provided that the above copyright
21  * notice appear in all copies and that both that copyright notice and
22  * this permission notice appear in supporting documentation, and that
23  * the name of M.I.T. not be used in advertising or publicity pertaining
24  * to distribution of the software without specific, written prior
25  * permission.  Furthermore if you modify this software you must label
26  * your software as modified software and not distribute it in such a
27  * fashion that it might be confused with the original M.I.T. software.
28  * M.I.T. makes no representations about the suitability of
29  * this software for any purpose.  It is provided "as is" without express
30  * or implied warranty.
31  *
32  */
33 #ifdef KRB5_DNS_LOOKUP
34 
35 #include "dnsglue.h"
36 
37 /*
38  * Opaque handle
39  */
40 struct krb5int_dns_state {
41     int nclass;
42     int ntype;
43     void *ansp;
44     int anslen;
45     int ansmax;
46 #if HAVE_NS_INITPARSE
47     int cur_ans;
48     ns_msg msg;
49 #else
50     unsigned char *ptr;
51     unsigned short nanswers;
52 #endif
53 };
54 
55 #if !HAVE_NS_INITPARSE
56 static int initparse(struct krb5int_dns_state *);
57 #endif
58 
59 /*
60  * krb5int_dns_init()
61  *
62  * Initialize an opaue handl.  Do name lookup and initial parsing of
63  * reply, skipping question section.  Prepare to iterate over answer
64  * section.  Returns -1 on error, 0 on success.
65  */
66 int
67 krb5int_dns_init(struct krb5int_dns_state **dsp,
68 		 char *host, int nclass, int ntype)
69 {
70 #if HAVE_RES_NSEARCH
71     struct __res_state statbuf;
72 #endif
73     struct krb5int_dns_state *ds;
74     int len, ret;
75     size_t nextincr, maxincr;
76     unsigned char *p;
77 
78     *dsp = ds = malloc(sizeof(*ds));
79     if (ds == NULL)
80 	return -1;
81 
82     ret = -1;
83     ds->nclass = nclass;
84     ds->ntype = ntype;
85     ds->ansp = NULL;
86     ds->anslen = 0;
87     ds->ansmax = 0;
88     nextincr = 2048;
89     maxincr = INT_MAX;
90 
91 #if HAVE_NS_INITPARSE
92     ds->cur_ans = 0;
93 #endif
94 
95 #if HAVE_RES_NSEARCH
96     memset(&statbuf, 0, sizeof(statbuf));
97     ret = res_ninit(&statbuf);
98     if (ret < 0)
99 	return -1;
100 #endif
101 
102     do {
103 	p = (ds->ansp == NULL)
104 	    ? malloc(nextincr) : realloc(ds->ansp, nextincr);
105 
106 	if (p == NULL && ds->ansp != NULL) {
107 	    ret = -1;
108 	    goto errout;
109 	}
110 	ds->ansp = p;
111 	ds->ansmax = nextincr;
112 
113 #if HAVE_RES_NSEARCH
114 	len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
115 			  ds->ansp, ds->ansmax);
116 #else
117 	len = res_search(host, ds->nclass, ds->ntype,
118 			 ds->ansp, ds->ansmax);
119 #endif
120 	if (len > maxincr) {
121 	    ret = -1;
122 	    goto errout;
123 	}
124 	while (nextincr < len)
125 	    nextincr *= 2;
126 	if (len < 0 || nextincr > maxincr) {
127 	    ret = -1;
128 	    goto errout;
129 	}
130     } while (len > ds->ansmax);
131 
132     ds->anslen = len;
133 #if HAVE_NS_INITPARSE
134     ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
135 #else
136     ret = initparse(ds);
137 #endif
138     if (ret < 0)
139 	goto errout;
140 
141     ret = 0;
142 
143 errout:
144 #if HAVE_RES_NSEARCH
145 #if HAVE_RES_NDESTROY
146     res_ndestroy(&statbuf);
147 #else
148     res_nclose(&statbuf);
149 #endif
150 #endif
151     if (ret < 0) {
152 	if (ds->ansp != NULL) {
153 	    free(ds->ansp);
154 	    ds->ansp = NULL;
155 	}
156     }
157 
158     return ret;
159 }
160 
161 #if HAVE_NS_INITPARSE
162 /*
163  * krb5int_dns_nextans - get next matching answer record
164  *
165  * Sets pp to NULL if no more records.  Returns -1 on error, 0 on
166  * success.
167  */
168 int
169 krb5int_dns_nextans(struct krb5int_dns_state *ds,
170 		    const unsigned char **pp, int *lenp)
171 {
172     int len;
173     ns_rr rr;
174 
175     *pp = NULL;
176     *lenp = 0;
177     while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
178 	len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
179 	if (len < 0)
180 	    return -1;
181 	ds->cur_ans++;
182 	if (ds->nclass == ns_rr_class(rr)
183 	    && ds->ntype == ns_rr_type(rr)) {
184 	    *pp = ns_rr_rdata(rr);
185 	    *lenp = ns_rr_rdlen(rr);
186 	    return 0;
187 	}
188     }
189     return 0;
190 }
191 #endif
192 
193 /*
194  * krb5int_dns_expand - wrapper for dn_expand()
195  */
196 int krb5int_dns_expand(struct krb5int_dns_state *ds,
197 		       const unsigned char *p,
198 		       char *buf, int len)
199 {
200 
201 #if HAVE_NS_NAME_UNCOMPRESS
202     return ns_name_uncompress(ds->ansp,
203 			      (unsigned char *)ds->ansp + ds->anslen,
204 			      p, buf, (size_t)len);
205 #else
206     return dn_expand(ds->ansp,
207 		     (unsigned char *)ds->ansp + ds->anslen,
208 		     p, buf, len);
209 #endif
210 }
211 
212 /*
213  * Free stuff.
214  */
215 void
216 krb5int_dns_fini(struct krb5int_dns_state *ds)
217 {
218     if (ds == NULL)
219 	return;
220     if (ds->ansp != NULL)
221 	free(ds->ansp);
222     free(ds);
223 }
224 
225 /*
226  * Compat routines for BIND 4
227  */
228 #if !HAVE_NS_INITPARSE
229 
230 /*
231  * initparse
232  *
233  * Skip header and question section of reply.  Set a pointer to the
234  * beginning of the answer section, and prepare to iterate over
235  * answer records.
236  */
237 static int
238 initparse(struct krb5int_dns_state *ds)
239 {
240     HEADER *hdr;
241     unsigned char *p;
242     unsigned short nqueries, nanswers;
243     int len;
244 #if !HAVE_DN_SKIPNAME
245     char host[MAXDNAME];
246 #endif
247 
248     if (ds->anslen < sizeof(HEADER))
249 	return -1;
250 
251     hdr = (HEADER *)ds->ansp;
252     p = ds->ansp;
253     nqueries = ntohs((unsigned short)hdr->qdcount);
254     nanswers = ntohs((unsigned short)hdr->ancount);
255     p += sizeof(HEADER);
256 
257     /*
258      * Skip query records.
259      */
260     while (nqueries--) {
261 #if HAVE_DN_SKIPNAME
262 	len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
263 #else
264 	len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
265 			p, host, sizeof(host));
266 #endif
267 	if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
268 	    return -1;
269 	p += len + 4;
270     }
271     ds->ptr = p;
272     ds->nanswers = nanswers;
273     return 0;
274 }
275 
276 /*
277  * krb5int_dns_nextans() - get next answer record
278  *
279  * Sets pp to NULL if no more records.
280  */
281 int
282 krb5int_dns_nextans(struct krb5int_dns_state *ds,
283 		    const unsigned char **pp, int *lenp)
284 {
285     int len;
286     unsigned char *p;
287     unsigned short ntype, nclass, rdlen;
288 #if !HAVE_DN_SKIPNAME
289     char host[MAXDNAME];
290 #endif
291 
292     *pp = NULL;
293     *lenp = 0;
294     p = ds->ptr;
295 
296     while (ds->nanswers--) {
297 #if HAVE_DN_SKIPNAME
298 	len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
299 #else
300 	len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
301 			p, host, sizeof(host));
302 #endif
303 	if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
304 	    return -1;
305 	p += len;
306 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
307 	/* Also skip 4 bytes of TTL */
308 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
309 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
310 
311 	if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
312 	    return -1;
313 	if (nclass == ds->nclass && ntype == ds->ntype) {
314 	    *pp = p;
315 	    *lenp = rdlen;
316 	    ds->ptr = p + rdlen;
317 	    return 0;
318 	}
319 	p += rdlen;
320     }
321     return 0;
322 out:
323     return -1;
324 }
325 
326 #endif
327 
328 #endif /* KRB5_DNS_LOOKUP */
329