1 #if defined(LIBC_SCCS) && !defined(lint)
2 static const char rcsid
[] = "$BINDId: hesiod.c,v 1.21 2000/02/28 14:51:08 vixie Exp $";
6 * Copyright (c) 1996,1999 by Internet Software Consortium.
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
27 * hesiod.c --- the core portion of the hesiod resolver.
29 * This file is derived from the hesiod library from Project Athena;
30 * It has been extensively rewritten by Theodore Ts'o to have a more
31 * thread-safe interface.
36 #include <sys/types.h>
37 #include <netinet/in.h>
38 #include <arpa/nameser.h>
51 #define _PATH_HESIOD_CONF "/etc/hesiod.conf"
55 int hesiod_init(void **context
);
56 void hesiod_end(void *context
);
57 char * hesiod_to_bind(void *context
, const char *name
,
59 char ** hesiod_resolve(void *context
, const char *name
,
61 void hesiod_free_list(void *context
, char **list
);
63 static int parse_config_file(struct hesiod_p
*ctx
, const char *filename
);
64 static char ** get_txt_records(struct hesiod_p
*ctx
, int class,
66 static int init(struct hesiod_p
*ctx
);
71 * This function is called to initialize a hesiod_p.
74 hesiod_init(void **context
) {
76 const char *configname
;
79 ctx
= malloc(sizeof(struct hesiod_p
));
86 /* Set default query classes. */
87 ctx
->classes
[0] = C_IN
;
88 ctx
->classes
[1] = C_HS
;
90 configname
= __libc_secure_getenv("HESIOD_CONFIG");
92 configname
= _PATH_HESIOD_CONF
;
93 if (parse_config_file(ctx
, configname
) < 0) {
96 * Use compiled in defaults.
98 ctx
->LHS
= malloc(strlen(DEF_LHS
)+1);
99 ctx
->RHS
= malloc(strlen(DEF_RHS
)+1);
100 if (ctx
->LHS
== 0 || ctx
->RHS
== 0)
102 strcpy(ctx
->LHS
, DEF_LHS
);
103 strcpy(ctx
->RHS
, DEF_RHS
);
109 * The default RHS can be overridden by an environment
112 if ((cp
= __libc_secure_getenv("HES_DOMAIN")) != NULL
) {
114 ctx
->RHS
= malloc(strlen(cp
)+2);
118 strcpy(ctx
->RHS
, cp
);
121 strcpy(ctx
->RHS
+ 1, cp
);
126 * If there is no default hesiod realm set, we return an
130 __set_errno(ENOEXEC
);
135 if (res_ninit(ctx
->res
) < 0)
148 * This function deallocates the hesiod_p
151 hesiod_end(void *context
) {
152 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
153 int save_errno
= errno
;
156 res_nclose(ctx
->res
);
159 if (ctx
->res
&& ctx
->free_res
)
160 (*ctx
->free_res
)(ctx
->res
);
162 __set_errno(save_errno
);
166 * This function takes a hesiod (name, type) and returns a DNS
167 * name which is to be resolved.
170 hesiod_to_bind(void *context
, const char *name
, const char *type
) {
171 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
173 char **rhs_list
= NULL
;
174 const char *RHS
, *cp
;
177 /* Decide what our RHS is, and set cp to the end of the actual name. */
178 if ((cp
= strchr(name
, '@')) != NULL
) {
179 if (strchr(cp
+ 1, '.'))
181 else if ((rhs_list
= hesiod_resolve(context
, cp
+ 1,
182 "rhs-extension")) != NULL
)
190 cp
= name
+ strlen(name
);
194 * Allocate the space we need, including up to three periods and
195 * the terminating NUL.
197 if ((bindname
= malloc((cp
- name
) + strlen(type
) + strlen(RHS
) +
198 (ctx
->LHS
? strlen(ctx
->LHS
) : 0) + 4)) == NULL
) {
200 hesiod_free_list(context
, rhs_list
);
204 /* Now put together the DNS name. */
205 endp
= (char *) __mempcpy (bindname
, name
, cp
- name
);
207 endp
= (char *) __stpcpy (endp
, type
);
209 if (ctx
->LHS
[0] != '.')
211 endp
= __stpcpy (endp
, ctx
->LHS
);
218 hesiod_free_list(context
, rhs_list
);
224 * This is the core function. Given a hesiod (name, type), it
225 * returns an array of strings returned by the resolver.
228 hesiod_resolve(void *context
, const char *name
, const char *type
) {
229 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
230 char *bindname
= hesiod_to_bind(context
, name
, type
);
233 if (bindname
== NULL
)
235 if (init(ctx
) == -1) {
240 retvec
= get_txt_records(ctx
, ctx
->classes
[0], bindname
);
242 if (retvec
== NULL
&& (errno
== ENOENT
|| errno
== ECONNREFUSED
) && ctx
->classes
[1])
243 retvec
= get_txt_records(ctx
, ctx
->classes
[1], bindname
);
251 hesiod_free_list(void *context
, char **list
) {
254 for (p
= list
; *p
; p
++)
260 * This function parses the /etc/hesiod.conf file
263 parse_config_file(struct hesiod_p
*ctx
, const char *filename
) {
264 char buf
[MAXDNAME
+7];
268 * Clear the existing configuration variable, just in case
273 ctx
->RHS
= ctx
->LHS
= 0;
274 /* Set default query classes. */
275 ctx
->classes
[0] = C_IN
;
276 ctx
->classes
[1] = C_HS
;
279 * Now open and parse the file...
281 if (!(fp
= fopen(filename
, "rce")))
284 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
285 char *key
, *data
, *cp
, **cpp
;
288 if (*cp
== '#' || *cp
== '\n' || *cp
== '\r')
290 while(*cp
== ' ' || *cp
== '\t')
293 while(*cp
!= ' ' && *cp
!= '\t' && *cp
!= '=')
297 while(*cp
== ' ' || *cp
== '\t' || *cp
== '=')
300 while(*cp
!= ' ' && *cp
!= '\n' && *cp
!= '\r')
305 if (strcasecmp(key
, "lhs") == 0)
307 else if (strcasecmp(key
, "rhs") == 0)
313 } else if (strcasecmp(key
, "classes") == 0) {
315 while (*data
&& n
< 2) {
316 cp
= strchrnul(data
, ',');
319 if (strcasecmp(data
, "IN") == 0)
320 ctx
->classes
[n
++] = C_IN
;
321 else if (strcasecmp(data
, "HS") == 0)
322 ctx
->classes
[n
++] = C_HS
;
326 /* Restore the default. Better than
328 ctx
->classes
[0] = C_IN
;
329 ctx
->classes
[1] = C_HS
;
331 || ctx
->classes
[0] == ctx
->classes
[1])
342 ctx
->RHS
= ctx
->LHS
= 0;
347 * Given a DNS class and a DNS name, do a lookup for TXT records, and
348 * return a list of them.
351 get_txt_records(struct hesiod_p
*ctx
, int class, const char *name
) {
353 int type
; /* RR type */
354 int class; /* RR class */
355 int dlen
; /* len of data section */
356 u_char
*data
; /* pointer to data */
359 u_char qbuf
[MAX_HESRESP
], abuf
[MAX_HESRESP
];
360 u_char
*cp
, *erdata
, *eom
;
361 char *dst
, *edst
, **list
;
362 int ancount
, qdcount
;
366 * Construct the query and send it.
368 n
= res_nmkquery(ctx
->res
, QUERY
, name
, class, T_TXT
, NULL
, 0,
369 NULL
, qbuf
, MAX_HESRESP
);
371 __set_errno(EMSGSIZE
);
374 n
= res_nsend(ctx
->res
, qbuf
, n
, abuf
, MAX_HESRESP
);
376 __set_errno(ECONNREFUSED
);
380 __set_errno(EMSGSIZE
);
385 * OK, parse the result.
387 hp
= (HEADER
*) abuf
;
388 ancount
= ntohs(hp
->ancount
);
389 qdcount
= ntohs(hp
->qdcount
);
390 cp
= abuf
+ sizeof(HEADER
);
393 /* Skip query, trying to get to the answer section which follows. */
394 for (i
= 0; i
< qdcount
; i
++) {
395 skip
= dn_skipname(cp
, eom
);
396 if (skip
< 0 || cp
+ skip
+ QFIXEDSZ
> eom
) {
397 __set_errno(EMSGSIZE
);
400 cp
+= skip
+ QFIXEDSZ
;
403 list
= malloc((ancount
+ 1) * sizeof(char *));
407 for (i
= 0; i
< ancount
; i
++) {
408 skip
= dn_skipname(cp
, eom
);
410 __set_errno(EMSGSIZE
);
414 if (cp
+ 3 * INT16SZ
+ INT32SZ
> eom
) {
415 __set_errno(EMSGSIZE
);
418 rr
.type
= ns_get16(cp
);
420 rr
.class = ns_get16(cp
);
421 cp
+= INT16SZ
+ INT32SZ
; /* skip the ttl, too */
422 rr
.dlen
= ns_get16(cp
);
424 if (cp
+ rr
.dlen
> eom
) {
425 __set_errno(EMSGSIZE
);
430 if (rr
.class != class || rr
.type
!= T_TXT
)
432 if (!(list
[j
] = malloc(rr
.dlen
)))
435 edst
= dst
+ rr
.dlen
;
436 erdata
= rr
.data
+ rr
.dlen
;
438 while (cp
< erdata
) {
439 n
= (unsigned char) *cp
++;
440 if (cp
+ n
> eom
|| dst
+ n
> edst
) {
441 __set_errno(EMSGSIZE
);
449 __set_errno(EMSGSIZE
);
462 for (i
= 0; i
< j
; i
++)
469 __hesiod_res_get(void *context
) {
470 struct hesiod_p
*ctx
= context
;
473 struct __res_state
*res
;
474 res
= (struct __res_state
*)calloc(1, sizeof *res
);
477 __hesiod_res_set(ctx
, res
, free
);
484 __hesiod_res_set(void *context
, struct __res_state
*res
,
485 void (*free_res
)(void *)) {
486 struct hesiod_p
*ctx
= context
;
488 if (ctx
->res
&& ctx
->free_res
) {
489 res_nclose(ctx
->res
);
490 (*ctx
->free_res
)(ctx
->res
);
494 ctx
->free_res
= free_res
;
498 init(struct hesiod_p
*ctx
) {
500 if (!ctx
->res
&& !__hesiod_res_get(ctx
))
503 if (__res_maybe_init (ctx
->res
, 0) == -1)