11047Sgtb /*
26815Ssemery * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
31047Sgtb * Use is subject to license terms.
41047Sgtb */
51047Sgtb
6781Sgtb /*
7781Sgtb * lib/krb5/os/dnsglue.c
8781Sgtb *
9781Sgtb * Copyright 2004 by the Massachusetts Institute of Technology.
10781Sgtb * All Rights Reserved.
11781Sgtb *
12781Sgtb * Export of this software from the United States of America may
13781Sgtb * require a specific license from the United States Government.
14781Sgtb * It is the responsibility of any person or organization contemplating
15781Sgtb * export to obtain such a license before exporting.
16781Sgtb *
17781Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18781Sgtb * distribute this software and its documentation for any purpose and
19781Sgtb * without fee is hereby granted, provided that the above copyright
20781Sgtb * notice appear in all copies and that both that copyright notice and
21781Sgtb * this permission notice appear in supporting documentation, and that
22781Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining
23781Sgtb * to distribution of the software without specific, written prior
24781Sgtb * permission. Furthermore if you modify this software you must label
25781Sgtb * your software as modified software and not distribute it in such a
26781Sgtb * fashion that it might be confused with the original M.I.T. software.
27781Sgtb * M.I.T. makes no representations about the suitability of
28781Sgtb * this software for any purpose. It is provided "as is" without express
29781Sgtb * or implied warranty.
30781Sgtb *
31781Sgtb */
32*7934SMark.Phalan@Sun.COM #include "autoconf.h"
33781Sgtb #ifdef KRB5_DNS_LOOKUP
34781Sgtb
35781Sgtb #include "dnsglue.h"
36781Sgtb
37781Sgtb /*
38*7934SMark.Phalan@Sun.COM * Only use res_ninit() if there's also a res_ndestroy(), to avoid
39*7934SMark.Phalan@Sun.COM * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
40*7934SMark.Phalan@Sun.COM * 5.x). While we're at it, make sure res_nsearch() is there too.
41*7934SMark.Phalan@Sun.COM *
42*7934SMark.Phalan@Sun.COM * In any case, it is probable that platforms having broken
43*7934SMark.Phalan@Sun.COM * res_ninit() will have thread safety hacks for res_init() and _res.
44*7934SMark.Phalan@Sun.COM */
45*7934SMark.Phalan@Sun.COM #if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH
46*7934SMark.Phalan@Sun.COM #define USE_RES_NINIT 1
47*7934SMark.Phalan@Sun.COM #endif
48*7934SMark.Phalan@Sun.COM
49*7934SMark.Phalan@Sun.COM /*
50781Sgtb * Opaque handle
51781Sgtb */
52781Sgtb struct krb5int_dns_state {
53781Sgtb int nclass;
54781Sgtb int ntype;
55781Sgtb void *ansp;
56781Sgtb int anslen;
57781Sgtb int ansmax;
58781Sgtb #if HAVE_NS_INITPARSE
59781Sgtb int cur_ans;
60781Sgtb ns_msg msg;
61781Sgtb #else
62781Sgtb unsigned char *ptr;
63781Sgtb unsigned short nanswers;
64781Sgtb #endif
65781Sgtb };
66781Sgtb
67781Sgtb #if !HAVE_NS_INITPARSE
68781Sgtb static int initparse(struct krb5int_dns_state *);
69781Sgtb #endif
70781Sgtb
71781Sgtb /*
72781Sgtb * krb5int_dns_init()
73781Sgtb *
74*7934SMark.Phalan@Sun.COM * Initialize an opaque handle. Do name lookup and initial parsing of
75781Sgtb * reply, skipping question section. Prepare to iterate over answer
76781Sgtb * section. Returns -1 on error, 0 on success.
77781Sgtb */
78781Sgtb int
krb5int_dns_init(struct krb5int_dns_state ** dsp,char * host,int nclass,int ntype)79781Sgtb krb5int_dns_init(struct krb5int_dns_state **dsp,
80781Sgtb char *host, int nclass, int ntype)
81781Sgtb {
82*7934SMark.Phalan@Sun.COM #if USE_RES_NINIT
83781Sgtb struct __res_state statbuf;
84781Sgtb #endif
85781Sgtb struct krb5int_dns_state *ds;
86781Sgtb int len, ret;
87781Sgtb size_t nextincr, maxincr;
88781Sgtb unsigned char *p;
89781Sgtb
90781Sgtb *dsp = ds = malloc(sizeof(*ds));
91781Sgtb if (ds == NULL)
92781Sgtb return -1;
93781Sgtb
94781Sgtb ret = -1;
95781Sgtb ds->nclass = nclass;
96781Sgtb ds->ntype = ntype;
97781Sgtb ds->ansp = NULL;
98781Sgtb ds->anslen = 0;
99781Sgtb ds->ansmax = 0;
100781Sgtb nextincr = 2048;
101781Sgtb maxincr = INT_MAX;
102781Sgtb
103781Sgtb #if HAVE_NS_INITPARSE
104781Sgtb ds->cur_ans = 0;
105781Sgtb #endif
106781Sgtb
107*7934SMark.Phalan@Sun.COM #if USE_RES_NINIT
1086815Ssemery memset(&statbuf, 0, sizeof(statbuf));
109781Sgtb ret = res_ninit(&statbuf);
110*7934SMark.Phalan@Sun.COM #else
111*7934SMark.Phalan@Sun.COM ret = res_init();
112*7934SMark.Phalan@Sun.COM #endif
113781Sgtb if (ret < 0)
114781Sgtb return -1;
115781Sgtb
116781Sgtb do {
117781Sgtb p = (ds->ansp == NULL)
118781Sgtb ? malloc(nextincr) : realloc(ds->ansp, nextincr);
119781Sgtb
120781Sgtb if (p == NULL && ds->ansp != NULL) {
121781Sgtb ret = -1;
122781Sgtb goto errout;
123781Sgtb }
124781Sgtb ds->ansp = p;
125781Sgtb ds->ansmax = nextincr;
126781Sgtb
127*7934SMark.Phalan@Sun.COM #if USE_RES_NINIT
128781Sgtb len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
129781Sgtb ds->ansp, ds->ansmax);
130781Sgtb #else
131781Sgtb len = res_search(host, ds->nclass, ds->ntype,
132781Sgtb ds->ansp, ds->ansmax);
133781Sgtb #endif
134781Sgtb if (len > maxincr) {
135781Sgtb ret = -1;
136781Sgtb goto errout;
137781Sgtb }
138781Sgtb while (nextincr < len)
139781Sgtb nextincr *= 2;
140781Sgtb if (len < 0 || nextincr > maxincr) {
141781Sgtb ret = -1;
142781Sgtb goto errout;
143781Sgtb }
144781Sgtb } while (len > ds->ansmax);
145781Sgtb
146781Sgtb ds->anslen = len;
147781Sgtb #if HAVE_NS_INITPARSE
148781Sgtb ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
149781Sgtb #else
150781Sgtb ret = initparse(ds);
151781Sgtb #endif
152781Sgtb if (ret < 0)
153781Sgtb goto errout;
154781Sgtb
155781Sgtb ret = 0;
156781Sgtb
157781Sgtb errout:
158*7934SMark.Phalan@Sun.COM #if USE_RES_NINIT
159781Sgtb res_ndestroy(&statbuf);
160781Sgtb #endif
161781Sgtb if (ret < 0) {
162781Sgtb if (ds->ansp != NULL) {
163781Sgtb free(ds->ansp);
164781Sgtb ds->ansp = NULL;
165781Sgtb }
166781Sgtb }
167781Sgtb
168781Sgtb return ret;
169781Sgtb }
170781Sgtb
171781Sgtb #if HAVE_NS_INITPARSE
172781Sgtb /*
173781Sgtb * krb5int_dns_nextans - get next matching answer record
174781Sgtb *
175781Sgtb * Sets pp to NULL if no more records. Returns -1 on error, 0 on
176781Sgtb * success.
177781Sgtb */
178781Sgtb int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)179781Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds,
180781Sgtb const unsigned char **pp, int *lenp)
181781Sgtb {
182781Sgtb int len;
183781Sgtb ns_rr rr;
184781Sgtb
185781Sgtb *pp = NULL;
186781Sgtb *lenp = 0;
187781Sgtb while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
188781Sgtb len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
189781Sgtb if (len < 0)
190781Sgtb return -1;
191781Sgtb ds->cur_ans++;
192781Sgtb if (ds->nclass == ns_rr_class(rr)
193781Sgtb && ds->ntype == ns_rr_type(rr)) {
194781Sgtb *pp = ns_rr_rdata(rr);
195781Sgtb *lenp = ns_rr_rdlen(rr);
196781Sgtb return 0;
197781Sgtb }
198781Sgtb }
199781Sgtb return 0;
200781Sgtb }
201781Sgtb #endif
202781Sgtb
203781Sgtb /*
204781Sgtb * krb5int_dns_expand - wrapper for dn_expand()
205781Sgtb */
krb5int_dns_expand(struct krb5int_dns_state * ds,const unsigned char * p,char * buf,int len)206781Sgtb int krb5int_dns_expand(struct krb5int_dns_state *ds,
207781Sgtb const unsigned char *p,
208781Sgtb char *buf, int len)
209781Sgtb {
210781Sgtb
211781Sgtb #if HAVE_NS_NAME_UNCOMPRESS
212781Sgtb return ns_name_uncompress(ds->ansp,
213781Sgtb (unsigned char *)ds->ansp + ds->anslen,
214781Sgtb p, buf, (size_t)len);
215781Sgtb #else
216781Sgtb return dn_expand(ds->ansp,
217781Sgtb (unsigned char *)ds->ansp + ds->anslen,
218781Sgtb p, buf, len);
219781Sgtb #endif
220781Sgtb }
221781Sgtb
222781Sgtb /*
223781Sgtb * Free stuff.
224781Sgtb */
225781Sgtb void
krb5int_dns_fini(struct krb5int_dns_state * ds)226781Sgtb krb5int_dns_fini(struct krb5int_dns_state *ds)
227781Sgtb {
228781Sgtb if (ds == NULL)
229781Sgtb return;
230781Sgtb if (ds->ansp != NULL)
231781Sgtb free(ds->ansp);
232781Sgtb free(ds);
233781Sgtb }
234781Sgtb
235781Sgtb /*
236781Sgtb * Compat routines for BIND 4
237781Sgtb */
238781Sgtb #if !HAVE_NS_INITPARSE
239781Sgtb
240781Sgtb /*
241781Sgtb * initparse
242781Sgtb *
243781Sgtb * Skip header and question section of reply. Set a pointer to the
244781Sgtb * beginning of the answer section, and prepare to iterate over
245781Sgtb * answer records.
246781Sgtb */
247781Sgtb static int
initparse(struct krb5int_dns_state * ds)248781Sgtb initparse(struct krb5int_dns_state *ds)
249781Sgtb {
250781Sgtb HEADER *hdr;
251781Sgtb unsigned char *p;
252781Sgtb unsigned short nqueries, nanswers;
253781Sgtb int len;
254781Sgtb #if !HAVE_DN_SKIPNAME
255781Sgtb char host[MAXDNAME];
256781Sgtb #endif
257781Sgtb
258781Sgtb if (ds->anslen < sizeof(HEADER))
259781Sgtb return -1;
260781Sgtb
261781Sgtb hdr = (HEADER *)ds->ansp;
262781Sgtb p = ds->ansp;
263781Sgtb nqueries = ntohs((unsigned short)hdr->qdcount);
264781Sgtb nanswers = ntohs((unsigned short)hdr->ancount);
265781Sgtb p += sizeof(HEADER);
266781Sgtb
267781Sgtb /*
268781Sgtb * Skip query records.
269781Sgtb */
270781Sgtb while (nqueries--) {
271781Sgtb #if HAVE_DN_SKIPNAME
272781Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
273781Sgtb #else
274781Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
275781Sgtb p, host, sizeof(host));
276781Sgtb #endif
277781Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
278781Sgtb return -1;
279781Sgtb p += len + 4;
280781Sgtb }
281781Sgtb ds->ptr = p;
282781Sgtb ds->nanswers = nanswers;
283781Sgtb return 0;
284781Sgtb }
285781Sgtb
286781Sgtb /*
287781Sgtb * krb5int_dns_nextans() - get next answer record
288781Sgtb *
289781Sgtb * Sets pp to NULL if no more records.
290781Sgtb */
291781Sgtb int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)292781Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds,
293781Sgtb const unsigned char **pp, int *lenp)
294781Sgtb {
295781Sgtb int len;
296781Sgtb unsigned char *p;
297781Sgtb unsigned short ntype, nclass, rdlen;
298781Sgtb #if !HAVE_DN_SKIPNAME
299781Sgtb char host[MAXDNAME];
300781Sgtb #endif
301781Sgtb
302781Sgtb *pp = NULL;
303781Sgtb *lenp = 0;
304781Sgtb p = ds->ptr;
305781Sgtb
306781Sgtb while (ds->nanswers--) {
307781Sgtb #if HAVE_DN_SKIPNAME
308781Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
309781Sgtb #else
310781Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
311781Sgtb p, host, sizeof(host));
312781Sgtb #endif
313781Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
314781Sgtb return -1;
315781Sgtb p += len;
316781Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
317781Sgtb /* Also skip 4 bytes of TTL */
318781Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
319781Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
320781Sgtb
321781Sgtb if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
322781Sgtb return -1;
323*7934SMark.Phalan@Sun.COM /* Solaris Kerberos - resync */
324*7934SMark.Phalan@Sun.COM #if 0
325*7934SMark.Phalan@Sun.COM if (rdlen > INT_MAX)
326*7934SMark.Phalan@Sun.COM return -1;
327*7934SMark.Phalan@Sun.COM #endif
328781Sgtb if (nclass == ds->nclass && ntype == ds->ntype) {
329781Sgtb *pp = p;
330781Sgtb *lenp = rdlen;
331781Sgtb ds->ptr = p + rdlen;
332781Sgtb return 0;
333781Sgtb }
334781Sgtb p += rdlen;
335781Sgtb }
336781Sgtb return 0;
337781Sgtb out:
338781Sgtb return -1;
339781Sgtb }
340781Sgtb
341781Sgtb #endif
342781Sgtb
343781Sgtb #endif /* KRB5_DNS_LOOKUP */
344