1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/dnsglue.c */
4 * Copyright 2004, 2009 by the Massachusetts Institute of Technology.
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.
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.
28 #ifdef KRB5_DNS_LOOKUP
33 * Only use res_ninit() if there's also a res_ndestroy(), to avoid
34 * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
35 * 5.x). While we're at it, make sure res_nsearch() is there too.
37 * In any case, it is probable that platforms having broken
38 * res_ninit() will have thread safety hacks for res_init() and _res.
40 #if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH
41 #define USE_RES_NINIT 1
47 struct krb5int_dns_state {
58 unsigned short nanswers;
62 #if !HAVE_NS_INITPARSE
63 static int initparse(struct krb5int_dns_state *);
69 * Initialize an opaque handle. Do name lookup and initial parsing of
70 * reply, skipping question section. Prepare to iterate over answer
71 * section. Returns -1 on error, 0 on success.
74 krb5int_dns_init(struct krb5int_dns_state **dsp,
75 char *host, int nclass, int ntype)
78 struct __res_state statbuf;
80 struct krb5int_dns_state *ds;
82 size_t nextincr, maxincr;
85 *dsp = ds = malloc(sizeof(*ds));
103 memset(&statbuf, 0, sizeof(statbuf));
104 ret = res_ninit(&statbuf);
112 p = (ds->ansp == NULL)
113 ? malloc(nextincr) : realloc(ds->ansp, nextincr);
120 ds->ansmax = nextincr;
123 len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
124 ds->ansp, ds->ansmax);
126 len = res_search(host, ds->nclass, ds->ntype,
127 ds->ansp, ds->ansmax);
129 if ((size_t) len > maxincr) {
133 while (nextincr < (size_t) len)
135 if (len < 0 || nextincr > maxincr) {
139 } while (len > ds->ansmax);
142 #if HAVE_NS_INITPARSE
143 ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
154 res_ndestroy(&statbuf);
157 if (ds->ansp != NULL) {
166 #if HAVE_NS_INITPARSE
168 * krb5int_dns_nextans - get next matching answer record
170 * Sets pp to NULL if no more records. Returns -1 on error, 0 on
174 krb5int_dns_nextans(struct krb5int_dns_state *ds,
175 const unsigned char **pp, int *lenp)
182 while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
183 len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
187 if (ds->nclass == (int)ns_rr_class(rr)
188 && ds->ntype == (int)ns_rr_type(rr)) {
189 *pp = ns_rr_rdata(rr);
190 *lenp = ns_rr_rdlen(rr);
199 * krb5int_dns_expand - wrapper for dn_expand()
202 krb5int_dns_expand(struct krb5int_dns_state *ds, const unsigned char *p,
206 #if HAVE_NS_NAME_UNCOMPRESS
207 return ns_name_uncompress(ds->ansp,
208 (unsigned char *)ds->ansp + ds->anslen,
209 p, buf, (size_t)len);
211 return dn_expand(ds->ansp,
212 (unsigned char *)ds->ansp + ds->anslen,
221 krb5int_dns_fini(struct krb5int_dns_state *ds)
225 if (ds->ansp != NULL)
231 * Compat routines for BIND 4
233 #if !HAVE_NS_INITPARSE
238 * Skip header and question section of reply. Set a pointer to the
239 * beginning of the answer section, and prepare to iterate over
243 initparse(struct krb5int_dns_state *ds)
247 unsigned short nqueries, nanswers;
249 #if !HAVE_DN_SKIPNAME
253 if ((size_t) ds->anslen < sizeof(HEADER))
256 hdr = (HEADER *)ds->ansp;
258 nqueries = ntohs((unsigned short)hdr->qdcount);
259 nanswers = ntohs((unsigned short)hdr->ancount);
263 * Skip query records.
267 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
269 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
270 p, host, sizeof(host));
272 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
277 ds->nanswers = nanswers;
282 * krb5int_dns_nextans() - get next answer record
284 * Sets pp to NULL if no more records.
287 krb5int_dns_nextans(struct krb5int_dns_state *ds,
288 const unsigned char **pp, int *lenp)
292 unsigned short ntype, nclass, rdlen;
293 #if !HAVE_DN_SKIPNAME
301 while (ds->nanswers--) {
303 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
305 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
306 p, host, sizeof(host));
308 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
311 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
312 /* Also skip 4 bytes of TTL */
313 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
314 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
316 if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
320 if (nclass == ds->nclass && ntype == ds->ntype) {
336 * Try to look up a TXT record pointing to a Kerberos realm
340 krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
342 krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN;
343 const unsigned char *p, *base;
346 struct krb5int_dns_state *ds = NULL;
350 * Form our query, and send it via DNS
353 krb5int_buf_init_fixed(&buf, host, sizeof(host));
354 if (name == NULL || name[0] == '\0') {
355 krb5int_buf_add(&buf, prefix);
357 krb5int_buf_add_fmt(&buf, "%s.%s", prefix, name);
359 /* Realm names don't (normally) end with ".", but if the query
360 doesn't end with "." and doesn't get an answer as is, the
361 resolv code will try appending the local domain. Since the
362 realm names are absolutes, let's stop that.
364 But only if a name has been specified. If we are performing
365 a search on the prefix alone then the intention is to allow
366 the local domain or domain search lists to be expanded.
369 len = krb5int_buf_len(&buf);
370 if (len > 0 && host[len - 1] != '.')
371 krb5int_buf_add(&buf, ".");
373 if (krb5int_buf_data(&buf) == NULL)
374 return KRB5_ERR_HOST_REALM_UNKNOWN;
375 ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
379 ret = krb5int_dns_nextans(ds, &base, &rdlen);
380 if (ret < 0 || base == NULL)
384 if (!INCR_OK(base, rdlen, p, 1))
387 *realm = malloc((size_t)len + 1);
388 if (*realm == NULL) {
392 strncpy(*realm, (const char *)p, (size_t)len);
393 (*realm)[len] = '\0';
394 /* Avoid a common error. */
395 if ( (*realm)[len-1] == '.' )
396 (*realm)[len-1] = '\0';
401 krb5int_dns_fini(ds);
407 #endif /* KRB5_DNS_LOOKUP */