]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - libares/ares_init.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / libares / ares_init.c
1 /* Copyright 1998 by the Massachusetts Institute of Technology.
2  *
3  * Permission to use, copy, modify, and distribute this
4  * software and its documentation for any purpose and without
5  * fee is hereby granted, provided that the above copyright
6  * notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting
8  * documentation, and that the name of M.I.T. not be used in
9  * advertising or publicity pertaining to distribution of the
10  * software without specific, written prior permission.
11  * M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  */
15
16 static const char rcsid[] = "$Id: ares_init.c,v 1.7 1999/10/23 19:28:13 danw Exp $";
17
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <netdb.h>
32 #include "ares.h"
33 #include "ares_private.h"
34
35 static int init_by_options(ares_channel channel, struct ares_options *options,
36                            int optmask);
37 static int init_by_environment(ares_channel channel);
38 static int init_by_resolv_conf(ares_channel channel);
39 static int init_by_defaults(ares_channel channel);
40 static int config_domain(ares_channel channel, char *str);
41 static int config_lookup(ares_channel channel, const char *str);
42 static int config_nameserver(struct server_state **servers, int *nservers,
43                              const char *str);
44 static int config_sortlist(struct apattern **sortlist, int *nsort,
45                            const char *str);
46 static int set_search(ares_channel channel, const char *str);
47 static int set_options(ares_channel channel, const char *str);
48 static char *try_config(char *s, char *opt);
49 static const char *try_option(const char *p, const char *q, const char *opt);
50 static int ip_addr(const char *s, int len, struct in_addr *addr);
51 static void natural_mask(struct apattern *pat);
52
53 int ares_init(ares_channel *channelptr)
54 {
55   return ares_init_options(channelptr, NULL, 0);
56 }
57
58 int ares_init_options(ares_channel *channelptr, struct ares_options *options,
59                       int optmask)
60 {
61   ares_channel channel;
62   int i, status;
63   struct server_state *server;
64   struct timeval tv;
65
66   channel = malloc(sizeof(struct ares_channeldata));
67   if (!channel)
68     return ARES_ENOMEM;
69
70   /* Set everything to distinguished values so we know they haven't
71    * been set yet.
72    */
73   channel->flags = -1;
74   channel->timeout = -1;
75   channel->tries = -1;
76   channel->ndots = -1;
77   channel->udp_port = -1;
78   channel->tcp_port = -1;
79   channel->nservers = -1;
80   channel->ndomains = -1;
81   channel->nsort = -1;
82   channel->lookups = NULL;
83
84   /* Initialize configuration by each of the four sources, from highest
85    * precedence to lowest.
86    */
87   status = init_by_options(channel, options, optmask);
88   if (status == ARES_SUCCESS)
89     status = init_by_environment(channel);
90   if (status == ARES_SUCCESS)
91     status = init_by_resolv_conf(channel);
92   if (status == ARES_SUCCESS)
93     status = init_by_defaults(channel);
94   if (status != ARES_SUCCESS)
95     {
96       /* Something failed; clean up memory we may have allocated. */
97       if (channel->nservers != -1)
98         free(channel->servers);
99       if (channel->ndomains != -1)
100         {
101           for (i = 0; i < channel->ndomains; i++)
102             free(channel->domains[i]);
103           free(channel->domains);
104         }
105       if (channel->nsort != -1)
106         free(channel->sortlist);
107       free(channel->lookups);
108       free(channel);
109       return status;
110     }
111
112   /* Trim to one server if ARES_FLAG_PRIMARY is set. */
113   if ((channel->flags & ARES_FLAG_PRIMARY) && channel->nservers > 1)
114     channel->nservers = 1;
115
116   /* Initialize server states. */
117   for (i = 0; i < channel->nservers; i++)
118     {
119       server = &channel->servers[i];
120       server->udp_socket = -1;
121       server->tcp_socket = -1;
122       server->tcp_lenbuf_pos = 0;
123       server->tcp_buffer = NULL;
124       server->qhead = NULL;
125       server->qtail = NULL;
126     }
127
128   /* Choose a somewhat random query ID.  The main point is to avoid
129    * collisions with stale queries.  An attacker trying to spoof a DNS
130    * answer also has to guess the query ID, but it's only a 16-bit
131    * field, so there's not much to be done about that.
132    */
133   gettimeofday(&tv, NULL);
134   channel->next_id = (tv.tv_sec ^ tv.tv_usec ^ getpid()) & 0xffff;
135
136   channel->queries = NULL;
137
138   *channelptr = channel;
139   return ARES_SUCCESS;
140 }
141
142 static int init_by_options(ares_channel channel, struct ares_options *options,
143                            int optmask)
144 {
145   int i;
146
147   /* Easy stuff. */
148   if ((optmask & ARES_OPT_FLAGS) && channel->flags == -1)
149     channel->flags = options->flags;
150   if ((optmask & ARES_OPT_TIMEOUT) && channel->timeout == -1)
151     channel->timeout = options->timeout;
152   if ((optmask & ARES_OPT_TRIES) && channel->tries == -1)
153     channel->tries = options->tries;
154   if ((optmask & ARES_OPT_NDOTS) && channel->ndots == -1)
155     channel->ndots = options->ndots;
156   if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == -1)
157     channel->udp_port = options->udp_port;
158   if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1)
159     channel->tcp_port = options->tcp_port;
160
161   /* Copy the servers, if given. */
162   if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1)
163     {
164       channel->servers =
165         malloc(options->nservers * sizeof(struct server_state));
166       if (!channel->servers && options->nservers != 0)
167         return ARES_ENOMEM;
168       for (i = 0; i < options->nservers; i++)
169         channel->servers[i].addr = options->servers[i];
170       channel->nservers = options->nservers;
171     }
172
173   /* Copy the domains, if given.  Keep channel->ndomains consistent so
174    * we can clean up in case of error.
175    */
176   if ((optmask & ARES_OPT_DOMAINS) && channel->ndomains == -1)
177     {
178       channel->domains = malloc(options->ndomains * sizeof(char *));
179       if (!channel->domains && options->ndomains != 0)
180         return ARES_ENOMEM;
181       for (i = 0; i < options->ndomains; i++)
182         {
183           channel->ndomains = i;
184           channel->domains[i] = strdup(options->domains[i]);
185           if (!channel->domains[i])
186             return ARES_ENOMEM;
187         }
188       channel->ndomains = options->ndomains;
189     }
190
191   /* Set lookups, if given. */
192   if ((optmask & ARES_OPT_LOOKUPS) && !channel->lookups)
193     {
194       channel->lookups = strdup(options->lookups);
195       if (!channel->lookups)
196         return ARES_ENOMEM;
197     }
198
199   return ARES_SUCCESS;
200 }
201
202 static int init_by_environment(ares_channel channel)
203 {
204   const char *localdomain, *res_options;
205   int status;
206
207   localdomain = getenv("LOCALDOMAIN");
208   if (localdomain && channel->ndomains == -1)
209     {
210       status = set_search(channel, localdomain);
211       if (status != ARES_SUCCESS)
212         return status;
213     }
214
215   res_options = getenv("RES_OPTIONS");
216   if (res_options)
217     {
218       status = set_options(channel, res_options);
219       if (status != ARES_SUCCESS)
220         return status;
221     }
222
223   return ARES_SUCCESS;
224 }
225
226 static int init_by_resolv_conf(ares_channel channel)
227 {
228   FILE *fp;
229   char *line = NULL, *p;
230   int linesize, status, nservers = 0, nsort = 0;
231   struct server_state *servers = NULL;
232   struct apattern *sortlist = NULL;
233
234   fp = fopen(PATH_RESOLV_CONF, "r");
235   if (!fp)
236     return (errno == ENOENT) ? ARES_SUCCESS : ARES_EFILE;
237   while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
238     {
239       if ((p = try_config(line, "domain")) && channel->ndomains == -1)
240         status = config_domain(channel, p);
241       else if ((p = try_config(line, "lookup")) && !channel->lookups)
242         status = config_lookup(channel, p);
243       else if ((p = try_config(line, "search")) && channel->ndomains == -1)
244         status = set_search(channel, p);
245       else if ((p = try_config(line, "nameserver")) && channel->nservers == -1)
246         status = config_nameserver(&servers, &nservers, p);
247       else if ((p = try_config(line, "sortlist")) && channel->nsort == -1)
248         status = config_sortlist(&sortlist, &nsort, p);
249       else if ((p = try_config(line, "options")))
250         status = set_options(channel, p);
251       else
252         status = ARES_SUCCESS;
253       if (status != ARES_SUCCESS)
254         break;
255     }
256   free(line);
257   fclose(fp);
258
259   /* Handle errors. */
260   if (status != ARES_EOF)
261     {
262       free(servers);
263       free(sortlist);
264       return status;
265     }
266
267   /* If we got any name server entries, fill them in. */
268   if (servers)
269     {
270       channel->servers = servers;
271       channel->nservers = nservers;
272     }
273
274   /* If we got any sortlist entries, fill them in. */
275   if (sortlist)
276     {
277       channel->sortlist = sortlist;
278       channel->nsort = nsort;
279     }
280
281   return ARES_SUCCESS;
282 }
283
284 static int init_by_defaults(ares_channel channel)
285 {
286   char hostname[256];
287
288   if (channel->flags == -1)
289     channel->flags = 0;
290   if (channel->timeout == -1)
291     channel->timeout = DEFAULT_TIMEOUT;
292   if (channel->tries == -1)
293     channel->tries = DEFAULT_TRIES;
294   if (channel->ndots == -1)
295     channel->ndots = 1;
296   if (channel->udp_port == -1)
297     channel->udp_port = htons(NAMESERVER_PORT);
298   if (channel->tcp_port == -1)
299     channel->tcp_port = htons(NAMESERVER_PORT);
300
301   if (channel->nservers == -1)
302     {
303       /* If nobody specified servers, try a local named. */
304       channel->servers = malloc(sizeof(struct server_state));
305       if (!channel->servers)
306         return ARES_ENOMEM;
307       channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK);
308       channel->nservers = 1;
309     }
310
311   if (channel->ndomains == -1)
312     {
313       /* Derive a default domain search list from the kernel hostname,
314        * or set it to empty if the hostname isn't helpful.
315        */
316       if (gethostname(hostname, sizeof(hostname)) == -1
317           || !strchr(hostname, '.'))
318         {
319           channel->domains = malloc(0);
320           channel->ndomains = 0;
321         }
322       else
323         {
324           channel->domains = malloc(sizeof(char *));
325           if (!channel->domains)
326             return ARES_ENOMEM;
327           channel->ndomains = 0;
328           channel->domains[0] = strdup(strchr(hostname, '.') + 1);
329           if (!channel->domains[0])
330             return ARES_ENOMEM;
331           channel->ndomains = 1;
332         }
333     }
334
335   if (channel->nsort == -1)
336     {
337       channel->sortlist = NULL;
338       channel->nsort = 0;
339     }
340
341   if (!channel->lookups)
342     {
343       channel->lookups = strdup("bf");
344       if (!channel->lookups)
345         return ARES_ENOMEM;
346     }
347
348   return ARES_SUCCESS;
349 }
350
351 static int config_domain(ares_channel channel, char *str)
352 {
353   char *q;
354
355   /* Set a single search domain. */
356   q = str;
357   while (*q && !isspace((unsigned char)*q))
358     q++;
359   *q = 0;
360   return set_search(channel, str);
361 }
362
363 static int config_lookup(ares_channel channel, const char *str)
364 {
365   char lookups[3], *l;
366   const char *p;
367
368   /* Set the lookup order.  Only the first letter of each work
369    * is relevant, and it has to be "b" for DNS or "f" for the
370    * host file.  Ignore everything else.
371    */
372   l = lookups;
373   p = str;
374   while (*p)
375     {
376       if ((*p == 'b' || *p == 'f') && l < lookups + 2)
377         *l++ = *p;
378       while (*p && !isspace((unsigned char)*p))
379         p++;
380       while (isspace((unsigned char)*p))
381         p++;
382     }
383   *l = 0;
384   channel->lookups = strdup(lookups);
385   return (channel->lookups) ? ARES_SUCCESS : ARES_ENOMEM;
386 }
387
388 static int config_nameserver(struct server_state **servers, int *nservers,
389                              const char *str)
390 {
391   struct in_addr addr;
392   struct server_state *newserv;
393
394   /* Add a nameserver entry, if this is a valid address. */
395   addr.s_addr = inet_addr(str);
396   if (addr.s_addr == INADDR_NONE)
397     return ARES_SUCCESS;
398   newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
399   if (!newserv)
400     return ARES_ENOMEM;
401   newserv[*nservers].addr = addr;
402   *servers = newserv;
403   (*nservers)++;
404   return ARES_SUCCESS;
405 }
406
407 static int config_sortlist(struct apattern **sortlist, int *nsort,
408                            const char *str)
409 {
410   struct apattern pat, *newsort;
411   const char *q;
412
413   /* Add sortlist entries. */
414   while (*str && *str != ';')
415     {
416       q = str;
417       while (*q && *q != '/' && *q != ';' && !isspace((unsigned char)*q))
418         q++;
419       if (ip_addr(str, q - str, &pat.addr) == 0)
420         {
421           /* We have a pattern address; now determine the mask. */
422           if (*q == '/')
423             {
424               str = q + 1;
425               while (*q && *q != ';' && !isspace((unsigned char)*q))
426                 q++;
427               if (ip_addr(str, q - str, &pat.mask) != 0)
428                 natural_mask(&pat);
429             }
430           else
431             natural_mask(&pat);
432
433           /* Add this pattern to our list. */
434           newsort = realloc(*sortlist, (*nsort + 1) * sizeof(struct apattern));
435           if (!newsort)
436             return ARES_ENOMEM;
437           newsort[*nsort] = pat;
438           *sortlist = newsort;
439           (*nsort)++;
440         }
441       else
442         {
443           while (*q && *q != ';' && !isspace((unsigned char)*q))
444             q++;
445         }
446       str = q;
447       while (isspace((unsigned char)*str))
448         str++;
449     }
450
451   return ARES_SUCCESS;
452 }
453
454 static int set_search(ares_channel channel, const char *str)
455 {
456   int n;
457   const char *p, *q;
458
459   /* Count the domains given. */
460   n = 0;
461   p = str;
462   while (*p)
463     {
464       while (*p && !isspace((unsigned char)*p))
465         p++;
466       while (isspace((unsigned char)*p))
467         p++;
468       n++;
469     }
470
471   channel->domains = malloc(n * sizeof(char *));
472   if (!channel->domains && n)
473     return ARES_ENOMEM;
474
475   /* Now copy the domains. */
476   n = 0;
477   p = str;
478   while (*p)
479     {
480       channel->ndomains = n;
481       q = p;
482       while (*q && !isspace((unsigned char)*q))
483         q++;
484       channel->domains[n] = malloc(q - p + 1);
485       if (!channel->domains[n])
486         return ARES_ENOMEM;
487       memcpy(channel->domains[n], p, q - p);
488       channel->domains[n][q - p] = 0;
489       p = q;
490       while (isspace((unsigned char)*p))
491         p++;
492       n++;
493     }
494   channel->ndomains = n;
495
496   return ARES_SUCCESS;
497 }
498
499 static int set_options(ares_channel channel, const char *str)
500 {
501   const char *p, *q, *val;
502
503   p = str;
504   while (*p)
505     {
506       q = p;
507       while (*q && !isspace((unsigned char)*q))
508         q++;
509       val = try_option(p, q, "ndots:");
510       if (val && channel->ndots == -1)
511         channel->ndots = atoi(val);
512       val = try_option(p, q, "retrans:");
513       if (val && channel->timeout == -1)
514         channel->timeout = atoi(val);
515       val = try_option(p, q, "retry:");
516       if (val && channel->tries == -1)
517         channel->tries = atoi(val);
518       p = q;
519       while (isspace((unsigned char)*p))
520         p++;
521     }
522
523   return ARES_SUCCESS;
524 }
525
526 static char *try_config(char *s, char *opt)
527 {
528   int len;
529
530   len = strlen(opt);
531   if (strncmp(s, opt, len) != 0 || !isspace((unsigned char)s[len]))
532     return NULL;
533   s += len;
534   while (isspace((unsigned char)*s))
535     s++;
536   return s;
537 }
538
539 static const char *try_option(const char *p, const char *q, const char *opt)
540 {
541   int len;
542
543   len = strlen(opt);
544   return (q - p > len && strncmp(p, opt, len) == 0) ? p + len : NULL;
545 }
546
547 static int ip_addr(const char *s, int len, struct in_addr *addr)
548 {
549   char ipbuf[16];
550
551   /* Four octets and three periods yields at most 15 characters. */
552   if (len > 15)
553     return -1;
554   memcpy(ipbuf, s, len);
555   ipbuf[len] = 0;
556
557   addr->s_addr = inet_addr(ipbuf);
558   if (addr->s_addr == INADDR_NONE && strcmp(ipbuf, "255.255.255.255") != 0)
559     return -1;
560   return 0;
561 }
562
563 static void natural_mask(struct apattern *pat)
564 {
565   struct in_addr addr;
566
567   /* Store a host-byte-order copy of pat in a struct in_addr.  Icky,
568    * but portable.
569    */
570   addr.s_addr = ntohl(pat->addr.s_addr);
571
572   /* This is out of date in the CIDR world, but some people might
573    * still rely on it.
574    */
575   if (IN_CLASSA(addr.s_addr))
576     pat->mask.s_addr = htonl(IN_CLASSA_NET);
577   else if (IN_CLASSB(addr.s_addr))
578     pat->mask.s_addr = htonl(IN_CLASSB_NET);
579   else
580     pat->mask.s_addr = htonl(IN_CLASSC_NET);
581 }