(_nss_dns_getcanonname_r): Initialize status to NSS_STATUS_UNAVAIL.
[kopensolaris-gnu/glibc.git] / resolv / res_hconf.c
1 /* Copyright (C) 1993, 1995-2003, 2004 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 #ifdef USE_IN_LIBIO
154           if (_IO_fwide (stderr, 0) > 0)
155             __fwprintf (stderr, L"%s", buf);
156           else
157 #endif
158             fputs (buf, stderr);
159
160           free (buf);
161           return 0;
162         }
163       if (_res_hconf.num_services >= SERVICE_MAX)
164         {
165           char *buf;
166
167           if (__asprintf (&buf, _("\
168 %s: line %d: cannot specify more than %d services"),
169                           fname, line_num, SERVICE_MAX) < 0)
170             return 0;
171
172 #ifdef USE_IN_LIBIO
173           if (_IO_fwide (stderr, 0) > 0)
174             __fwprintf (stderr, L"%s", buf);
175           else
176 #endif
177             fputs (buf, stderr);
178
179           free (buf);
180           return 0;
181         }
182       _res_hconf.service[_res_hconf.num_services++] = service;
183
184       args = skip_ws (args);
185       switch (*args)
186         {
187         case ',':
188         case ';':
189         case ':':
190           args = skip_ws (++args);
191           if (!*args || *args == '#')
192             {
193               char *buf;
194
195               if (__asprintf (&buf, _("\
196 %s: line %d: list delimiter not followed by keyword"),
197                               fname, line_num) < 0)
198                 return 0;
199
200 #ifdef USE_IN_LIBIO
201               if (_IO_fwide (stderr, 0) > 0)
202                 __fwprintf (stderr, L"%s", buf);
203               else
204 #endif
205                 fputs (buf, stderr);
206
207               free (buf);
208               return 0;
209             }
210         default:
211           break;
212         }
213     }
214   while (*args && *args != '#');
215   return args;
216 }
217
218
219 static const char *
220 arg_trimdomain_list (const char *fname, int line_num, const char *args,
221                      unsigned int flag)
222 {
223   const char * start;
224   size_t len;
225
226   do
227     {
228       start = args;
229       args = skip_string (args);
230       len = args - start;
231
232       if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
233         {
234           char *buf;
235
236           if (__asprintf (&buf, _("\
237 %s: line %d: cannot specify more than %d trim domains"),
238                           fname, line_num, TRIMDOMAINS_MAX) < 0)
239             return 0;
240
241 #ifdef USE_IN_LIBIO
242               if (_IO_fwide (stderr, 0) > 0)
243                 __fwprintf (stderr, L"%s", buf);
244               else
245 #endif
246                 fputs (buf, stderr);
247
248               free (buf);
249           return 0;
250         }
251       _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
252         __strndup (start, len);
253       args = skip_ws (args);
254       switch (*args)
255         {
256         case ',': case ';': case ':':
257           args = skip_ws (++args);
258           if (!*args || *args == '#')
259             {
260               char *buf;
261
262               if (__asprintf (&buf, _("\
263 %s: line %d: list delimiter not followed by domain"),
264                               fname, line_num) < 0)
265                 return 0;
266
267 #ifdef USE_IN_LIBIO
268               if (_IO_fwide (stderr, 0) > 0)
269                 __fwprintf (stderr, L"%s", buf);
270               else
271 #endif
272                 fputs (buf, stderr);
273
274               free (buf);
275               return 0;
276             }
277         default:
278           break;
279         }
280     }
281   while (*args && *args != '#');
282   return args;
283 }
284
285
286 static const char *
287 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
288 {
289   const char *start = args;
290   size_t len;
291
292   args = skip_string (args);
293   len = args - start;
294
295   if (len == 3 && __strncasecmp (start, "off", len) == 0)
296     _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
297   else
298     {
299       _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
300       if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
301           || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
302         _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
303     }
304   return args;
305 }
306
307
308 static const char *
309 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
310 {
311   if (__strncasecmp (args, "on", 2) == 0)
312     {
313       args += 2;
314       _res_hconf.flags |= flag;
315     }
316   else if (__strncasecmp (args, "off", 3) == 0)
317     {
318       args += 3;
319       _res_hconf.flags &= ~flag;
320     }
321   else
322     {
323       char *buf;
324
325       if (__asprintf (&buf,
326                       _("%s: line %d: expected `on' or `off', found `%s'\n"),
327                       fname, line_num, args) < 0)
328         return 0;
329
330 #ifdef USE_IN_LIBIO
331       if (_IO_fwide (stderr, 0) > 0)
332         __fwprintf (stderr, L"%s", buf);
333       else
334 #endif
335         fputs (buf, stderr);
336
337       free (buf);
338       return 0;
339     }
340   return args;
341 }
342
343
344 static void
345 parse_line (const char *fname, int line_num, const char *str)
346 {
347   const char *start;
348   struct cmd *c = 0;
349   size_t len;
350   size_t i;
351
352   str = skip_ws (str);
353
354   /* skip line comment and empty lines: */
355   if (*str == '\0' || *str == '#') return;
356
357   start = str;
358   str = skip_string (str);
359   len = str - start;
360
361   for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
362     {
363       if (__strncasecmp (start, cmd[i].name, len) == 0
364           && strlen (cmd[i].name) == len)
365         {
366           c = &cmd[i];
367           break;
368         }
369     }
370   if (c == NULL)
371     {
372       char *buf;
373
374       if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
375                       fname, line_num, start) < 0)
376         return;
377
378 #ifdef USE_IN_LIBIO
379       if (_IO_fwide (stderr, 0) > 0)
380         __fwprintf (stderr, L"%s", buf);
381       else
382 #endif
383         fputs (buf, stderr);
384
385       free (buf);
386       return;
387     }
388
389   /* process args: */
390   str = skip_ws (str);
391   str = (*c->parse_args) (fname, line_num, str, c->arg);
392   if (!str)
393     return;
394
395   /* rest of line must contain white space or comment only: */
396   while (*str)
397     {
398       if (!isspace (*str)) {
399         if (*str != '#')
400           {
401             char *buf;
402
403             if (__asprintf (&buf,
404                             _("%s: line %d: ignoring trailing garbage `%s'\n"),
405                             fname, line_num, str) < 0)
406               break;
407
408 #ifdef USE_IN_LIBIO
409             if (_IO_fwide (stderr, 0) > 0)
410               __fwprintf (stderr, L"%s", buf);
411             else
412 #endif
413               fputs (buf, stderr);
414
415             free (buf);
416           }
417         break;
418       }
419       ++str;
420     }
421 }
422
423
424 static void
425 do_init (void)
426 {
427   const char *hconf_name;
428   int line_num = 0;
429   char buf[256], *envval;
430   FILE *fp;
431
432   memset (&_res_hconf, '\0', sizeof (_res_hconf));
433
434   hconf_name = getenv (ENV_HOSTCONF);
435   if (hconf_name == NULL)
436     hconf_name = _PATH_HOSTCONF;
437
438   fp = fopen (hconf_name, "rc");
439   if (!fp)
440     /* make up something reasonable: */
441     _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
442   else
443     {
444       /* No threads using this stream.  */
445       __fsetlocking (fp, FSETLOCKING_BYCALLER);
446
447       while (fgets_unlocked (buf, sizeof (buf), fp))
448         {
449           ++line_num;
450           *__strchrnul (buf, '\n') = '\0';
451           parse_line (hconf_name, line_num, buf);
452         }
453       fclose (fp);
454     }
455
456   envval = getenv (ENV_SERVORDER);
457   if (envval)
458     {
459       _res_hconf.num_services = 0;
460       arg_service_list (ENV_SERVORDER, 1, envval, 0);
461     }
462
463   envval = getenv (ENV_SPOOF);
464   if (envval)
465     arg_spoof (ENV_SPOOF, 1, envval, 0);
466
467   envval = getenv (ENV_MULTI);
468   if (envval)
469     arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
470
471   envval = getenv (ENV_REORDER);
472   if (envval)
473     arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
474
475   envval = getenv (ENV_TRIM_ADD);
476   if (envval)
477     arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
478
479   envval = getenv (ENV_TRIM_OVERR);
480   if (envval)
481     {
482       _res_hconf.num_trimdomains = 0;
483       arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
484     }
485
486   _res_hconf.initialized = 1;
487 }
488
489
490 /* Initialize hconf datastructure by reading host.conf file and
491    environment variables.  */
492 void
493 _res_hconf_init (void)
494 {
495   __libc_once_define (static, once);
496
497   __libc_once (once, do_init);
498 }
499
500
501 /* List of known interfaces.  */
502 libc_freeres_ptr (
503 static struct netaddr
504 {
505   int addrtype;
506   union
507   {
508     struct
509     {
510       u_int32_t addr;
511       u_int32_t mask;
512     } ipv4;
513   } u;
514 } *ifaddrs);
515
516 /* We need to protect the dynamic buffer handling.  */
517 __libc_lock_define_initialized (static, lock);
518
519 /* Reorder addresses returned in a hostent such that the first address
520    is an address on the local subnet, if there is such an address.
521    Otherwise, nothing is changed.
522
523    Note that this function currently only handles IPv4 addresses.  */
524
525 void
526 _res_hconf_reorder_addrs (struct hostent *hp)
527 {
528 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
529   int i, j;
530   /* Number of interfaces.  */
531   static int num_ifs = -1;
532
533   /* Only reorder if we're supposed to.  */
534   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
535     return;
536
537   /* Can't deal with anything but IPv4 for now...  */
538   if (hp->h_addrtype != AF_INET)
539     return;
540
541   if (num_ifs <= 0)
542     {
543       struct ifreq *ifr, *cur_ifr;
544       int sd, num, i;
545       /* Save errno.  */
546       int save = errno;
547
548       /* Initialize interface table.  */
549
550       num_ifs = 0;
551
552       /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
553       sd = __socket (AF_INET, SOCK_DGRAM, 0);
554       if (sd < 0)
555         return;
556
557       /* Get lock.  */
558       __libc_lock_lock (lock);
559
560       /* Get a list of interfaces.  */
561       __ifreq (&ifr, &num, sd);
562       if (!ifr)
563         goto cleanup;
564
565       ifaddrs = malloc (num * sizeof (ifaddrs[0]));
566       if (!ifaddrs)
567         goto cleanup1;
568
569       /* Copy usable interfaces in ifaddrs structure.  */
570       for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
571         {
572           if (cur_ifr->ifr_addr.sa_family != AF_INET)
573             continue;
574
575           ifaddrs[num_ifs].addrtype = AF_INET;
576           ifaddrs[num_ifs].u.ipv4.addr =
577             ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
578
579           if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
580             continue;
581
582           ifaddrs[num_ifs].u.ipv4.mask =
583             ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
584
585           /* Now we're committed to this entry.  */
586           ++num_ifs;
587         }
588       /* Just keep enough memory to hold all the interfaces we want.  */
589       ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
590       assert (ifaddrs != NULL);
591
592     cleanup1:
593       __if_freereq (ifr, num);
594
595     cleanup:
596       /* Release lock, preserve error value, and close socket.  */
597       save = errno;
598       __libc_lock_unlock (lock);
599       __close (sd);
600     }
601
602   if (num_ifs == 0)
603     return;
604
605   /* Find an address for which we have a direct connection.  */
606   for (i = 0; hp->h_addr_list[i]; ++i)
607     {
608       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
609
610       for (j = 0; j < num_ifs; ++j)
611         {
612           u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
613           u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
614
615           if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
616             {
617               void *tmp;
618
619               tmp = hp->h_addr_list[i];
620               hp->h_addr_list[i] = hp->h_addr_list[0];
621               hp->h_addr_list[0] = tmp;
622               return;
623             }
624         }
625     }
626 #endif /* defined(SIOCGIFCONF) && ... */
627 }
628
629
630 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
631    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
632    original code applied all trimdomains in order, meaning that the
633    same domainname could be trimmed multiple times.  I believe this
634    was unintentional.  */
635 void
636 _res_hconf_trim_domain (char *hostname)
637 {
638   size_t hostname_len, trim_len;
639   int i;
640
641   hostname_len = strlen (hostname);
642
643   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
644     {
645       const char *trim = _res_hconf.trimdomain[i];
646
647       trim_len = strlen (trim);
648       if (hostname_len > trim_len
649           && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
650         {
651           hostname[hostname_len - trim_len] = '\0';
652           break;
653         }
654     }
655 }
656
657
658 /* Trim all hostnames/aliases in HP according to the trimdomain list.
659    Notice that HP is modified inplace!  */
660 void
661 _res_hconf_trim_domains (struct hostent *hp)
662 {
663   int i;
664
665   if (_res_hconf.num_trimdomains == 0)
666     return;
667
668   _res_hconf_trim_domain (hp->h_name);
669   for (i = 0; hp->h_aliases[i]; ++i)
670     _res_hconf_trim_domain (hp->h_aliases[i]);
671 }