Include <signal.h> in sysdeps/nptl/allocrtsig.c
[platform/upstream/glibc.git] / resolv / res_hconf.c
1 /* Copyright (C) 1993-2015 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, see
17    <http://www.gnu.org/licenses/>.  */
18
19 /* This file provides a Linux /etc/host.conf compatible front end to
20    the various name resolvers (/etc/hosts, named, NIS server, etc.).
21    Though mostly compatibly, the following differences exist compared
22    to the original implementation:
23
24         - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
25           environment variable (i.e., `off', `nowarn', or `warn').
26
27         - line comments can appear anywhere (not just at the beginning of
28           a line)
29 */
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <libintl.h>
35 #include <memory.h>
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <net/if.h>
41 #include <sys/ioctl.h>
42 #include <unistd.h>
43 #include <netinet/in.h>
44 #include <bits/libc-lock.h>
45 #include "ifreq.h"
46 #include "res_hconf.h"
47 #include <wchar.h>
48
49 #if IS_IN (libc)
50 # define fgets_unlocked __fgets_unlocked
51 #endif
52
53 #define _PATH_HOSTCONF  "/etc/host.conf"
54
55 /* Environment vars that all user to override default behavior:  */
56 #define ENV_HOSTCONF    "RESOLV_HOST_CONF"
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 enum parse_cbs
64   {
65     CB_none,
66     CB_arg_trimdomain_list,
67     CB_arg_spoof,
68     CB_arg_bool
69   };
70
71 static const struct cmd
72 {
73   const char name[11];
74   uint8_t cb;
75   unsigned int arg;
76 } cmd[] =
77 {
78   {"order",             CB_none,                0},
79   {"trim",              CB_arg_trimdomain_list, 0},
80   {"spoof",             CB_arg_spoof,           0},
81   {"multi",             CB_arg_bool,            HCONF_FLAG_MULTI},
82   {"nospoof",           CB_arg_bool,            HCONF_FLAG_SPOOF},
83   {"spoofalert",        CB_arg_bool,            HCONF_FLAG_SPOOFALERT},
84   {"reorder",           CB_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_trimdomain_list (const char *fname, int line_num, const char *args)
111 {
112   const char * start;
113   size_t len;
114
115   do
116     {
117       start = args;
118       args = skip_string (args);
119       len = args - start;
120
121       if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
122         {
123           char *buf;
124
125           if (__asprintf (&buf, _("\
126 %s: line %d: cannot specify more than %d trim domains"),
127                           fname, line_num, TRIMDOMAINS_MAX) < 0)
128             return 0;
129
130           __fxprintf (NULL, "%s", buf);
131
132           free (buf);
133           return 0;
134         }
135       _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
136         __strndup (start, len);
137       args = skip_ws (args);
138       switch (*args)
139         {
140         case ',': case ';': case ':':
141           args = skip_ws (++args);
142           if (!*args || *args == '#')
143             {
144               char *buf;
145
146               if (__asprintf (&buf, _("\
147 %s: line %d: list delimiter not followed by domain"),
148                               fname, line_num) < 0)
149                 return 0;
150
151               __fxprintf (NULL, "%s", buf);
152
153               free (buf);
154               return 0;
155             }
156         default:
157           break;
158         }
159     }
160   while (*args && *args != '#');
161   return args;
162 }
163
164
165 static const char *
166 arg_spoof (const char *fname, int line_num, const char *args)
167 {
168   const char *start = args;
169   size_t len;
170
171   args = skip_string (args);
172   len = args - start;
173
174   if (len == 3 && __strncasecmp (start, "off", len) == 0)
175     _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
176   else
177     {
178       _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
179       if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
180           || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
181         _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
182     }
183   return args;
184 }
185
186
187 static const char *
188 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
189 {
190   if (__strncasecmp (args, "on", 2) == 0)
191     {
192       args += 2;
193       _res_hconf.flags |= flag;
194     }
195   else if (__strncasecmp (args, "off", 3) == 0)
196     {
197       args += 3;
198       _res_hconf.flags &= ~flag;
199     }
200   else
201     {
202       char *buf;
203
204       if (__asprintf (&buf,
205                       _("%s: line %d: expected `on' or `off', found `%s'\n"),
206                       fname, line_num, args) < 0)
207         return 0;
208
209       __fxprintf (NULL, "%s", buf);
210
211       free (buf);
212       return 0;
213     }
214   return args;
215 }
216
217
218 static void
219 parse_line (const char *fname, int line_num, const char *str)
220 {
221   const char *start;
222   const struct cmd *c = 0;
223   size_t len;
224   size_t i;
225
226   str = skip_ws (str);
227
228   /* skip line comment and empty lines: */
229   if (*str == '\0' || *str == '#') return;
230
231   start = str;
232   str = skip_string (str);
233   len = str - start;
234
235   for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
236     {
237       if (__strncasecmp (start, cmd[i].name, len) == 0
238           && strlen (cmd[i].name) == len)
239         {
240           c = &cmd[i];
241           break;
242         }
243     }
244   if (c == NULL)
245     {
246       char *buf;
247
248       if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
249                       fname, line_num, start) < 0)
250         return;
251
252       __fxprintf (NULL, "%s", buf);
253
254       free (buf);
255       return;
256     }
257
258   /* process args: */
259   str = skip_ws (str);
260
261   if (c->cb == CB_arg_trimdomain_list)
262     str = arg_trimdomain_list (fname, line_num, str);
263   else if (c->cb == CB_arg_spoof)
264     str = arg_spoof (fname, line_num, str);
265   else if (c->cb == CB_arg_bool)
266     str = arg_bool (fname, line_num, str, c->arg);
267   else
268     /* Ignore the line.  */
269     return;
270
271   if (!str)
272     return;
273
274   /* rest of line must contain white space or comment only: */
275   while (*str)
276     {
277       if (!isspace (*str)) {
278         if (*str != '#')
279           {
280             char *buf;
281
282             if (__asprintf (&buf,
283                             _("%s: line %d: ignoring trailing garbage `%s'\n"),
284                             fname, line_num, str) < 0)
285               break;
286
287             __fxprintf (NULL, "%s", buf);
288
289             free (buf);
290           }
291         break;
292       }
293       ++str;
294     }
295 }
296
297
298 static void
299 do_init (void)
300 {
301   const char *hconf_name;
302   int line_num = 0;
303   char buf[256], *envval;
304   FILE *fp;
305
306   memset (&_res_hconf, '\0', sizeof (_res_hconf));
307
308   hconf_name = getenv (ENV_HOSTCONF);
309   if (hconf_name == NULL)
310     hconf_name = _PATH_HOSTCONF;
311
312   fp = fopen (hconf_name, "rce");
313   if (fp)
314     {
315       /* No threads using this stream.  */
316       __fsetlocking (fp, FSETLOCKING_BYCALLER);
317
318       while (fgets_unlocked (buf, sizeof (buf), fp))
319         {
320           ++line_num;
321           *__strchrnul (buf, '\n') = '\0';
322           parse_line (hconf_name, line_num, buf);
323         }
324       fclose (fp);
325     }
326
327   envval = getenv (ENV_SPOOF);
328   if (envval)
329     arg_spoof (ENV_SPOOF, 1, envval);
330
331   envval = getenv (ENV_MULTI);
332   if (envval)
333     arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
334
335   envval = getenv (ENV_REORDER);
336   if (envval)
337     arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
338
339   envval = getenv (ENV_TRIM_ADD);
340   if (envval)
341     arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
342
343   envval = getenv (ENV_TRIM_OVERR);
344   if (envval)
345     {
346       _res_hconf.num_trimdomains = 0;
347       arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
348     }
349
350   _res_hconf.initialized = 1;
351 }
352
353
354 /* Initialize hconf datastructure by reading host.conf file and
355    environment variables.  */
356 void
357 _res_hconf_init (void)
358 {
359   __libc_once_define (static, once);
360
361   __libc_once (once, do_init);
362 }
363
364
365 #if IS_IN (libc)
366 # if defined SIOCGIFCONF && defined SIOCGIFNETMASK
367 /* List of known interfaces.  */
368 libc_freeres_ptr (
369 static struct netaddr
370 {
371   int addrtype;
372   union
373   {
374     struct
375     {
376       u_int32_t addr;
377       u_int32_t mask;
378     } ipv4;
379   } u;
380 } *ifaddrs);
381 # endif
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   /* We need to protect the dynamic buffer handling.  */
397   __libc_lock_define_initialized (static, lock);
398
399   /* Only reorder if we're supposed to.  */
400   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
401     return;
402
403   /* Can't deal with anything but IPv4 for now...  */
404   if (hp->h_addrtype != AF_INET)
405     return;
406
407   if (num_ifs <= 0)
408     {
409       struct ifreq *ifr, *cur_ifr;
410       int sd, num, i;
411       /* Save errno.  */
412       int save = errno;
413
414       /* Initialize interface table.  */
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       /* Recheck, somebody else might have done the work by done.  */
425       if (num_ifs <= 0)
426         {
427           int new_num_ifs = 0;
428
429           /* Get a list of interfaces.  */
430           __ifreq (&ifr, &num, sd);
431           if (!ifr)
432             goto cleanup;
433
434           ifaddrs = malloc (num * sizeof (ifaddrs[0]));
435           if (!ifaddrs)
436             goto cleanup1;
437
438           /* Copy usable interfaces in ifaddrs structure.  */
439           for (cur_ifr = ifr, i = 0; i < num;
440                cur_ifr = __if_nextreq (cur_ifr), ++i)
441             {
442               if (cur_ifr->ifr_addr.sa_family != AF_INET)
443                 continue;
444
445               ifaddrs[new_num_ifs].addrtype = AF_INET;
446               ifaddrs[new_num_ifs].u.ipv4.addr =
447                 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
448
449               if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
450                 continue;
451
452               ifaddrs[new_num_ifs].u.ipv4.mask =
453                 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
454
455               /* Now we're committed to this entry.  */
456               ++new_num_ifs;
457             }
458           /* Just keep enough memory to hold all the interfaces we want.  */
459           ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
460           assert (ifaddrs != NULL);
461
462         cleanup1:
463           __if_freereq (ifr, num);
464
465         cleanup:
466           /* Release lock, preserve error value, and close socket.  */
467           errno = save;
468
469           num_ifs = new_num_ifs;
470
471           __libc_lock_unlock (lock);
472         }
473
474       __close (sd);
475     }
476
477   if (num_ifs == 0)
478     return;
479
480   /* Find an address for which we have a direct connection.  */
481   for (i = 0; hp->h_addr_list[i]; ++i)
482     {
483       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
484
485       for (j = 0; j < num_ifs; ++j)
486         {
487           u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
488           u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
489
490           if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
491             {
492               void *tmp;
493
494               tmp = hp->h_addr_list[i];
495               hp->h_addr_list[i] = hp->h_addr_list[0];
496               hp->h_addr_list[0] = tmp;
497               return;
498             }
499         }
500     }
501 #endif /* defined(SIOCGIFCONF) && ... */
502 }
503
504
505 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
506    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
507    original code applied all trimdomains in order, meaning that the
508    same domainname could be trimmed multiple times.  I believe this
509    was unintentional.  */
510 void
511 _res_hconf_trim_domain (char *hostname)
512 {
513   size_t hostname_len, trim_len;
514   int i;
515
516   hostname_len = strlen (hostname);
517
518   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
519     {
520       const char *trim = _res_hconf.trimdomain[i];
521
522       trim_len = strlen (trim);
523       if (hostname_len > trim_len
524           && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
525         {
526           hostname[hostname_len - trim_len] = '\0';
527           break;
528         }
529     }
530 }
531
532
533 /* Trim all hostnames/aliases in HP according to the trimdomain list.
534    Notice that HP is modified inplace!  */
535 void
536 _res_hconf_trim_domains (struct hostent *hp)
537 {
538   int i;
539
540   if (_res_hconf.num_trimdomains == 0)
541     return;
542
543   _res_hconf_trim_domain (hp->h_name);
544   for (i = 0; hp->h_aliases[i]; ++i)
545     _res_hconf_trim_domain (hp->h_aliases[i]);
546 }
547 #endif