Generic __longjmp.c.
[kopensolaris-gnu/glibc.git] / resolv / res_hconf.c
1 /* Copyright (C) 1993, 1995-2003, 2004, 2005 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by David Mosberger (davidm@azstarnet.com).
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 /* This file provides a Linux /etc/host.conf compatible front end to
21    the various name resolvers (/etc/hosts, named, NIS server, etc.).
22    Though mostly compatibly, the following differences exist compared
23    to the original implementation:
24
25         - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
26           environment variable (i.e., `off', `nowarn', or `warn').
27
28         - line comments can appear anywhere (not just at the beginning of
29           a line)
30 */
31
32 #include <assert.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <libintl.h>
36 #include <memory.h>
37 #include <stdio.h>
38 #include <stdio_ext.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <net/if.h>
42 #include <sys/ioctl.h>
43 #include <unistd.h>
44 #include <netinet/in.h>
45 #include <bits/libc-lock.h>
46 #include "ifreq.h"
47 #include "res_hconf.h"
48 #ifdef USE_IN_LIBIO
49 # include <wchar.h>
50 #endif
51
52 #define _PATH_HOSTCONF  "/etc/host.conf"
53
54 /* Environment vars that all user to override default behavior:  */
55 #define ENV_HOSTCONF    "RESOLV_HOST_CONF"
56 #define ENV_SERVORDER   "RESOLV_SERV_ORDER"
57 #define ENV_SPOOF       "RESOLV_SPOOF_CHECK"
58 #define ENV_TRIM_OVERR  "RESOLV_OVERRIDE_TRIM_DOMAINS"
59 #define ENV_TRIM_ADD    "RESOLV_ADD_TRIM_DOMAINS"
60 #define ENV_MULTI       "RESOLV_MULTI"
61 #define ENV_REORDER     "RESOLV_REORDER"
62
63 static const char *arg_service_list (const char *, int, const char *,
64                                      unsigned int);
65 static const char *arg_trimdomain_list (const char *, int, const char *,
66                                         unsigned int);
67 static const char *arg_spoof (const char *, int, const char *, unsigned int);
68 static const char *arg_bool (const char *, int, const char *, unsigned int);
69
70 static struct cmd
71 {
72   const char *name;
73   const char *(*parse_args) (const char * filename, int line_num,
74                              const char * args, unsigned int arg);
75   unsigned int arg;
76 } cmd[] =
77 {
78   {"order",             arg_service_list,       0},
79   {"trim",              arg_trimdomain_list,    0},
80   {"spoof",             arg_spoof,              0},
81   {"multi",             arg_bool,               HCONF_FLAG_MULTI},
82   {"nospoof",           arg_bool,               HCONF_FLAG_SPOOF},
83   {"spoofalert",        arg_bool,               HCONF_FLAG_SPOOFALERT},
84   {"reorder",           arg_bool,               HCONF_FLAG_REORDER}
85 };
86
87 /* Structure containing the state.  */
88 struct hconf _res_hconf;
89
90 /* Skip white space.  */
91 static const char *
92 skip_ws (const char *str)
93 {
94   while (isspace (*str)) ++str;
95   return str;
96 }
97
98
99 /* Skip until whitespace, comma, end of line, or comment character.  */
100 static const char *
101 skip_string (const char *str)
102 {
103   while (*str && !isspace (*str) && *str != '#' && *str != ',')
104     ++str;
105   return str;
106 }
107
108
109 static const char *
110 arg_service_list (const char *fname, int line_num, const char *args,
111                   unsigned int arg)
112 {
113   enum Name_Service service;
114   const char *start;
115   size_t len;
116   size_t i;
117   static struct
118   {
119     const char * name;
120     enum Name_Service service;
121   } svcs[] =
122     {
123       {"bind",  SERVICE_BIND},
124       {"hosts", SERVICE_HOSTS},
125       {"nis",   SERVICE_NIS},
126     };
127
128   do
129     {
130       start = args;
131       args = skip_string (args);
132       len = args - start;
133
134       service = SERVICE_NONE;
135       for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
136         {
137           if (__strncasecmp (start, svcs[i].name, len) == 0
138               && len == strlen (svcs[i].name))
139           {
140             service = svcs[i].service;
141             break;
142           }
143       }
144       if (service == SERVICE_NONE)
145         {
146           char *buf;
147
148           if (__asprintf (&buf,
149                           _("%s: line %d: expected service, found `%s'\n"),
150                           fname, line_num, start) < 0)
151             return 0;
152
153           __fxprintf (NULL, "%s", buf);
154
155           free (buf);
156           return 0;
157         }
158       if (_res_hconf.num_services >= SERVICE_MAX)
159         {
160           char *buf;
161
162           if (__asprintf (&buf, _("\
163 %s: line %d: cannot specify more than %d services"),
164                           fname, line_num, SERVICE_MAX) < 0)
165             return 0;
166
167           __fxprintf (NULL, "%s", buf);
168
169           free (buf);
170           return 0;
171         }
172       _res_hconf.service[_res_hconf.num_services++] = service;
173
174       args = skip_ws (args);
175       switch (*args)
176         {
177         case ',':
178         case ';':
179         case ':':
180           args = skip_ws (++args);
181           if (!*args || *args == '#')
182             {
183               char *buf;
184
185               if (__asprintf (&buf, _("\
186 %s: line %d: list delimiter not followed by keyword"),
187                               fname, line_num) < 0)
188                 return 0;
189
190               __fxprintf (NULL, "%s", buf);
191
192               free (buf);
193               return 0;
194             }
195         default:
196           break;
197         }
198     }
199   while (*args && *args != '#');
200   return args;
201 }
202
203
204 static const char *
205 arg_trimdomain_list (const char *fname, int line_num, const char *args,
206                      unsigned int flag)
207 {
208   const char * start;
209   size_t len;
210
211   do
212     {
213       start = args;
214       args = skip_string (args);
215       len = args - start;
216
217       if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
218         {
219           char *buf;
220
221           if (__asprintf (&buf, _("\
222 %s: line %d: cannot specify more than %d trim domains"),
223                           fname, line_num, TRIMDOMAINS_MAX) < 0)
224             return 0;
225
226           __fxprintf (NULL, "%s", buf);
227
228           free (buf);
229           return 0;
230         }
231       _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
232         __strndup (start, len);
233       args = skip_ws (args);
234       switch (*args)
235         {
236         case ',': case ';': case ':':
237           args = skip_ws (++args);
238           if (!*args || *args == '#')
239             {
240               char *buf;
241
242               if (__asprintf (&buf, _("\
243 %s: line %d: list delimiter not followed by domain"),
244                               fname, line_num) < 0)
245                 return 0;
246
247               __fxprintf (NULL, "%s", buf);
248
249               free (buf);
250               return 0;
251             }
252         default:
253           break;
254         }
255     }
256   while (*args && *args != '#');
257   return args;
258 }
259
260
261 static const char *
262 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
263 {
264   const char *start = args;
265   size_t len;
266
267   args = skip_string (args);
268   len = args - start;
269
270   if (len == 3 && __strncasecmp (start, "off", len) == 0)
271     _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
272   else
273     {
274       _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
275       if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
276           || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
277         _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
278     }
279   return args;
280 }
281
282
283 static const char *
284 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
285 {
286   if (__strncasecmp (args, "on", 2) == 0)
287     {
288       args += 2;
289       _res_hconf.flags |= flag;
290     }
291   else if (__strncasecmp (args, "off", 3) == 0)
292     {
293       args += 3;
294       _res_hconf.flags &= ~flag;
295     }
296   else
297     {
298       char *buf;
299
300       if (__asprintf (&buf,
301                       _("%s: line %d: expected `on' or `off', found `%s'\n"),
302                       fname, line_num, args) < 0)
303         return 0;
304
305       __fxprintf (NULL, "%s", buf);
306
307       free (buf);
308       return 0;
309     }
310   return args;
311 }
312
313
314 static void
315 parse_line (const char *fname, int line_num, const char *str)
316 {
317   const char *start;
318   struct cmd *c = 0;
319   size_t len;
320   size_t i;
321
322   str = skip_ws (str);
323
324   /* skip line comment and empty lines: */
325   if (*str == '\0' || *str == '#') return;
326
327   start = str;
328   str = skip_string (str);
329   len = str - start;
330
331   for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
332     {
333       if (__strncasecmp (start, cmd[i].name, len) == 0
334           && strlen (cmd[i].name) == len)
335         {
336           c = &cmd[i];
337           break;
338         }
339     }
340   if (c == NULL)
341     {
342       char *buf;
343
344       if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
345                       fname, line_num, start) < 0)
346         return;
347
348       __fxprintf (NULL, "%s", buf);
349
350       free (buf);
351       return;
352     }
353
354   /* process args: */
355   str = skip_ws (str);
356   str = (*c->parse_args) (fname, line_num, str, c->arg);
357   if (!str)
358     return;
359
360   /* rest of line must contain white space or comment only: */
361   while (*str)
362     {
363       if (!isspace (*str)) {
364         if (*str != '#')
365           {
366             char *buf;
367
368             if (__asprintf (&buf,
369                             _("%s: line %d: ignoring trailing garbage `%s'\n"),
370                             fname, line_num, str) < 0)
371               break;
372
373             __fxprintf (NULL, "%s", buf);
374
375             free (buf);
376           }
377         break;
378       }
379       ++str;
380     }
381 }
382
383
384 static void
385 do_init (void)
386 {
387   const char *hconf_name;
388   int line_num = 0;
389   char buf[256], *envval;
390   FILE *fp;
391
392   memset (&_res_hconf, '\0', sizeof (_res_hconf));
393
394   hconf_name = getenv (ENV_HOSTCONF);
395   if (hconf_name == NULL)
396     hconf_name = _PATH_HOSTCONF;
397
398   fp = fopen (hconf_name, "rc");
399   if (!fp)
400     /* make up something reasonable: */
401     _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
402   else
403     {
404       /* No threads using this stream.  */
405       __fsetlocking (fp, FSETLOCKING_BYCALLER);
406
407       while (fgets_unlocked (buf, sizeof (buf), fp))
408         {
409           ++line_num;
410           *__strchrnul (buf, '\n') = '\0';
411           parse_line (hconf_name, line_num, buf);
412         }
413       fclose (fp);
414     }
415
416   envval = getenv (ENV_SERVORDER);
417   if (envval)
418     {
419       _res_hconf.num_services = 0;
420       arg_service_list (ENV_SERVORDER, 1, envval, 0);
421     }
422
423   envval = getenv (ENV_SPOOF);
424   if (envval)
425     arg_spoof (ENV_SPOOF, 1, envval, 0);
426
427   envval = getenv (ENV_MULTI);
428   if (envval)
429     arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
430
431   envval = getenv (ENV_REORDER);
432   if (envval)
433     arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
434
435   envval = getenv (ENV_TRIM_ADD);
436   if (envval)
437     arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
438
439   envval = getenv (ENV_TRIM_OVERR);
440   if (envval)
441     {
442       _res_hconf.num_trimdomains = 0;
443       arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
444     }
445
446   _res_hconf.initialized = 1;
447 }
448
449
450 /* Initialize hconf datastructure by reading host.conf file and
451    environment variables.  */
452 void
453 _res_hconf_init (void)
454 {
455   __libc_once_define (static, once);
456
457   __libc_once (once, do_init);
458 }
459
460
461 /* List of known interfaces.  */
462 libc_freeres_ptr (
463 static struct netaddr
464 {
465   int addrtype;
466   union
467   {
468     struct
469     {
470       u_int32_t addr;
471       u_int32_t mask;
472     } ipv4;
473   } u;
474 } *ifaddrs);
475
476 /* We need to protect the dynamic buffer handling.  */
477 __libc_lock_define_initialized (static, lock);
478
479 /* Reorder addresses returned in a hostent such that the first address
480    is an address on the local subnet, if there is such an address.
481    Otherwise, nothing is changed.
482
483    Note that this function currently only handles IPv4 addresses.  */
484
485 void
486 _res_hconf_reorder_addrs (struct hostent *hp)
487 {
488 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
489   int i, j;
490   /* Number of interfaces.  */
491   static int num_ifs = -1;
492
493   /* Only reorder if we're supposed to.  */
494   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
495     return;
496
497   /* Can't deal with anything but IPv4 for now...  */
498   if (hp->h_addrtype != AF_INET)
499     return;
500
501   if (num_ifs <= 0)
502     {
503       struct ifreq *ifr, *cur_ifr;
504       int sd, num, i;
505       /* Save errno.  */
506       int save = errno;
507
508       /* Initialize interface table.  */
509
510       num_ifs = 0;
511
512       /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
513       sd = __socket (AF_INET, SOCK_DGRAM, 0);
514       if (sd < 0)
515         return;
516
517       /* Get lock.  */
518       __libc_lock_lock (lock);
519
520       /* Get a list of interfaces.  */
521       __ifreq (&ifr, &num, sd);
522       if (!ifr)
523         goto cleanup;
524
525       ifaddrs = malloc (num * sizeof (ifaddrs[0]));
526       if (!ifaddrs)
527         goto cleanup1;
528
529       /* Copy usable interfaces in ifaddrs structure.  */
530       for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
531         {
532           if (cur_ifr->ifr_addr.sa_family != AF_INET)
533             continue;
534
535           ifaddrs[num_ifs].addrtype = AF_INET;
536           ifaddrs[num_ifs].u.ipv4.addr =
537             ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
538
539           if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
540             continue;
541
542           ifaddrs[num_ifs].u.ipv4.mask =
543             ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
544
545           /* Now we're committed to this entry.  */
546           ++num_ifs;
547         }
548       /* Just keep enough memory to hold all the interfaces we want.  */
549       ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
550       assert (ifaddrs != NULL);
551
552     cleanup1:
553       __if_freereq (ifr, num);
554
555     cleanup:
556       /* Release lock, preserve error value, and close socket.  */
557       save = errno;
558       __libc_lock_unlock (lock);
559       __close (sd);
560     }
561
562   if (num_ifs == 0)
563     return;
564
565   /* Find an address for which we have a direct connection.  */
566   for (i = 0; hp->h_addr_list[i]; ++i)
567     {
568       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
569
570       for (j = 0; j < num_ifs; ++j)
571         {
572           u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
573           u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
574
575           if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
576             {
577               void *tmp;
578
579               tmp = hp->h_addr_list[i];
580               hp->h_addr_list[i] = hp->h_addr_list[0];
581               hp->h_addr_list[0] = tmp;
582               return;
583             }
584         }
585     }
586 #endif /* defined(SIOCGIFCONF) && ... */
587 }
588
589
590 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
591    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
592    original code applied all trimdomains in order, meaning that the
593    same domainname could be trimmed multiple times.  I believe this
594    was unintentional.  */
595 void
596 _res_hconf_trim_domain (char *hostname)
597 {
598   size_t hostname_len, trim_len;
599   int i;
600
601   hostname_len = strlen (hostname);
602
603   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
604     {
605       const char *trim = _res_hconf.trimdomain[i];
606
607       trim_len = strlen (trim);
608       if (hostname_len > trim_len
609           && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
610         {
611           hostname[hostname_len - trim_len] = '\0';
612           break;
613         }
614     }
615 }
616
617
618 /* Trim all hostnames/aliases in HP according to the trimdomain list.
619    Notice that HP is modified inplace!  */
620 void
621 _res_hconf_trim_domains (struct hostent *hp)
622 {
623   int i;
624
625   if (_res_hconf.num_trimdomains == 0)
626     return;
627
628   _res_hconf_trim_domain (hp->h_name);
629   for (i = 0; hp->h_aliases[i]; ++i)
630     _res_hconf_trim_domain (hp->h_aliases[i]);
631 }