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 static const char rcsid
[] = "$Id$";
46 #include <sys/types.h>
47 #include <netinet/in.h>
48 #include <arpa/nameser.h>
59 /* A few operating systems don't define these. */
67 static int read_config_file (struct hesiod_p
*ctx
, const char *filename
);
68 static char **get_txt_records (struct hesiod_p
*ctx
, int class,
71 # define cistrcmp(s1, s2) strcasecmp (s1, s2)
73 static int cistrcmp (const char *s1
, const char *s2
);
76 /* This function is called to initialize a hesiod_p. */
78 hesiod_init (void **context
)
81 const char *p
, *configname
;
83 ctx
= malloc (sizeof (struct hesiod_p
));
87 configname
= __secure_getenv ("HESIOD_CONFIG");
89 configname
= SYSCONFDIR
"/hesiod.conf";
90 if (read_config_file (ctx
, configname
) >= 0)
92 /* The default rhs can be overridden by an environment variable. */
93 p
= __secure_getenv ("HES_DOMAIN");
98 ctx
->rhs
= malloc (strlen (p
) + 2);
102 strcpy (ctx
->rhs
+ 1, (*p
== '.') ? p
+ 1 : p
);
106 __set_errno (ENOMEM
);
113 __set_errno (ENOMEM
);
124 /* This function deallocates the hesiod_p. */
126 hesiod_end (void *context
)
128 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
136 /* This function takes a hesiod (name, type) and returns a DNS
137 * name which is to be resolved.
140 hesiod_to_bind (void *context
, const char *name
, const char *type
)
142 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
143 char bindname
[MAXDNAME
], *p
, *endp
, *ret
, **rhs_list
= NULL
;
147 endp
= stpcpy (bindname
, name
);
149 /* Find the right right hand side to use, possibly truncating bindname. */
150 p
= strchr (bindname
, '@');
155 rhs
= name
+ (p
- bindname
);
158 rhs_list
= hesiod_resolve (context
, p
, "rhs-extension");
163 __set_errno (ENOENT
);
171 /* See if we have enough room. */
172 len
= (endp
- bindname
) + 1 + strlen (type
);
174 len
+= strlen (ctx
->lhs
) + ((ctx
->lhs
[0] != '.') ? 1 : 0);
175 len
+= strlen (rhs
) + ((rhs
[0] != '.') ? 1 : 0);
176 if (len
> sizeof (bindname
) - 1)
179 hesiod_free_list (context
, rhs_list
);
180 __set_errno (EMSGSIZE
);
184 /* Put together the rest of the domain. */
185 endp
= stpcpy (stpcpy (endp
, "."), type
);
188 if (ctx
->lhs
[0] != '.')
189 endp
= stpcpy (endp
, ".");
190 endp
= stpcpy (endp
, ctx
->lhs
);
193 endp
= stpcpy (endp
, ".");
194 endp
= stpcpy (endp
, rhs
);
196 /* rhs_list is no longer needed, since we're done with rhs. */
198 hesiod_free_list (context
, rhs_list
);
200 /* Make a copy of the result and return it to the caller. */
201 ret
= malloc ((endp
- bindname
) + 1);
204 __set_errno (ENOMEM
);
207 return strcpy (ret
, bindname
);
210 /* This is the core function. Given a hesiod name and type, it
211 * returns an array of strings returned by the resolver.
214 hesiod_resolve (void *context
, const char *name
, const char *type
)
216 struct hesiod_p
*ctx
= (struct hesiod_p
*) context
;
217 char *bindname
, **retvec
;
219 bindname
= hesiod_to_bind (context
, name
, type
);
220 if (bindname
== NULL
)
223 retvec
= get_txt_records(ctx
, ctx
->classes
[0], bindname
);
224 if (retvec
== NULL
&& errno
== ENOENT
&& ctx
->classes
[1])
225 retvec
= get_txt_records (ctx
, ctx
->classes
[1], bindname
);
232 hesiod_free_list (void *context
, char **list
)
236 for (p
= list
; *p
; p
++)
241 /* This function parses the /etc/hesiod.conf file. Returns 0 on success,
242 * -1 on failure. On failure, it might leave values in ctx->lhs or
243 * ctx->rhs which need to be freed by the caller. */
245 read_config_file (struct hesiod_p
*ctx
, const char *filename
)
247 char *key
, *data
, *p
, **which
;
248 char buf
[MAXDNAME
+ 7];
252 /* Set default query classes. */
253 ctx
->classes
[0] = C_IN
;
254 ctx
->classes
[1] = C_HS
;
256 /* Try to open the configuration file. */
257 fp
= fopen (filename
, "r");
260 /* Use compiled in default domain names. */
261 ctx
->lhs
= malloc (strlen (DEF_LHS
) + 1);
262 ctx
->rhs
= malloc (strlen (DEF_RHS
) + 1);
263 if (ctx
->lhs
&& ctx
->rhs
)
265 strcpy (ctx
->lhs
, DEF_LHS
);
266 strcpy (ctx
->rhs
, DEF_RHS
);
271 __set_errno (ENOMEM
);
278 while (fgets (buf
, sizeof (buf
), fp
) != NULL
)
281 if (*p
== '#' || *p
== '\n' || *p
== '\r')
283 while (*p
== ' ' || *p
== '\t')
286 while(*p
!= ' ' && *p
!= '\t' && *p
!= '=')
290 while (isspace (*p
) || *p
== '=')
293 while (!isspace (*p
))
297 if (cistrcmp (key
, "lhs") == 0 || cistrcmp (key
, "rhs") == 0)
299 which
= (strcmp (key
, "lhs") == 0) ? &ctx
->lhs
: &ctx
->rhs
;
300 *which
= strdup (data
);
303 __set_errno (ENOMEM
);
307 else if (cistrcmp (key
, "classes") == 0)
310 while (*data
&& n
< 2)
313 while (*p
&& *p
!= ',')
317 if (cistrcmp (data
, "IN") == 0)
318 ctx
->classes
[n
++] = C_IN
;
319 else if (cistrcmp (data
, "HS") == 0)
320 ctx
->classes
[n
++] = C_HS
;
324 ctx
->classes
[n
++] = 0;
329 if (!ctx
->rhs
|| ctx
->classes
[0] == 0 || ctx
->classes
[0] == ctx
->classes
[1])
331 __set_errno (ENOEXEC
);
338 /* Given a DNS class and a DNS name, do a lookup for TXT records, and
339 * return a list of them.
342 get_txt_records (struct hesiod_p
*ctx
, int qclass
, const char *name
)
345 unsigned char qbuf
[PACKETSZ
], abuf
[MAX_HESRESP
], *p
, *eom
, *eor
;
347 int ancount
, qdcount
, i
, j
, n
, skip
, type
, class, len
;
349 /* Make sure the resolver is initialized. */
350 if ((_res
.options
& RES_INIT
) == 0 && res_init () == -1)
353 /* Construct the query. */
354 n
= res_mkquery (QUERY
, name
, qclass
, T_TXT
, NULL
, 0,
355 NULL
, qbuf
, PACKETSZ
);
359 /* Send the query. */
360 n
= res_send (qbuf
, n
, abuf
, MAX_HESRESP
);
363 __set_errno (ECONNREFUSED
);
367 /* Parse the header of the result. */
368 hp
= (HEADER
*) abuf
;
369 ancount
= ntohs (hp
->ancount
);
370 qdcount
= ntohs (hp
->qdcount
);
371 p
= abuf
+ sizeof (HEADER
);
374 /* Skip questions, trying to get to the answer section which follows. */
375 for (i
= 0; i
< qdcount
; ++i
)
377 skip
= dn_skipname (p
, eom
);
378 if (skip
< 0 || p
+ skip
+ QFIXEDSZ
> eom
)
380 __set_errno (EMSGSIZE
);
383 p
+= skip
+ QFIXEDSZ
;
386 /* Allocate space for the text record answers. */
387 list
= malloc ((ancount
+ 1) * sizeof(char *));
390 __set_errno (ENOMEM
);
394 /* Parse the answers. */
396 for (i
= 0; i
< ancount
; i
++)
398 /* Parse the header of this answer. */
399 skip
= dn_skipname (p
, eom
);
400 if (skip
< 0 || p
+ skip
+ 10 > eom
)
402 type
= p
[skip
+ 0] << 8 | p
[skip
+ 1];
403 class = p
[skip
+ 2] << 8 | p
[skip
+ 3];
404 len
= p
[skip
+ 8] << 8 | p
[skip
+ 9];
408 __set_errno (EMSGSIZE
);
412 /* Skip entries of the wrong class and type. */
413 if (class != qclass
|| type
!= T_TXT
)
419 /* Allocate space for this answer. */
420 list
[j
] = malloc (len
);
423 __set_errno (ENOMEM
);
428 /* Copy answer data into the allocated area. */
432 n
= (unsigned char) *p
++;
435 __set_errno (EMSGSIZE
);
438 dst
= mempcpy (dst
, p
, n
);
443 __set_errno (EMSGSIZE
);
449 /* If we didn't terminate the loop normally, something went wrong. */
452 for (i
= 0; i
< j
; i
++)
460 __set_errno (ENOENT
);
471 cistrcmp (const char *s1
, const char *s2
)
473 while (*s1
&& tolower(*s1
) == tolower(*s2
))
478 return tolower(*s1
) - tolower(*s2
);