* wcsmbs/wcsatcliff.c (MEMCHR): Define.
[platform/upstream/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 /* Reorder addresses returned in a hostent such that the first address
381    is an address on the local subnet, if there is such an address.
382    Otherwise, nothing is changed.
383
384    Note that this function currently only handles IPv4 addresses.  */
385
386 void
387 _res_hconf_reorder_addrs (struct hostent *hp)
388 {
389 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
390   int i, j;
391   /* Number of interfaces.  */
392   static int num_ifs = -1;
393   /* We need to protect the dynamic buffer handling.  */
394   __libc_lock_define_initialized (static, lock);
395
396   /* Only reorder if we're supposed to.  */
397   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
398     return;
399
400   /* Can't deal with anything but IPv4 for now...  */
401   if (hp->h_addrtype != AF_INET)
402     return;
403
404   if (num_ifs <= 0)
405     {
406       struct ifreq *ifr, *cur_ifr;
407       int sd, num, i;
408       /* Save errno.  */
409       int save = errno;
410
411       /* Initialize interface table.  */
412
413       /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
414       sd = __socket (AF_INET, SOCK_DGRAM, 0);
415       if (sd < 0)
416         return;
417
418       /* Get lock.  */
419       __libc_lock_lock (lock);
420
421       /* Recheck, somebody else might have done the work by done.  */
422       if (num_ifs <= 0)
423         {
424           int new_num_ifs = 0;
425
426           /* Get a list of interfaces.  */
427           __ifreq (&ifr, &num, sd);
428           if (!ifr)
429             goto cleanup;
430
431           ifaddrs = malloc (num * sizeof (ifaddrs[0]));
432           if (!ifaddrs)
433             goto cleanup1;
434
435           /* Copy usable interfaces in ifaddrs structure.  */
436           for (cur_ifr = ifr, i = 0; i < num;
437                cur_ifr = __if_nextreq (cur_ifr), ++i)
438             {
439               if (cur_ifr->ifr_addr.sa_family != AF_INET)
440                 continue;
441
442               ifaddrs[new_num_ifs].addrtype = AF_INET;
443               ifaddrs[new_num_ifs].u.ipv4.addr =
444                 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
445
446               if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
447                 continue;
448
449               ifaddrs[new_num_ifs].u.ipv4.mask =
450                 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
451
452               /* Now we're committed to this entry.  */
453               ++new_num_ifs;
454             }
455           /* Just keep enough memory to hold all the interfaces we want.  */
456           ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
457           assert (ifaddrs != NULL);
458
459         cleanup1:
460           __if_freereq (ifr, num);
461
462         cleanup:
463           /* Release lock, preserve error value, and close socket.  */
464           save = errno;
465
466           num_ifs = new_num_ifs;
467
468           __libc_lock_unlock (lock);
469         }
470
471       __close (sd);
472     }
473
474   if (num_ifs == 0)
475     return;
476
477   /* Find an address for which we have a direct connection.  */
478   for (i = 0; hp->h_addr_list[i]; ++i)
479     {
480       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
481
482       for (j = 0; j < num_ifs; ++j)
483         {
484           u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
485           u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
486
487           if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
488             {
489               void *tmp;
490
491               tmp = hp->h_addr_list[i];
492               hp->h_addr_list[i] = hp->h_addr_list[0];
493               hp->h_addr_list[0] = tmp;
494               return;
495             }
496         }
497     }
498 #endif /* defined(SIOCGIFCONF) && ... */
499 }
500
501
502 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
503    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
504    original code applied all trimdomains in order, meaning that the
505    same domainname could be trimmed multiple times.  I believe this
506    was unintentional.  */
507 void
508 _res_hconf_trim_domain (char *hostname)
509 {
510   size_t hostname_len, trim_len;
511   int i;
512
513   hostname_len = strlen (hostname);
514
515   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
516     {
517       const char *trim = _res_hconf.trimdomain[i];
518
519       trim_len = strlen (trim);
520       if (hostname_len > trim_len
521           && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
522         {
523           hostname[hostname_len - trim_len] = '\0';
524           break;
525         }
526     }
527 }
528
529
530 /* Trim all hostnames/aliases in HP according to the trimdomain list.
531    Notice that HP is modified inplace!  */
532 void
533 _res_hconf_trim_domains (struct hostent *hp)
534 {
535   int i;
536
537   if (_res_hconf.num_trimdomains == 0)
538     return;
539
540   _res_hconf_trim_domain (hp->h_name);
541   for (i = 0; hp->h_aliases[i]; ++i)
542     _res_hconf_trim_domain (hp->h_aliases[i]);
543 }
544 #endif