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
36 * Only use res_ninit() if there's also a res_ndestroy(), to avoid
37 * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
38 * 5.x). While we're at it, make sure res_nsearch() is there too.
40 * In any case, it is probable that platforms having broken
41 * res_ninit() will have thread safety hacks for res_init() and _res.
47 struct krb5int_dns_state {
58 unsigned short nanswers;
62 #if !HAVE_NS_INITPARSE
63 static int initparse(struct krb5int_dns_state *);
67 * Define macros to use the best available DNS search functions. INIT_HANDLE()
68 * returns true if handle initialization is successful, false if it is not.
69 * SEARCH() returns the length of the response or -1 on error.
70 * DECLARE_HANDLE() must be used last in the declaration list since it may
71 * evaluate to nothing.
74 #if defined(__APPLE__)
76 /* Use the OS X interfaces dns_open, dns_search, and dns_free. */
77 #define DECLARE_HANDLE(h) dns_handle_t h
78 #define INIT_HANDLE(h) ((h = dns_open(NULL)) != NULL)
79 #define SEARCH(h, n, c, t, a, l) dns_search(h, n, c, t, a, l, NULL, NULL)
80 #define DESTROY_HANDLE(h) dns_free(h)
82 #elif HAVE_RES_NINIT && HAVE_RES_NSEARCH
84 /* Use res_ninit, res_nsearch, and res_ndestroy or res_nclose. */
85 #define DECLARE_HANDLE(h) struct __res_state h
86 #define INIT_HANDLE(h) (memset(&h, 0, sizeof(h)), res_ninit(&h) == 0)
87 #define SEARCH(h, n, c, t, a, l) res_nsearch(&h, n, c, t, a, l)
89 #define DESTROY_HANDLE(h) res_ndestroy(&h)
91 #define DESTROY_HANDLE(h) res_nclose(&h)
96 /* Use res_init and res_search. */
97 #define DECLARE_HANDLE(h)
98 #define INIT_HANDLE(h) (res_init() == 0)
99 #define SEARCH(h, n, c, t, a, l) res_search(n, c, t, a, l)
100 #define DESTROY_HANDLE(h)
107 * Initialize an opaque handle. Do name lookup and initial parsing of
108 * reply, skipping question section. Prepare to iterate over answer
109 * section. Returns -1 on error, 0 on success.
112 krb5int_dns_init(struct krb5int_dns_state **dsp,
113 char *host, int nclass, int ntype)
115 struct krb5int_dns_state *ds;
117 size_t nextincr, maxincr;
121 *dsp = ds = malloc(sizeof(*ds));
134 #if HAVE_NS_INITPARSE
142 p = (ds->ansp == NULL)
143 ? malloc(nextincr) : realloc(ds->ansp, nextincr);
150 ds->ansmax = nextincr;
152 len = SEARCH(h, host, ds->nclass, ds->ntype, ds->ansp, ds->ansmax);
153 if ((size_t) len > maxincr) {
157 while (nextincr < (size_t) len)
159 if (len < 0 || nextincr > maxincr) {
163 } while (len > ds->ansmax);
166 #if HAVE_NS_INITPARSE
167 ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
179 if (ds->ansp != NULL) {
188 #if HAVE_NS_INITPARSE
190 * krb5int_dns_nextans - get next matching answer record
192 * Sets pp to NULL if no more records. Returns -1 on error, 0 on
196 krb5int_dns_nextans(struct krb5int_dns_state *ds,
197 const unsigned char **pp, int *lenp)
204 while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
205 len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
209 if (ds->nclass == (int)ns_rr_class(rr)
210 && ds->ntype == (int)ns_rr_type(rr)) {
211 *pp = ns_rr_rdata(rr);
212 *lenp = ns_rr_rdlen(rr);
221 * krb5int_dns_expand - wrapper for dn_expand()
224 krb5int_dns_expand(struct krb5int_dns_state *ds, const unsigned char *p,
228 #if HAVE_NS_NAME_UNCOMPRESS
229 return ns_name_uncompress(ds->ansp,
230 (unsigned char *)ds->ansp + ds->anslen,
231 p, buf, (size_t)len);
233 return dn_expand(ds->ansp,
234 (unsigned char *)ds->ansp + ds->anslen,
243 krb5int_dns_fini(struct krb5int_dns_state *ds)
247 if (ds->ansp != NULL)
253 * Compat routines for BIND 4
255 #if !HAVE_NS_INITPARSE
260 * Skip header and question section of reply. Set a pointer to the
261 * beginning of the answer section, and prepare to iterate over
265 initparse(struct krb5int_dns_state *ds)
269 unsigned short nqueries, nanswers;
271 #if !HAVE_DN_SKIPNAME
275 if ((size_t) ds->anslen < sizeof(HEADER))
278 hdr = (HEADER *)ds->ansp;
280 nqueries = ntohs((unsigned short)hdr->qdcount);
281 nanswers = ntohs((unsigned short)hdr->ancount);
285 * Skip query records.
289 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
291 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
292 p, host, sizeof(host));
294 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
299 ds->nanswers = nanswers;
304 * krb5int_dns_nextans() - get next answer record
306 * Sets pp to NULL if no more records.
309 krb5int_dns_nextans(struct krb5int_dns_state *ds,
310 const unsigned char **pp, int *lenp)
314 unsigned short ntype, nclass, rdlen;
315 #if !HAVE_DN_SKIPNAME
323 while (ds->nanswers--) {
325 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
327 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
328 p, host, sizeof(host));
330 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
333 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
334 /* Also skip 4 bytes of TTL */
335 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
336 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
338 if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
342 if (nclass == ds->nclass && ntype == ds->ntype) {
358 * Try to look up a TXT record pointing to a Kerberos realm
362 k5_try_realm_txt_rr(krb5_context context, const char *prefix, const char *name,
365 krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN;
366 const unsigned char *p, *base;
369 struct krb5int_dns_state *ds = NULL;
373 * Form our query, and send it via DNS
376 k5_buf_init_fixed(&buf, host, sizeof(host));
377 if (name == NULL || name[0] == '\0') {
378 k5_buf_add(&buf, prefix);
380 k5_buf_add_fmt(&buf, "%s.%s", prefix, name);
382 /* Realm names don't (normally) end with ".", but if the query
383 doesn't end with "." and doesn't get an answer as is, the
384 resolv code will try appending the local domain. Since the
385 realm names are absolutes, let's stop that.
387 But only if a name has been specified. If we are performing
388 a search on the prefix alone then the intention is to allow
389 the local domain or domain search lists to be expanded.
392 if (buf.len > 0 && host[buf.len - 1] != '.')
393 k5_buf_add(&buf, ".");
395 if (k5_buf_status(&buf) != 0)
396 return KRB5_ERR_HOST_REALM_UNKNOWN;
397 ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
399 TRACE_TXT_LOOKUP_NOTFOUND(context, host);
403 ret = krb5int_dns_nextans(ds, &base, &rdlen);
404 if (ret < 0 || base == NULL)
408 if (!INCR_OK(base, rdlen, p, 1))
411 *realm = malloc((size_t)len + 1);
412 if (*realm == NULL) {
416 strncpy(*realm, (const char *)p, (size_t)len);
417 (*realm)[len] = '\0';
418 /* Avoid a common error. */
419 if ( (*realm)[len-1] == '.' )
420 (*realm)[len-1] = '\0';
422 TRACE_TXT_LOOKUP_SUCCESS(context, host, *realm);
426 krb5int_dns_fini(ds);
432 #endif /* KRB5_DNS_LOOKUP */