1 /* Copyright (c) 1996 by Internet Software Consortium.
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
8 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
9 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
10 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
11 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
12 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
13 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
17 /* Copyright 1996 by the Massachusetts Institute of Technology.
19 * Permission to use, copy, modify, and distribute this
20 * software and its documentation for any purpose and without
21 * fee is hereby granted, provided that the above copyright
22 * notice appear in all copies and that both that copyright
23 * notice and this permission notice appear in supporting
24 * documentation, and that the name of M.I.T. not be used in
25 * advertising or publicity pertaining to distribution of the
26 * software without specific, written prior permission.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose. It is provided "as is"
29 * without express or implied warranty.
32 /* This file is part of the hesiod library. It implements the core
33 * portion of the hesiod resolver.
35 * This file is loosely based on an interim version of hesiod.c from
36 * the BIND IRS library, which was in turn based on an earlier version
37 * of this file. Extensive changes have been made on each step of the
40 * This implementation is not truly thread-safe at the moment because
41 * it uses res_send() and accesses _res.
44 #if defined(LIBC_SCCS) && !defined(lint)
45 static const char rcsid
[] = "$Id$";
48 #include <sys/types.h>
49 #include <netinet/in.h>
50 #include <arpa/nameser.h>
61 /* A few operating systems don't define these. */
69 static int read_config_file (struct hesiod_p
*ctx
, const char *filename
);
70 static char **get_txt_records (struct hesiod_p
*ctx
, int class,
73 # define cistrcmp(s1, s2) strcasecmp (s1, s2)
75 static int cistrcmp (const char *s1
, const char *s2
);
78 /* This function is called to initialize a hesiod_p. */
80 hesiod_init (void **context
)
83 const char *p
, *configname
;
85 ctx
= malloc (sizeof (struct hesiod_p
));
89 configname
= __secure_getenv ("HESIOD_CONFIG");
91 configname
= SYSCONFDIR
"/hesiod.conf";
92 if (read_config_file (ctx
, configname
) >= 0)
94 /* The default rhs can be overridden by an environment variable. */
95 p
= __secure_getenv ("HES_DOMAIN");
100 ctx
->rhs
= malloc (strlen (p
) + 2);
104 strcpy (ctx
->rhs
+ 1, (*p
== '.') ? p
+ 1 : p
);
108 __set_errno (ENOMEM
);
115 __set_errno (ENOMEM
);
126 /* This function deallocates the hesiod_p. */
128 hesiod_end (void *context
)
130 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
138 /* This function takes a hesiod (name, type) and returns a DNS
139 * name which is to be resolved.
142 hesiod_to_bind (void *context
, const char *name
, const char *type
)
144 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
145 char bindname
[MAXDNAME
], *p
, *endp
, *ret
, **rhs_list
= NULL
;
149 endp
= stpcpy (bindname
, name
);
151 /* Find the right right hand side to use, possibly truncating bindname. */
152 p
= strchr (bindname
, '@');
157 rhs
= name
+ (p
- bindname
);
160 rhs_list
= hesiod_resolve (context
, p
, "rhs-extension");
165 __set_errno (ENOENT
);
173 /* See if we have enough room. */
174 len
= (endp
- bindname
) + 1 + strlen (type
);
176 len
+= strlen (ctx
->lhs
) + ((ctx
->lhs
[0] != '.') ? 1 : 0);
177 len
+= strlen (rhs
) + ((rhs
[0] != '.') ? 1 : 0);
178 if (len
> sizeof (bindname
) - 1)
181 hesiod_free_list (context
, rhs_list
);
182 __set_errno (EMSGSIZE
);
186 /* Put together the rest of the domain. */
187 endp
= stpcpy (stpcpy (endp
, "."), type
);
190 if (ctx
->lhs
[0] != '.')
191 endp
= stpcpy (endp
, ".");
192 endp
= stpcpy (endp
, ctx
->lhs
);
195 endp
= stpcpy (endp
, ".");
196 endp
= stpcpy (endp
, rhs
);
198 /* rhs_list is no longer needed, since we're done with rhs. */
200 hesiod_free_list (context
, rhs_list
);
202 /* Make a copy of the result and return it to the caller. */
203 ret
= malloc ((endp
- bindname
) + 1);
206 __set_errno (ENOMEM
);
209 return strcpy (ret
, bindname
);
212 /* This is the core function. Given a hesiod name and type, it
213 * returns an array of strings returned by the resolver.
216 hesiod_resolve (void *context
, const char *name
, const char *type
)
218 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
219 char *bindname
, **retvec
;
221 bindname
= hesiod_to_bind (context
, name
, type
);
222 if (bindname
== NULL
)
225 retvec
= get_txt_records(ctx
, ctx
->classes
[0], bindname
);
226 if (retvec
== NULL
&& errno
== ENOENT
&& ctx
->classes
[1])
227 retvec
= get_txt_records (ctx
, ctx
->classes
[1], bindname
);
234 hesiod_free_list (void *context
, char **list
)
238 for (p
= list
; *p
; p
++)
243 /* This function parses the /etc/hesiod.conf file. Returns 0 on success,
244 * -1 on failure. On failure, it might leave values in ctx->lhs or
245 * ctx->rhs which need to be freed by the caller. */
247 read_config_file (struct hesiod_p
*ctx
, const char *filename
)
249 char *key
, *data
, *p
, **which
;
250 char buf
[MAXDNAME
+ 7];
254 /* Set default query classes. */
255 ctx
->classes
[0] = C_IN
;
256 ctx
->classes
[1] = C_HS
;
258 /* Try to open the configuration file. */
259 fp
= fopen (filename
, "r");
262 /* Use compiled in default domain names. */
263 ctx
->lhs
= malloc (strlen (DEF_LHS
) + 1);
264 ctx
->rhs
= malloc (strlen (DEF_RHS
) + 1);
265 if (ctx
->lhs
&& ctx
->rhs
)
267 strcpy (ctx
->lhs
, DEF_LHS
);
268 strcpy (ctx
->rhs
, DEF_RHS
);
273 __set_errno (ENOMEM
);
280 while (fgets (buf
, sizeof (buf
), fp
) != NULL
)
283 if (*p
== '#' || *p
== '\n' || *p
== '\r')
285 while (*p
== ' ' || *p
== '\t')
288 while(*p
!= ' ' && *p
!= '\t' && *p
!= '=')
292 while (isspace (*p
) || *p
== '=')
295 while (!isspace (*p
))
299 if (cistrcmp (key
, "lhs") == 0 || cistrcmp (key
, "rhs") == 0)
301 which
= (strcmp (key
, "lhs") == 0) ? &ctx
->lhs
: &ctx
->rhs
;
302 *which
= strdup (data
);
305 __set_errno (ENOMEM
);
309 else if (cistrcmp (key
, "classes") == 0)
312 while (*data
&& n
< 2)
315 while (*p
&& *p
!= ',')
319 if (cistrcmp (data
, "IN") == 0)
320 ctx
->classes
[n
++] = C_IN
;
321 else if (cistrcmp (data
, "HS") == 0)
322 ctx
->classes
[n
++] = C_HS
;
326 ctx
->classes
[n
++] = 0;
331 if (!ctx
->rhs
|| ctx
->classes
[0] == 0 || ctx
->classes
[0] == ctx
->classes
[1])
333 __set_errno (ENOEXEC
);
340 /* Given a DNS class and a DNS name, do a lookup for TXT records, and
341 * return a list of them.
344 get_txt_records (struct hesiod_p
*ctx
, int qclass
, const char *name
)
347 unsigned char qbuf
[PACKETSZ
], abuf
[MAX_HESRESP
], *p
, *eom
, *eor
;
349 int ancount
, qdcount
, i
, j
, n
, skip
, type
, class, len
;
351 /* Make sure the resolver is initialized. */
352 if ((_res
.options
& RES_INIT
) == 0 && res_init () == -1)
355 /* Construct the query. */
356 n
= res_mkquery (QUERY
, name
, qclass
, T_TXT
, NULL
, 0,
357 NULL
, qbuf
, PACKETSZ
);
361 /* Send the query. */
362 n
= res_send (qbuf
, n
, abuf
, MAX_HESRESP
);
365 __set_errno (ECONNREFUSED
);
369 /* Parse the header of the result. */
370 hp
= (HEADER
*) abuf
;
371 ancount
= ntohs (hp
->ancount
);
372 qdcount
= ntohs (hp
->qdcount
);
373 p
= abuf
+ sizeof (HEADER
);
376 /* Skip questions, trying to get to the answer section which follows. */
377 for (i
= 0; i
< qdcount
; ++i
)
379 skip
= dn_skipname (p
, eom
);
380 if (skip
< 0 || p
+ skip
+ QFIXEDSZ
> eom
)
382 __set_errno (EMSGSIZE
);
385 p
+= skip
+ QFIXEDSZ
;
388 /* Allocate space for the text record answers. */
389 list
= malloc ((ancount
+ 1) * sizeof(char *));
392 __set_errno (ENOMEM
);
396 /* Parse the answers. */
398 for (i
= 0; i
< ancount
; i
++)
400 /* Parse the header of this answer. */
401 skip
= dn_skipname (p
, eom
);
402 if (skip
< 0 || p
+ skip
+ 10 > eom
)
404 type
= p
[skip
+ 0] << 8 | p
[skip
+ 1];
405 class = p
[skip
+ 2] << 8 | p
[skip
+ 3];
406 len
= p
[skip
+ 8] << 8 | p
[skip
+ 9];
410 __set_errno (EMSGSIZE
);
414 /* Skip entries of the wrong class and type. */
415 if (class != qclass
|| type
!= T_TXT
)
421 /* Allocate space for this answer. */
422 list
[j
] = malloc (len
);
425 __set_errno (ENOMEM
);
430 /* Copy answer data into the allocated area. */
434 n
= (unsigned char) *p
++;
437 __set_errno (EMSGSIZE
);
440 dst
= mempcpy (dst
, p
, n
);
445 __set_errno (EMSGSIZE
);
451 /* If we didn't terminate the loop normally, something went wrong. */
454 for (i
= 0; i
< j
; i
++)
462 __set_errno (ENOENT
);
473 cistrcmp (const char *s1
, const char *s2
)
475 while (*s1
&& tolower(*s1
) == tolower(*s2
))
480 return tolower(*s1
) - tolower(*s2
);