1 #if defined(LIBC_SCCS) && !defined(lint)
2 static const char rcsid
[] = "$Id: hesiod.c,v 1.7 2005/07/28 06:51:48 marka Exp $";
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1996,1999 by Internet Software Consortium.
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * hesiod.c --- the core portion of the hesiod resolver.
27 * This file is derived from the hesiod library from Project Athena;
28 * It has been extensively rewritten by Theodore Ts'o to have a more
29 * thread-safe interface.
31 * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
36 #include "port_before.h"
38 #include <sys/types.h>
39 #include <netinet/in.h>
40 #include <arpa/nameser.h>
49 #include "port_after.h"
51 #include "pathnames.h"
57 int hesiod_init(void **context
);
58 void hesiod_end(void *context
);
59 char * hesiod_to_bind(void *context
, const char *name
,
61 char ** hesiod_resolve(void *context
, const char *name
,
63 void hesiod_free_list(void *context
, char **list
);
65 static int parse_config_file(struct hesiod_p
*ctx
, const char *filename
);
66 static char ** get_txt_records(struct hesiod_p
*ctx
, int class,
68 static int init(struct hesiod_p
*ctx
);
73 * This function is called to initialize a hesiod_p.
76 hesiod_init(void **context
) {
80 ctx
= malloc(sizeof(struct hesiod_p
));
86 memset(ctx
, 0, sizeof (*ctx
));
88 if (parse_config_file(ctx
, _PATH_HESIOD_CONF
) < 0) {
91 * Use compiled in defaults.
93 ctx
->LHS
= malloc(strlen(DEF_LHS
) + 1);
94 ctx
->RHS
= malloc(strlen(DEF_RHS
) + 1);
95 if (ctx
->LHS
== NULL
|| ctx
->RHS
== NULL
) {
99 strcpy(ctx
->LHS
, DEF_LHS
); /* (checked) */
100 strcpy(ctx
->RHS
, DEF_RHS
); /* (checked) */
106 * The default RHS can be overridden by an environment
109 if ((cp
= getenv("HES_DOMAIN")) != NULL
) {
110 size_t RHSlen
= strlen(cp
) + 2;
112 ctx
->RHS
= malloc(RHSlen
);
118 strcpy(ctx
->RHS
, cp
); /* (checked) */
120 strcpy(ctx
->RHS
, "."); /* (checked) */
121 strcat(ctx
->RHS
, cp
); /* (checked) */
126 * If there is no default hesiod realm set, we return an
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
);
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
;
176 /* Decide what our RHS is, and set cp to the end of the actual name. */
177 if ((cp
= strchr(name
, '@')) != NULL
) {
178 if (strchr(cp
+ 1, '.'))
180 else if ((rhs_list
= hesiod_resolve(context
, cp
+ 1,
181 "rhs-extension")) != NULL
)
189 cp
= name
+ strlen(name
);
193 * Allocate the space we need, including up to three periods and
194 * the terminating NUL.
196 if ((bindname
= malloc((cp
- name
) + strlen(type
) + strlen(RHS
) +
197 (ctx
->LHS
? strlen(ctx
->LHS
) : 0) + 4)) == NULL
) {
200 hesiod_free_list(context
, rhs_list
);
204 /* Now put together the DNS name. */
205 memcpy(bindname
, name
, cp
- name
);
206 bindname
[cp
- name
] = '\0';
207 strcat(bindname
, ".");
208 strcat(bindname
, type
);
210 if (ctx
->LHS
[0] != '.')
211 strcat(bindname
, ".");
212 strcat(bindname
, ctx
->LHS
);
215 strcat(bindname
, ".");
216 strcat(bindname
, RHS
);
219 hesiod_free_list(context
, rhs_list
);
225 * This is the core function. Given a hesiod (name, type), it
226 * returns an array of strings returned by the resolver.
229 hesiod_resolve(void *context
, const char *name
, const char *type
) {
230 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
231 char *bindname
= hesiod_to_bind(context
, name
, type
);
234 if (bindname
== NULL
)
236 if (init(ctx
) == -1) {
241 if ((retvec
= get_txt_records(ctx
, C_IN
, bindname
))) {
249 retvec
= get_txt_records(ctx
, C_HS
, bindname
);
255 hesiod_free_list(void *context
, char **list
) {
260 for (p
= list
; *p
; p
++)
266 * This function parses the /etc/hesiod.conf file
269 parse_config_file(struct hesiod_p
*ctx
, const char *filename
) {
270 char *key
, *data
, *cp
, **cpp
;
271 char buf
[MAXDNAME
+7];
275 * Clear the existing configuration variable, just in case
280 ctx
->RHS
= ctx
->LHS
= 0;
283 * Now open and parse the file...
285 if (!(fp
= fopen(filename
, "r")))
288 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
290 if (*cp
== '#' || *cp
== '\n' || *cp
== '\r')
292 while(*cp
== ' ' || *cp
== '\t')
295 while(*cp
!= ' ' && *cp
!= '\t' && *cp
!= '=')
299 while(*cp
== ' ' || *cp
== '\t' || *cp
== '=')
302 while(*cp
!= ' ' && *cp
!= '\n' && *cp
!= '\r')
306 if (strcmp(key
, "lhs") == 0)
308 else if (strcmp(key
, "rhs") == 0)
313 *cpp
= malloc(strlen(data
) + 1);
327 ctx
->RHS
= ctx
->LHS
= 0;
332 * Given a DNS class and a DNS name, do a lookup for TXT records, and
333 * return a list of them.
336 get_txt_records(struct hesiod_p
*ctx
, int class, const char *name
) {
338 int type
; /*%< RR type */
339 int class; /*%< RR class */
340 int dlen
; /*%< len of data section */
341 u_char
*data
; /*%< pointer to data */
344 u_char qbuf
[MAX_HESRESP
], abuf
[MAX_HESRESP
];
345 u_char
*cp
, *erdata
, *eom
;
346 char *dst
, *edst
, **list
;
347 int ancount
, qdcount
;
351 * Construct the query and send it.
353 n
= res_nmkquery(ctx
->res
, QUERY
, name
, class, T_TXT
, NULL
, 0,
354 NULL
, qbuf
, MAX_HESRESP
);
359 n
= res_nsend(ctx
->res
, qbuf
, n
, abuf
, MAX_HESRESP
);
361 errno
= ECONNREFUSED
;
370 * OK, parse the result.
372 hp
= (HEADER
*) abuf
;
373 ancount
= ntohs(hp
->ancount
);
374 qdcount
= ntohs(hp
->qdcount
);
375 cp
= abuf
+ sizeof(HEADER
);
378 /* Skip query, trying to get to the answer section which follows. */
379 for (i
= 0; i
< qdcount
; i
++) {
380 skip
= dn_skipname(cp
, eom
);
381 if (skip
< 0 || cp
+ skip
+ QFIXEDSZ
> eom
) {
385 cp
+= skip
+ QFIXEDSZ
;
388 list
= malloc((ancount
+ 1) * sizeof(char *));
394 for (i
= 0; i
< ancount
; i
++) {
395 skip
= dn_skipname(cp
, eom
);
401 if (cp
+ 3 * INT16SZ
+ INT32SZ
> eom
) {
405 rr
.type
= ns_get16(cp
);
407 rr
.class = ns_get16(cp
);
408 cp
+= INT16SZ
+ INT32SZ
; /*%< skip the ttl, too */
409 rr
.dlen
= ns_get16(cp
);
411 if (cp
+ rr
.dlen
> eom
) {
417 if (rr
.class != class || rr
.type
!= T_TXT
)
419 if (!(list
[j
] = malloc(rr
.dlen
)))
422 edst
= dst
+ rr
.dlen
;
423 erdata
= rr
.data
+ rr
.dlen
;
425 while (cp
< erdata
) {
426 n
= (unsigned char) *cp
++;
427 if (cp
+ n
> eom
|| dst
+ n
> edst
) {
449 for (i
= 0; i
< j
; i
++)
456 __hesiod_res_get(void *context
) {
457 struct hesiod_p
*ctx
= context
;
460 struct __res_state
*res
;
461 res
= (struct __res_state
*)malloc(sizeof *res
);
466 memset(res
, 0, sizeof *res
);
467 __hesiod_res_set(ctx
, res
, free
);
474 __hesiod_res_set(void *context
, struct __res_state
*res
,
475 void (*free_res
)(void *)) {
476 struct hesiod_p
*ctx
= context
;
478 if (ctx
->res
&& ctx
->free_res
) {
479 res_nclose(ctx
->res
);
480 (*ctx
->free_res
)(ctx
->res
);
484 ctx
->free_res
= free_res
;
488 init(struct hesiod_p
*ctx
) {
490 if (!ctx
->res
&& !__hesiod_res_get(ctx
))
493 if (((ctx
->res
->options
& RES_INIT
) == 0U) &&
494 (res_ninit(ctx
->res
) == -1))