Fix pthread_mutexattr_getrobust_np definition
[kopensolaris-gnu/glibc.git] / resolv / res_hconf.c
1 /* Copyright (C) 1993, 1995-2006, 2007 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_SPOOF       "RESOLV_SPOOF_CHECK"
57 #define ENV_TRIM_OVERR  "RESOLV_OVERRIDE_TRIM_DOMAINS"
58 #define ENV_TRIM_ADD    "RESOLV_ADD_TRIM_DOMAINS"
59 #define ENV_MULTI       "RESOLV_MULTI"
60 #define ENV_REORDER     "RESOLV_REORDER"
61
62 enum parse_cbs
63   {
64     CB_none,
65     CB_arg_trimdomain_list,
66     CB_arg_spoof,
67     CB_arg_bool
68   };
69
70 static const struct cmd
71 {
72   const char name[11];
73   uint8_t cb;
74   unsigned int arg;
75 } cmd[] =
76 {
77   {"order",             CB_none,                0},
78   {"trim",              CB_arg_trimdomain_list, 0},
79   {"spoof",             CB_arg_spoof,           0},
80   {"multi",             CB_arg_bool,            HCONF_FLAG_MULTI},
81   {"nospoof",           CB_arg_bool,            HCONF_FLAG_SPOOF},
82   {"spoofalert",        CB_arg_bool,            HCONF_FLAG_SPOOFALERT},
83   {"reorder",           CB_arg_bool,            HCONF_FLAG_REORDER}
84 };
85
86 /* Structure containing the state.  */
87 struct hconf _res_hconf;
88
89 /* Skip white space.  */
90 static const char *
91 skip_ws (const char *str)
92 {
93   while (isspace (*str)) ++str;
94   return str;
95 }
96
97
98 /* Skip until whitespace, comma, end of line, or comment character.  */
99 static const char *
100 skip_string (const char *str)
101 {
102   while (*str && !isspace (*str) && *str != '#' && *str != ',')
103     ++str;
104   return str;
105 }
106
107
108 static const char *
109 arg_trimdomain_list (const char *fname, int line_num, const char *args)
110 {
111   const char * start;
112   size_t len;
113
114   do
115     {
116       start = args;
117       args = skip_string (args);
118       len = args - start;
119
120       if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
121         {
122           char *buf;
123
124           if (__asprintf (&buf, _("\
125 %s: line %d: cannot specify more than %d trim domains"),
126                           fname, line_num, TRIMDOMAINS_MAX) < 0)
127             return 0;
128
129           __fxprintf (NULL, "%s", buf);
130
131           free (buf);
132           return 0;
133         }
134       _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
135         __strndup (start, len);
136       args = skip_ws (args);
137       switch (*args)
138         {
139         case ',': case ';': case ':':
140           args = skip_ws (++args);
141           if (!*args || *args == '#')
142             {
143               char *buf;
144
145               if (__asprintf (&buf, _("\
146 %s: line %d: list delimiter not followed by domain"),
147                               fname, line_num) < 0)
148                 return 0;
149
150               __fxprintf (NULL, "%s", buf);
151
152               free (buf);
153               return 0;
154             }
155         default:
156           break;
157         }
158     }
159   while (*args && *args != '#');
160   return args;
161 }
162
163
164 static const char *
165 arg_spoof (const char *fname, int line_num, const char *args)
166 {
167   const char *start = args;
168   size_t len;
169
170   args = skip_string (args);
171   len = args - start;
172
173   if (len == 3 && __strncasecmp (start, "off", len) == 0)
174     _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
175   else
176     {
177       _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
178       if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
179           || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
180         _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
181     }
182   return args;
183 }
184
185
186 static const char *
187 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
188 {
189   if (__strncasecmp (args, "on", 2) == 0)
190     {
191       args += 2;
192       _res_hconf.flags |= flag;
193     }
194   else if (__strncasecmp (args, "off", 3) == 0)
195     {
196       args += 3;
197       _res_hconf.flags &= ~flag;
198     }
199   else
200     {
201       char *buf;
202
203       if (__asprintf (&buf,
204                       _("%s: line %d: expected `on' or `off', found `%s'\n"),
205                       fname, line_num, args) < 0)
206         return 0;
207
208       __fxprintf (NULL, "%s", buf);
209
210       free (buf);
211       return 0;
212     }
213   return args;
214 }
215
216
217 static void
218 parse_line (const char *fname, int line_num, const char *str)
219 {
220   const char *start;
221   const struct cmd *c = 0;
222   size_t len;
223   size_t i;
224
225   str = skip_ws (str);
226
227   /* skip line comment and empty lines: */
228   if (*str == '\0' || *str == '#') return;
229
230   start = str;
231   str = skip_string (str);
232   len = str - start;
233
234   for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
235     {
236       if (__strncasecmp (start, cmd[i].name, len) == 0
237           && strlen (cmd[i].name) == len)
238         {
239           c = &cmd[i];
240           break;
241         }
242     }
243   if (c == NULL)
244     {
245       char *buf;
246
247       if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
248                       fname, line_num, start) < 0)
249         return;
250
251       __fxprintf (NULL, "%s", buf);
252
253       free (buf);
254       return;
255     }
256
257   /* process args: */
258   str = skip_ws (str);
259
260   if (c->cb == CB_arg_trimdomain_list)
261     str = arg_trimdomain_list (fname, line_num, str);
262   else if (c->cb == CB_arg_spoof)
263     str = arg_spoof (fname, line_num, str);
264   else if (c->cb == CB_arg_bool)
265     str = arg_bool (fname, line_num, str, c->arg);
266   else
267     /* Ignore the line.  */
268     return;
269
270   if (!str)
271     return;
272
273   /* rest of line must contain white space or comment only: */
274   while (*str)
275     {
276       if (!isspace (*str)) {
277         if (*str != '#')
278           {
279             char *buf;
280
281             if (__asprintf (&buf,
282                             _("%s: line %d: ignoring trailing garbage `%s'\n"),
283                             fname, line_num, str) < 0)
284               break;
285
286             __fxprintf (NULL, "%s", buf);
287
288             free (buf);
289           }
290         break;
291       }
292       ++str;
293     }
294 }
295
296
297 static void
298 do_init (void)
299 {
300   const char *hconf_name;
301   int line_num = 0;
302   char buf[256], *envval;
303   FILE *fp;
304
305   memset (&_res_hconf, '\0', sizeof (_res_hconf));
306
307   hconf_name = getenv (ENV_HOSTCONF);
308   if (hconf_name == NULL)
309     hconf_name = _PATH_HOSTCONF;
310
311   fp = fopen (hconf_name, "rc");
312   if (fp)
313     {
314       /* No threads using this stream.  */
315       __fsetlocking (fp, FSETLOCKING_BYCALLER);
316
317       while (fgets_unlocked (buf, sizeof (buf), fp))
318         {
319           ++line_num;
320           *__strchrnul (buf, '\n') = '\0';
321           parse_line (hconf_name, line_num, buf);
322         }
323       fclose (fp);
324     }
325
326   envval = getenv (ENV_SPOOF);
327   if (envval)
328     arg_spoof (ENV_SPOOF, 1, envval);
329
330   envval = getenv (ENV_MULTI);
331   if (envval)
332     arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
333
334   envval = getenv (ENV_REORDER);
335   if (envval)
336     arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
337
338   envval = getenv (ENV_TRIM_ADD);
339   if (envval)
340     arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
341
342   envval = getenv (ENV_TRIM_OVERR);
343   if (envval)
344     {
345       _res_hconf.num_trimdomains = 0;
346       arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
347     }
348
349   _res_hconf.initialized = 1;
350 }
351
352
353 /* Initialize hconf datastructure by reading host.conf file and
354    environment variables.  */
355 void
356 _res_hconf_init (void)
357 {
358   __libc_once_define (static, once);
359
360   __libc_once (once, do_init);
361 }
362
363
364 #ifndef NOT_IN_libc
365 /* List of known interfaces.  */
366 libc_freeres_ptr (
367 static struct netaddr
368 {
369   int addrtype;
370   union
371   {
372     struct
373     {
374       u_int32_t addr;
375       u_int32_t mask;
376     } ipv4;
377   } u;
378 } *ifaddrs);
379
380 /* We need to protect the dynamic buffer handling.  */
381 __libc_lock_define_initialized (static, lock);
382
383 /* Reorder addresses returned in a hostent such that the first address
384    is an address on the local subnet, if there is such an address.
385    Otherwise, nothing is changed.
386
387    Note that this function currently only handles IPv4 addresses.  */
388
389 void
390 _res_hconf_reorder_addrs (struct hostent *hp)
391 {
392 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
393   int i, j;
394   /* Number of interfaces.  */
395   static int num_ifs = -1;
396
397   /* Only reorder if we're supposed to.  */
398   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
399     return;
400
401   /* Can't deal with anything but IPv4 for now...  */
402   if (hp->h_addrtype != AF_INET)
403     return;
404
405   if (num_ifs <= 0)
406     {
407       struct ifreq *ifr, *cur_ifr;
408       int sd, num, i;
409       /* Save errno.  */
410       int save = errno;
411
412       /* Initialize interface table.  */
413
414       num_ifs = 0;
415
416       /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
417       sd = __socket (AF_INET, SOCK_DGRAM, 0);
418       if (sd < 0)
419         return;
420
421       /* Get lock.  */
422       __libc_lock_lock (lock);
423
424       /* Get a list of interfaces.  */
425       __ifreq (&ifr, &num, sd);
426       if (!ifr)
427         goto cleanup;
428
429       ifaddrs = malloc (num * sizeof (ifaddrs[0]));
430       if (!ifaddrs)
431         goto cleanup1;
432
433       /* Copy usable interfaces in ifaddrs structure.  */
434       for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
435         {
436           if (cur_ifr->ifr_addr.sa_family != AF_INET)
437             continue;
438
439           ifaddrs[num_ifs].addrtype = AF_INET;
440           ifaddrs[num_ifs].u.ipv4.addr =
441             ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
442
443           if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
444             continue;
445
446           ifaddrs[num_ifs].u.ipv4.mask =
447             ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
448
449           /* Now we're committed to this entry.  */
450           ++num_ifs;
451         }
452       /* Just keep enough memory to hold all the interfaces we want.  */
453       ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
454       assert (ifaddrs != NULL);
455
456     cleanup1:
457       __if_freereq (ifr, num);
458
459     cleanup:
460       /* Release lock, preserve error value, and close socket.  */
461       save = errno;
462       __libc_lock_unlock (lock);
463       __close (sd);
464     }
465
466   if (num_ifs == 0)
467     return;
468
469   /* Find an address for which we have a direct connection.  */
470   for (i = 0; hp->h_addr_list[i]; ++i)
471     {
472       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
473
474       for (j = 0; j < num_ifs; ++j)
475         {
476           u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
477           u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
478
479           if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
480             {
481               void *tmp;
482
483               tmp = hp->h_addr_list[i];
484               hp->h_addr_list[i] = hp->h_addr_list[0];
485               hp->h_addr_list[0] = tmp;
486               return;
487             }
488         }
489     }
490 #endif /* defined(SIOCGIFCONF) && ... */
491 }
492
493
494 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
495    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
496    original code applied all trimdomains in order, meaning that the
497    same domainname could be trimmed multiple times.  I believe this
498    was unintentional.  */
499 void
500 _res_hconf_trim_domain (char *hostname)
501 {
502   size_t hostname_len, trim_len;
503   int i;
504
505   hostname_len = strlen (hostname);
506
507   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
508     {
509       const char *trim = _res_hconf.trimdomain[i];
510
511       trim_len = strlen (trim);
512       if (hostname_len > trim_len
513           && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
514         {
515           hostname[hostname_len - trim_len] = '\0';
516           break;
517         }
518     }
519 }
520
521
522 /* Trim all hostnames/aliases in HP according to the trimdomain list.
523    Notice that HP is modified inplace!  */
524 void
525 _res_hconf_trim_domains (struct hostent *hp)
526 {
527   int i;
528
529   if (_res_hconf.num_trimdomains == 0)
530     return;
531
532   _res_hconf_trim_domain (hp->h_name);
533   for (i = 0; hp->h_aliases[i]; ++i)
534     _res_hconf_trim_domain (hp->h_aliases[i]);
535 }
536 #endif