Sparc string routines from Jakub.
[kopensolaris-gnu/glibc.git] / hesiod / hesiod.c
1 /* Copyright (c) 1996 by Internet Software Consortium.
2  *
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.
6  *
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
14  * SOFTWARE.
15  */
16
17 /* Copyright 1996 by the Massachusetts Institute of Technology.
18  *
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.
30  */
31
32 /* This file is part of the hesiod library.  It implements the core
33  * portion of the hesiod resolver.
34  *
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
38  * path.
39  *
40  * This implementation is not truly thread-safe at the moment because
41  * it uses res_send() and accesses _res.
42  */
43
44 static const char rcsid[] = "$Id$";
45
46 #include <sys/types.h>
47 #include <netinet/in.h>
48 #include <arpa/nameser.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <resolv.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include "hesiod.h"
57 #include "hesiod_p.h"
58
59 /* A few operating systems don't define these. */
60 #ifndef C_HS
61 # define C_HS   4
62 #endif
63 #ifndef T_TXT
64 # define T_TXT  16
65 #endif
66
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,
69                                const char *name);
70 #ifdef _LIBC
71 # define cistrcmp(s1, s2) strcasecmp (s1, s2)
72 #else
73 static int cistrcmp (const char *s1, const char *s2);
74 #endif
75
76 /* This function is called to initialize a hesiod_p. */
77 int
78 hesiod_init (void **context)
79 {
80   struct hesiod_p *ctx;
81   const char *p, *configname;
82
83   ctx = malloc (sizeof (struct hesiod_p));
84   if (ctx)
85     {
86       *context = ctx;
87       configname = __secure_getenv ("HESIOD_CONFIG");
88       if (!configname)
89         configname = SYSCONFDIR "/hesiod.conf";
90       if (read_config_file (ctx, configname) >= 0)
91         {
92           /* The default rhs can be overridden by an environment variable. */
93           p = __secure_getenv ("HES_DOMAIN");
94           if (p)
95             {
96               if (ctx->rhs)
97                 free (ctx->rhs);
98               ctx->rhs = malloc (strlen (p) + 2);
99               if (ctx->rhs)
100                 {
101                   *ctx->rhs = '.';
102                   strcpy (ctx->rhs + 1, (*p == '.') ? p + 1 : p);
103                   return 0;
104                 }
105               else
106                 __set_errno (ENOMEM);
107             }
108           else
109             return 0;
110         }
111     }
112   else
113     __set_errno (ENOMEM);
114
115   if (ctx->lhs)
116     free (ctx->lhs);
117   if (ctx->rhs)
118     free (ctx->rhs);
119   if (ctx)
120     free (ctx);
121   return -1;
122 }
123
124 /* This function deallocates the hesiod_p. */
125 void
126 hesiod_end (void *context)
127 {
128   struct hesiod_p *ctx = (struct hesiod_p *) context;
129
130   free (ctx->rhs);
131   if (ctx->lhs)
132     free (ctx->lhs);
133   free (ctx);
134 }
135
136 /* This function takes a hesiod (name, type) and returns a DNS
137  * name which is to be resolved.
138  */
139 char *
140 hesiod_to_bind (void *context, const char *name, const char *type)
141 {
142   struct hesiod_p *ctx = (struct hesiod_p *) context;
143   char bindname[MAXDNAME], *p, *endp, *ret, **rhs_list = NULL;
144   const char *rhs;
145   size_t len;
146
147   endp = stpcpy (bindname, name);
148
149   /* Find the right right hand side to use, possibly truncating bindname. */
150   p = strchr (bindname, '@');
151   if (p)
152     {
153       *p++ = 0;
154       if (strchr (p, '.'))
155         rhs = name + (p - bindname);
156       else
157         {
158           rhs_list = hesiod_resolve (context, p, "rhs-extension");
159           if (rhs_list)
160             rhs = *rhs_list;
161           else
162             {
163               __set_errno (ENOENT);
164               return NULL;
165             }
166         }
167     }
168   else
169     rhs = ctx->rhs;
170
171   /* See if we have enough room. */
172   len = (endp - bindname) + 1 + strlen (type);
173   if (ctx->lhs)
174     len += strlen (ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
175   len += strlen (rhs) + ((rhs[0] != '.') ? 1 : 0);
176   if (len > sizeof (bindname) - 1)
177     {
178       if (rhs_list)
179         hesiod_free_list (context, rhs_list);
180       __set_errno (EMSGSIZE);
181       return NULL;
182     }
183
184   /* Put together the rest of the domain. */
185   endp = stpcpy (stpcpy (endp, "."), type);
186   if (ctx->lhs)
187     {
188       if (ctx->lhs[0] != '.')
189         endp = stpcpy (endp, ".");
190       endp = stpcpy (endp, ctx->lhs);
191     }
192   if (rhs[0] != '.')
193     endp = stpcpy (endp, ".");
194   endp = stpcpy (endp, rhs);
195
196   /* rhs_list is no longer needed, since we're done with rhs. */
197   if (rhs_list)
198     hesiod_free_list (context, rhs_list);
199
200   /* Make a copy of the result and return it to the caller. */
201   ret = malloc ((endp - bindname) + 1);
202   if (!ret)
203     {
204       __set_errno (ENOMEM);
205       return NULL;
206     }
207   return strcpy (ret, bindname);
208 }
209
210 /* This is the core function.  Given a hesiod name and type, it
211  * returns an array of strings returned by the resolver.
212  */
213 char **
214 hesiod_resolve (void *context, const char *name, const char *type)
215 {
216   struct hesiod_p *ctx = (struct hesiod_p *) context;
217   char *bindname, **retvec;
218
219   bindname = hesiod_to_bind (context, name, type);
220   if (bindname == NULL)
221     return NULL;
222
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);
226
227   free (bindname);
228   return retvec;
229 }
230
231 void
232 hesiod_free_list (void *context, char **list)
233 {
234   char **p;
235
236   for (p = list; *p; p++)
237     free (*p);
238   free (list);
239 }
240
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. */
244 static int
245 read_config_file (struct hesiod_p *ctx, const char *filename)
246 {
247   char *key, *data, *p, **which;
248   char buf[MAXDNAME + 7];
249   int n;
250   FILE *fp;
251
252   /* Set default query classes. */
253   ctx->classes[0] = C_IN;
254   ctx->classes[1] = C_HS;
255
256   /* Try to open the configuration file. */
257   fp = fopen (filename, "r");
258   if (fp == NULL)
259     {
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)
264         {
265           strcpy (ctx->lhs, DEF_LHS);
266           strcpy (ctx->rhs, DEF_RHS);
267           return 0;
268         }
269       else
270         {
271           __set_errno (ENOMEM);
272           return -1;
273         }
274     }
275
276   ctx->lhs = NULL;
277   ctx->rhs = NULL;
278   while (fgets (buf, sizeof (buf), fp) != NULL)
279     {
280       p = buf;
281       if (*p == '#' || *p == '\n' || *p == '\r')
282         continue;
283       while (*p == ' ' || *p == '\t')
284         ++p;
285       key = p;
286       while(*p != ' ' && *p != '\t' && *p != '=')
287         ++p;
288       *p++ = 0;
289
290       while (isspace (*p) || *p == '=')
291         ++p;
292       data = p;
293       while (!isspace (*p))
294         ++p;
295       *p = 0;
296
297       if (cistrcmp (key, "lhs") == 0 || cistrcmp (key, "rhs") == 0)
298         {
299           which = (strcmp (key, "lhs") == 0) ? &ctx->lhs : &ctx->rhs;
300           *which = strdup (data);
301           if (!*which)
302             {
303               __set_errno (ENOMEM);
304               return -1;
305             }
306         }
307       else if (cistrcmp (key, "classes") == 0)
308         {
309           n = 0;
310           while (*data && n < 2)
311             {
312               p = data;
313               while (*p && *p != ',')
314                 ++p;
315               if (*p)
316                 *p++ = 0;
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;
321               data = p;
322             }
323           while (n < 2)
324             ctx->classes[n++] = 0;
325         }
326     }
327   fclose (fp);
328
329   if (!ctx->rhs || ctx->classes[0] == 0 || ctx->classes[0] == ctx->classes[1])
330     {
331       __set_errno (ENOEXEC);
332       return -1;
333     }
334
335   return 0;
336 }
337
338 /* Given a DNS class and a DNS name, do a lookup for TXT records, and
339  * return a list of them.
340  */
341 static char **
342 get_txt_records (struct hesiod_p *ctx, int qclass, const char *name)
343 {
344   HEADER *hp;
345   unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
346   char *dst, **list;
347   int ancount, qdcount, i, j, n, skip, type, class, len;
348
349   /* Make sure the resolver is initialized. */
350   if ((_res.options & RES_INIT) == 0 && res_init () == -1)
351     return NULL;
352
353   /* Construct the query. */
354   n = res_mkquery (QUERY, name, qclass, T_TXT, NULL, 0,
355                    NULL, qbuf, PACKETSZ);
356   if (n < 0)
357       return NULL;
358
359   /* Send the query. */
360   n = res_send (qbuf, n, abuf, MAX_HESRESP);
361   if (n < 0)
362     {
363       __set_errno (ECONNREFUSED);
364       return NULL;
365     }
366
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);
372   eom = abuf + n;
373
374   /* Skip questions, trying to get to the answer section which follows. */
375   for (i = 0; i < qdcount; ++i)
376     {
377       skip = dn_skipname (p, eom);
378       if (skip < 0 || p + skip + QFIXEDSZ > eom)
379         {
380           __set_errno (EMSGSIZE);
381           return NULL;
382         }
383       p += skip + QFIXEDSZ;
384     }
385
386   /* Allocate space for the text record answers. */
387   list = malloc ((ancount + 1) * sizeof(char *));
388   if (list == NULL)
389     {
390       __set_errno (ENOMEM);
391       return NULL;
392     }
393
394   /* Parse the answers. */
395   j = 0;
396   for (i = 0; i < ancount; i++)
397     {
398       /* Parse the header of this answer. */
399       skip = dn_skipname (p, eom);
400       if (skip < 0 || p + skip + 10 > eom)
401         break;
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];
405       p += skip + 10;
406       if (p + len > eom)
407         {
408           __set_errno (EMSGSIZE);
409           break;
410         }
411
412       /* Skip entries of the wrong class and type. */
413       if (class != qclass || type != T_TXT)
414         {
415           p += len;
416           continue;
417         }
418
419       /* Allocate space for this answer. */
420       list[j] = malloc (len);
421       if (!list[j])
422         {
423           __set_errno (ENOMEM);
424           break;
425         }
426       dst = list[j++];
427
428       /* Copy answer data into the allocated area. */
429       eor = p + len;
430       while (p < eor)
431         {
432           n = (unsigned char) *p++;
433           if (p + n > eor)
434             {
435               __set_errno (EMSGSIZE);
436               break;
437             }
438           dst = mempcpy (dst, p, n);
439           p += n;
440         }
441       if (p < eor)
442         {
443           __set_errno (EMSGSIZE);
444           break;
445         }
446       *dst = 0;
447     }
448
449   /* If we didn't terminate the loop normally, something went wrong. */
450   if (i < ancount)
451     {
452       for (i = 0; i < j; i++)
453         free (list[i]);
454       free (list);
455       return NULL;
456     }
457
458   if (j == 0)
459     {
460       __set_errno (ENOENT);
461       free (list);
462       return NULL;
463     }
464
465   list[j] = NULL;
466   return list;
467 }
468
469 #ifndef _LIBC
470 static int
471 cistrcmp (const char *s1, const char *s2)
472 {
473   while (*s1 && tolower(*s1) == tolower(*s2))
474     {
475       s1++;
476       s2++;
477     }
478   return tolower(*s1) - tolower(*s2);
479 }
480 #endif