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