source: trunk/libs/newlib/src/newlib/libc/sys/linux/net/res_hconf.c

Last change on this file was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

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