1 /* Copyright (C) 1997-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
19 * Copyright (c) 1996,1999 by Internet Software Consortium.
21 * Permission to use, copy, modify, and distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the above
23 * copyright notice and this permission notice appear in all copies.
25 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
26 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
28 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
29 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
30 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
31 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
36 * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
40 * hesiod.c --- the core portion of the hesiod resolver.
42 * This file is derived from the hesiod library from Project Athena;
43 * It has been extensively rewritten by Theodore Ts'o to have a more
44 * thread-safe interface.
49 #include <sys/types.h>
50 #include <netinet/in.h>
51 #include <arpa/nameser.h>
63 #define _PATH_HESIOD_CONF "/etc/hesiod.conf"
67 static int parse_config_file(struct hesiod_p
*ctx
, const char *filename
);
68 static char ** get_txt_records(struct hesiod_p
*ctx
, int class,
74 * This function is called to initialize a hesiod_p.
77 hesiod_init(void **context
) {
79 const char *configname
;
82 ctx
= malloc(sizeof(struct hesiod_p
));
88 /* Set default query classes. */
89 ctx
->classes
[0] = C_IN
;
90 ctx
->classes
[1] = C_HS
;
92 configname
= __libc_secure_getenv("HESIOD_CONFIG");
94 configname
= _PATH_HESIOD_CONF
;
95 if (parse_config_file(ctx
, configname
) < 0) {
99 * The default RHS can be overridden by an environment
102 if ((cp
= __libc_secure_getenv("HES_DOMAIN")) != NULL
) {
104 ctx
->RHS
= malloc(strlen(cp
)+2);
108 strcpy(ctx
->RHS
, cp
);
111 strcpy(ctx
->RHS
+ 1, cp
);
116 * If there is no default hesiod realm set, we return an
120 __set_errno(ENOEXEC
);
133 * This function deallocates the hesiod_p
136 hesiod_end(void *context
) {
137 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
138 int save_errno
= errno
;
143 __set_errno(save_errno
);
147 * This function takes a hesiod (name, type) and returns a DNS
148 * name which is to be resolved.
151 hesiod_to_bind(void *context
, const char *name
, const char *type
) {
152 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
154 char **rhs_list
= NULL
;
155 const char *RHS
, *cp
;
158 /* Decide what our RHS is, and set cp to the end of the actual name. */
159 if ((cp
= strchr(name
, '@')) != NULL
) {
160 if (strchr(cp
+ 1, '.'))
162 else if ((rhs_list
= hesiod_resolve(context
, cp
+ 1,
163 "rhs-extension")) != NULL
)
171 cp
= name
+ strlen(name
);
175 * Allocate the space we need, including up to three periods and
176 * the terminating NUL.
178 if ((bindname
= malloc((cp
- name
) + strlen(type
) + strlen(RHS
) +
179 (ctx
->LHS
? strlen(ctx
->LHS
) : 0) + 4)) == NULL
) {
181 hesiod_free_list(context
, rhs_list
);
185 /* Now put together the DNS name. */
186 endp
= (char *) __mempcpy (bindname
, name
, cp
- name
);
188 endp
= (char *) __stpcpy (endp
, type
);
190 if (ctx
->LHS
[0] != '.')
192 endp
= __stpcpy (endp
, ctx
->LHS
);
199 hesiod_free_list(context
, rhs_list
);
205 * This is the core function. Given a hesiod (name, type), it
206 * returns an array of strings returned by the resolver.
209 hesiod_resolve(void *context
, const char *name
, const char *type
) {
210 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
211 char *bindname
= hesiod_to_bind(context
, name
, type
);
214 if (bindname
== NULL
)
217 retvec
= get_txt_records(ctx
, ctx
->classes
[0], bindname
);
219 if (retvec
== NULL
&& (errno
== ENOENT
|| errno
== ECONNREFUSED
) && ctx
->classes
[1])
220 retvec
= get_txt_records(ctx
, ctx
->classes
[1], bindname
);
228 hesiod_free_list(void *context
, char **list
) {
231 for (p
= list
; *p
; p
++)
237 * This function parses the /etc/hesiod.conf file
240 parse_config_file(struct hesiod_p
*ctx
, const char *filename
) {
241 char buf
[MAXDNAME
+7];
245 * Clear the existing configuration variable, just in case
250 ctx
->RHS
= ctx
->LHS
= 0;
251 /* Set default query classes. */
252 ctx
->classes
[0] = C_IN
;
253 ctx
->classes
[1] = C_HS
;
256 * Now open and parse the file...
258 if (!(fp
= fopen(filename
, "rce")))
261 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
262 char *key
, *data
, *cp
, **cpp
;
265 if (*cp
== '#' || *cp
== '\n' || *cp
== '\r')
267 while(*cp
== ' ' || *cp
== '\t')
270 while(*cp
!= ' ' && *cp
!= '\t' && *cp
!= '=')
274 while(*cp
== ' ' || *cp
== '\t' || *cp
== '=')
277 while(*cp
!= ' ' && *cp
!= '\n' && *cp
!= '\r')
282 if (strcasecmp(key
, "lhs") == 0)
284 else if (strcasecmp(key
, "rhs") == 0)
290 } else if (strcasecmp(key
, "classes") == 0) {
292 while (*data
&& n
< 2) {
293 cp
= strchrnul(data
, ',');
296 if (strcasecmp(data
, "IN") == 0)
297 ctx
->classes
[n
++] = C_IN
;
298 else if (strcasecmp(data
, "HS") == 0)
299 ctx
->classes
[n
++] = C_HS
;
303 /* Restore the default. Better than
305 ctx
->classes
[0] = C_IN
;
306 ctx
->classes
[1] = C_HS
;
308 || ctx
->classes
[0] == ctx
->classes
[1])
319 ctx
->RHS
= ctx
->LHS
= 0;
324 * Given a DNS class and a DNS name, do a lookup for TXT records, and
325 * return a list of them.
328 get_txt_records(struct hesiod_p
*ctx
, int class, const char *name
) {
330 int type
; /* RR type */
331 int class; /* RR class */
332 int dlen
; /* len of data section */
333 u_char
*data
; /* pointer to data */
336 u_char qbuf
[MAX_HESRESP
], abuf
[MAX_HESRESP
];
337 u_char
*cp
, *erdata
, *eom
;
338 char *dst
, *edst
, **list
;
339 int ancount
, qdcount
;
343 * Construct the query and send it.
345 n
= res_mkquery(QUERY
, name
, class, T_TXT
, NULL
, 0,
346 NULL
, qbuf
, MAX_HESRESP
);
348 __set_errno(EMSGSIZE
);
351 n
= res_send(qbuf
, n
, abuf
, MAX_HESRESP
);
353 __set_errno(ECONNREFUSED
);
357 __set_errno(EMSGSIZE
);
362 * OK, parse the result.
364 hp
= (HEADER
*) abuf
;
365 ancount
= ntohs(hp
->ancount
);
366 qdcount
= ntohs(hp
->qdcount
);
367 cp
= abuf
+ sizeof(HEADER
);
370 /* Skip query, trying to get to the answer section which follows. */
371 for (i
= 0; i
< qdcount
; i
++) {
372 skip
= dn_skipname(cp
, eom
);
373 if (skip
< 0 || cp
+ skip
+ QFIXEDSZ
> eom
) {
374 __set_errno(EMSGSIZE
);
377 cp
+= skip
+ QFIXEDSZ
;
380 list
= malloc((ancount
+ 1) * sizeof(char *));
384 for (i
= 0; i
< ancount
; i
++) {
385 skip
= dn_skipname(cp
, eom
);
387 __set_errno(EMSGSIZE
);
391 if (cp
+ 3 * INT16SZ
+ INT32SZ
> eom
) {
392 __set_errno(EMSGSIZE
);
395 rr
.type
= ns_get16(cp
);
397 rr
.class = ns_get16(cp
);
398 cp
+= INT16SZ
+ INT32SZ
; /* skip the ttl, too */
399 rr
.dlen
= ns_get16(cp
);
401 if (rr
.dlen
== 0 || cp
+ rr
.dlen
> eom
) {
402 __set_errno(EMSGSIZE
);
407 if (rr
.class != class || rr
.type
!= T_TXT
)
409 if (!(list
[j
] = malloc(rr
.dlen
)))
412 edst
= dst
+ rr
.dlen
;
413 erdata
= rr
.data
+ rr
.dlen
;
415 while (cp
< erdata
) {
416 n
= (unsigned char) *cp
++;
417 if (cp
+ n
> eom
|| dst
+ n
> edst
) {
418 __set_errno(EMSGSIZE
);
426 __set_errno(EMSGSIZE
);
439 for (i
= 0; i
< j
; i
++)