1 /*
2  * Copyright 2005 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     ret = res_ninit(&statbuf);
97     if (ret < 0)
98 	return -1;
99 #endif
100 
101     do {
102 	p = (ds->ansp == NULL)
103 	    ? malloc(nextincr) : realloc(ds->ansp, nextincr);
104 
105 	if (p == NULL && ds->ansp != NULL) {
106 	    ret = -1;
107 	    goto errout;
108 	}
109 	ds->ansp = p;
110 	ds->ansmax = nextincr;
111 
112 #if HAVE_RES_NSEARCH
113 	len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
114 			  ds->ansp, ds->ansmax);
115 #else
116 	len = res_search(host, ds->nclass, ds->ntype,
117 			 ds->ansp, ds->ansmax);
118 #endif
119 	if (len > maxincr) {
120 	    ret = -1;
121 	    goto errout;
122 	}
123 	while (nextincr < len)
124 	    nextincr *= 2;
125 	if (len < 0 || nextincr > maxincr) {
126 	    ret = -1;
127 	    goto errout;
128 	}
129     } while (len > ds->ansmax);
130 
131     ds->anslen = len;
132 #if HAVE_NS_INITPARSE
133     ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
134 #else
135     ret = initparse(ds);
136 #endif
137     if (ret < 0)
138 	goto errout;
139 
140     ret = 0;
141 
142 errout:
143 #if HAVE_RES_NSEARCH
144 #if HAVE_RES_NDESTROY
145     res_ndestroy(&statbuf);
146 #else
147     res_nclose(&statbuf);
148 #endif
149 #endif
150     if (ret < 0) {
151 	if (ds->ansp != NULL) {
152 	    free(ds->ansp);
153 	    ds->ansp = NULL;
154 	}
155     }
156 
157     return ret;
158 }
159 
160 #if HAVE_NS_INITPARSE
161 /*
162  * krb5int_dns_nextans - get next matching answer record
163  *
164  * Sets pp to NULL if no more records.  Returns -1 on error, 0 on
165  * success.
166  */
167 int
168 krb5int_dns_nextans(struct krb5int_dns_state *ds,
169 		    const unsigned char **pp, int *lenp)
170 {
171     int len;
172     ns_rr rr;
173 
174     *pp = NULL;
175     *lenp = 0;
176     while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
177 	len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
178 	if (len < 0)
179 	    return -1;
180 	ds->cur_ans++;
181 	if (ds->nclass == ns_rr_class(rr)
182 	    && ds->ntype == ns_rr_type(rr)) {
183 	    *pp = ns_rr_rdata(rr);
184 	    *lenp = ns_rr_rdlen(rr);
185 	    return 0;
186 	}
187     }
188     return 0;
189 }
190 #endif
191 
192 /*
193  * krb5int_dns_expand - wrapper for dn_expand()
194  */
195 int krb5int_dns_expand(struct krb5int_dns_state *ds,
196 		       const unsigned char *p,
197 		       char *buf, int len)
198 {
199 
200 #if HAVE_NS_NAME_UNCOMPRESS
201     return ns_name_uncompress(ds->ansp,
202 			      (unsigned char *)ds->ansp + ds->anslen,
203 			      p, buf, (size_t)len);
204 #else
205     return dn_expand(ds->ansp,
206 		     (unsigned char *)ds->ansp + ds->anslen,
207 		     p, buf, len);
208 #endif
209 }
210 
211 /*
212  * Free stuff.
213  */
214 void
215 krb5int_dns_fini(struct krb5int_dns_state *ds)
216 {
217     if (ds == NULL)
218 	return;
219     if (ds->ansp != NULL)
220 	free(ds->ansp);
221     free(ds);
222 }
223 
224 /*
225  * Compat routines for BIND 4
226  */
227 #if !HAVE_NS_INITPARSE
228 
229 /*
230  * initparse
231  *
232  * Skip header and question section of reply.  Set a pointer to the
233  * beginning of the answer section, and prepare to iterate over
234  * answer records.
235  */
236 static int
237 initparse(struct krb5int_dns_state *ds)
238 {
239     HEADER *hdr;
240     unsigned char *p;
241     unsigned short nqueries, nanswers;
242     int len;
243 #if !HAVE_DN_SKIPNAME
244     char host[MAXDNAME];
245 #endif
246 
247     if (ds->anslen < sizeof(HEADER))
248 	return -1;
249 
250     hdr = (HEADER *)ds->ansp;
251     p = ds->ansp;
252     nqueries = ntohs((unsigned short)hdr->qdcount);
253     nanswers = ntohs((unsigned short)hdr->ancount);
254     p += sizeof(HEADER);
255 
256     /*
257      * Skip query records.
258      */
259     while (nqueries--) {
260 #if HAVE_DN_SKIPNAME
261 	len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
262 #else
263 	len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
264 			p, host, sizeof(host));
265 #endif
266 	if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
267 	    return -1;
268 	p += len + 4;
269     }
270     ds->ptr = p;
271     ds->nanswers = nanswers;
272     return 0;
273 }
274 
275 /*
276  * krb5int_dns_nextans() - get next answer record
277  *
278  * Sets pp to NULL if no more records.
279  */
280 int
281 krb5int_dns_nextans(struct krb5int_dns_state *ds,
282 		    const unsigned char **pp, int *lenp)
283 {
284     int len;
285     unsigned char *p;
286     unsigned short ntype, nclass, rdlen;
287 #if !HAVE_DN_SKIPNAME
288     char host[MAXDNAME];
289 #endif
290 
291     *pp = NULL;
292     *lenp = 0;
293     p = ds->ptr;
294 
295     while (ds->nanswers--) {
296 #if HAVE_DN_SKIPNAME
297 	len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
298 #else
299 	len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
300 			p, host, sizeof(host));
301 #endif
302 	if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
303 	    return -1;
304 	p += len;
305 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
306 	/* Also skip 4 bytes of TTL */
307 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
308 	SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
309 
310 	if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
311 	    return -1;
312 	if (nclass == ds->nclass && ntype == ds->ntype) {
313 	    *pp = p;
314 	    *lenp = rdlen;
315 	    ds->ptr = p + rdlen;
316 	    return 0;
317 	}
318 	p += rdlen;
319     }
320     return 0;
321 out:
322     return -1;
323 }
324 
325 #endif
326 
327 #endif /* KRB5_DNS_LOOKUP */
328