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