about summary refs log tree commit diff
path: root/REORG.TODO/resolv/res_hconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/resolv/res_hconf.c')
-rw-r--r--REORG.TODO/resolv/res_hconf.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/REORG.TODO/resolv/res_hconf.c b/REORG.TODO/resolv/res_hconf.c
new file mode 100644
index 0000000000..8fc06e9abd
--- /dev/null
+++ b/REORG.TODO/resolv/res_hconf.c
@@ -0,0 +1,572 @@
+/* Copyright (C) 1993-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by David Mosberger (davidm@azstarnet.com).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This file provides a Linux /etc/host.conf compatible front end to
+   the various name resolvers (/etc/hosts, named, NIS server, etc.).
+   Though mostly compatibly, the following differences exist compared
+   to the original implementation:
+
+	- line comments can appear anywhere (not just at the beginning of
+	  a line)
+*/
+
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <libintl.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <libc-lock.h>
+#include "ifreq.h"
+#include "res_hconf.h"
+#include <wchar.h>
+#include <atomic.h>
+
+#if IS_IN (libc)
+# define fgets_unlocked __fgets_unlocked
+#endif
+
+#define _PATH_HOSTCONF	"/etc/host.conf"
+
+/* Environment vars that all user to override default behavior:  */
+#define ENV_HOSTCONF	"RESOLV_HOST_CONF"
+#define ENV_TRIM_OVERR	"RESOLV_OVERRIDE_TRIM_DOMAINS"
+#define ENV_TRIM_ADD	"RESOLV_ADD_TRIM_DOMAINS"
+#define ENV_MULTI	"RESOLV_MULTI"
+#define ENV_REORDER	"RESOLV_REORDER"
+
+enum parse_cbs
+  {
+    CB_none,
+    CB_arg_trimdomain_list,
+    CB_arg_bool
+  };
+
+static const struct cmd
+{
+  const char name[11];
+  uint8_t cb;
+  unsigned int arg;
+} cmd[] =
+{
+  {"order",		CB_none,		0},
+  {"trim",		CB_arg_trimdomain_list,	0},
+  {"multi",		CB_arg_bool,		HCONF_FLAG_MULTI},
+  {"reorder",		CB_arg_bool,		HCONF_FLAG_REORDER}
+};
+
+/* Structure containing the state.  */
+struct hconf _res_hconf;
+
+/* Skip white space.  */
+static const char *
+skip_ws (const char *str)
+{
+  while (isspace (*str)) ++str;
+  return str;
+}
+
+
+/* Skip until whitespace, comma, end of line, or comment character.  */
+static const char *
+skip_string (const char *str)
+{
+  while (*str && !isspace (*str) && *str != '#' && *str != ',')
+    ++str;
+  return str;
+}
+
+
+static const char *
+arg_trimdomain_list (const char *fname, int line_num, const char *args)
+{
+  const char * start;
+  size_t len;
+
+  do
+    {
+      start = args;
+      args = skip_string (args);
+      len = args - start;
+
+      if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
+	{
+	  char *buf;
+
+	  if (__asprintf (&buf, _("\
+%s: line %d: cannot specify more than %d trim domains"),
+			  fname, line_num, TRIMDOMAINS_MAX) < 0)
+	    return 0;
+
+	  __fxprintf (NULL, "%s", buf);
+
+	  free (buf);
+	  return 0;
+	}
+      _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
+	__strndup (start, len);
+      args = skip_ws (args);
+      switch (*args)
+	{
+	case ',': case ';': case ':':
+	  args = skip_ws (++args);
+	  if (!*args || *args == '#')
+	    {
+	      char *buf;
+
+	      if (__asprintf (&buf, _("\
+%s: line %d: list delimiter not followed by domain"),
+			      fname, line_num) < 0)
+		return 0;
+
+	      __fxprintf (NULL, "%s", buf);
+
+	      free (buf);
+	      return 0;
+	    }
+	default:
+	  break;
+	}
+    }
+  while (*args && *args != '#');
+  return args;
+}
+
+
+static const char *
+arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
+{
+  if (__strncasecmp (args, "on", 2) == 0)
+    {
+      args += 2;
+      _res_hconf.flags |= flag;
+    }
+  else if (__strncasecmp (args, "off", 3) == 0)
+    {
+      args += 3;
+      _res_hconf.flags &= ~flag;
+    }
+  else
+    {
+      char *buf;
+
+      if (__asprintf (&buf,
+		      _("%s: line %d: expected `on' or `off', found `%s'\n"),
+		      fname, line_num, args) < 0)
+	return 0;
+
+      __fxprintf (NULL, "%s", buf);
+
+      free (buf);
+      return 0;
+    }
+  return args;
+}
+
+
+static void
+parse_line (const char *fname, int line_num, const char *str)
+{
+  const char *start;
+  const struct cmd *c = 0;
+  size_t len;
+  size_t i;
+
+  str = skip_ws (str);
+
+  /* skip line comment and empty lines: */
+  if (*str == '\0' || *str == '#') return;
+
+  start = str;
+  str = skip_string (str);
+  len = str - start;
+
+  for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
+    {
+      if (__strncasecmp (start, cmd[i].name, len) == 0
+	  && strlen (cmd[i].name) == len)
+	{
+	  c = &cmd[i];
+	  break;
+	}
+    }
+  if (c == NULL)
+    {
+      char *buf;
+
+      if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
+		      fname, line_num, start) < 0)
+	return;
+
+      __fxprintf (NULL, "%s", buf);
+
+      free (buf);
+      return;
+    }
+
+  /* process args: */
+  str = skip_ws (str);
+
+  if (c->cb == CB_arg_trimdomain_list)
+    str = arg_trimdomain_list (fname, line_num, str);
+  else if (c->cb == CB_arg_bool)
+    str = arg_bool (fname, line_num, str, c->arg);
+  else
+    /* Ignore the line.  */
+    return;
+
+  if (!str)
+    return;
+
+  /* rest of line must contain white space or comment only: */
+  while (*str)
+    {
+      if (!isspace (*str)) {
+	if (*str != '#')
+	  {
+	    char *buf;
+
+	    if (__asprintf (&buf,
+			    _("%s: line %d: ignoring trailing garbage `%s'\n"),
+			    fname, line_num, str) < 0)
+	      break;
+
+	    __fxprintf (NULL, "%s", buf);
+
+	    free (buf);
+	  }
+	break;
+      }
+      ++str;
+    }
+}
+
+
+static void
+do_init (void)
+{
+  const char *hconf_name;
+  int line_num = 0;
+  char buf[256], *envval;
+  FILE *fp;
+
+  memset (&_res_hconf, '\0', sizeof (_res_hconf));
+
+  hconf_name = getenv (ENV_HOSTCONF);
+  if (hconf_name == NULL)
+    hconf_name = _PATH_HOSTCONF;
+
+  fp = fopen (hconf_name, "rce");
+  if (fp)
+    {
+      /* No threads using this stream.  */
+      __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
+      while (fgets_unlocked (buf, sizeof (buf), fp))
+	{
+	  ++line_num;
+	  *__strchrnul (buf, '\n') = '\0';
+	  parse_line (hconf_name, line_num, buf);
+	}
+      fclose (fp);
+    }
+
+  envval = getenv (ENV_MULTI);
+  if (envval)
+    arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
+
+  envval = getenv (ENV_REORDER);
+  if (envval)
+    arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
+
+  envval = getenv (ENV_TRIM_ADD);
+  if (envval)
+    arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
+
+  envval = getenv (ENV_TRIM_OVERR);
+  if (envval)
+    {
+      _res_hconf.num_trimdomains = 0;
+      arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
+    }
+
+  /* See comments on the declaration of _res_hconf.  */
+  atomic_store_release (&_res_hconf.initialized, 1);
+}
+
+
+/* Initialize hconf datastructure by reading host.conf file and
+   environment variables.  */
+void
+_res_hconf_init (void)
+{
+  __libc_once_define (static, once);
+
+  __libc_once (once, do_init);
+}
+
+
+#if IS_IN (libc)
+# if defined SIOCGIFCONF && defined SIOCGIFNETMASK
+/* List of known interfaces.  */
+libc_freeres_ptr (
+static struct netaddr
+{
+  int addrtype;
+  union
+  {
+    struct
+    {
+      u_int32_t	addr;
+      u_int32_t	mask;
+    } ipv4;
+  } u;
+} *ifaddrs);
+# endif
+
+/* Reorder addresses returned in a hostent such that the first address
+   is an address on the local subnet, if there is such an address.
+   Otherwise, nothing is changed.
+
+   Note that this function currently only handles IPv4 addresses.  */
+
+void
+_res_hconf_reorder_addrs (struct hostent *hp)
+{
+#if defined SIOCGIFCONF && defined SIOCGIFNETMASK
+  int i, j;
+  /* Number of interfaces.  Also serves as a flag for the
+     double-checked locking idiom.  */
+  static int num_ifs = -1;
+  /* Local copy of num_ifs, for non-atomic access.  */
+  int num_ifs_local;
+  /* We need to protect the dynamic buffer handling.  The lock is only
+     acquired during initialization.  Afterwards, a positive num_ifs
+     value indicates completed initialization.  */
+  __libc_lock_define_initialized (static, lock);
+
+  /* Only reorder if we're supposed to.  */
+  if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
+    return;
+
+  /* Can't deal with anything but IPv4 for now...  */
+  if (hp->h_addrtype != AF_INET)
+    return;
+
+  /* This load synchronizes with the release MO store in the
+     initialization block below.  */
+  num_ifs_local = atomic_load_acquire (&num_ifs);
+  if (num_ifs_local <= 0)
+    {
+      struct ifreq *ifr, *cur_ifr;
+      int sd, num, i;
+      /* Save errno.  */
+      int save = errno;
+
+      /* Initialize interface table.  */
+
+      /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
+      sd = __socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+      if (sd < 0)
+	return;
+
+      /* Get lock.  */
+      __libc_lock_lock (lock);
+
+      /* Recheck, somebody else might have done the work by now.  No
+	 ordering is required for the load because we have the lock,
+	 and num_ifs is only updated under the lock.  Also see (3) in
+	 the analysis below.  */
+      num_ifs_local = atomic_load_relaxed (&num_ifs);
+      if (num_ifs_local <= 0)
+	{
+	  /* This is the only block which writes to num_ifs.  It can
+	     be executed several times (sequentially) if
+	     initialization does not yield any interfaces, and num_ifs
+	     remains zero.  However, once we stored a positive value
+	     in num_ifs below, this block cannot be entered again due
+	     to the condition above.  */
+	  int new_num_ifs = 0;
+
+	  /* Get a list of interfaces.  */
+	  __ifreq (&ifr, &num, sd);
+	  if (!ifr)
+	    goto cleanup;
+
+	  ifaddrs = malloc (num * sizeof (ifaddrs[0]));
+	  if (!ifaddrs)
+	    goto cleanup1;
+
+	  /* Copy usable interfaces in ifaddrs structure.  */
+	  for (cur_ifr = ifr, i = 0; i < num;
+	       cur_ifr = __if_nextreq (cur_ifr), ++i)
+	    {
+	      union
+	      {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+	      } ss;
+
+	      if (cur_ifr->ifr_addr.sa_family != AF_INET)
+		continue;
+
+	      ifaddrs[new_num_ifs].addrtype = AF_INET;
+	      ss.sa = cur_ifr->ifr_addr;
+	      ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;
+
+	      if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
+		continue;
+
+	      ss.sa = cur_ifr->ifr_netmask;
+	      ifaddrs[new_num_ifs].u.ipv4.mask = ss.sin.sin_addr.s_addr;
+
+	      /* Now we're committed to this entry.  */
+	      ++new_num_ifs;
+	    }
+	  /* Just keep enough memory to hold all the interfaces we want.  */
+	  ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
+	  assert (ifaddrs != NULL);
+
+	cleanup1:
+	  __if_freereq (ifr, num);
+
+	cleanup:
+	  /* Release lock, preserve error value, and close socket.  */
+	  errno = save;
+
+	  /* Advertise successful initialization if new_num_ifs is
+	     positive (and no updates to ifaddrs are permitted after
+	     that).  Otherwise, num_ifs remains unchanged, at zero.
+	     This store synchronizes with the initial acquire MO
+	     load.  */
+	  atomic_store_release (&num_ifs, new_num_ifs);
+	  /* Keep the local copy current, to save another load.  */
+	  num_ifs_local = new_num_ifs;
+	}
+
+      __libc_lock_unlock (lock);
+
+      __close (sd);
+    }
+
+  /* num_ifs_local cannot be negative because the if statement above
+     covered this case.  It can still be zero if we just performed
+     initialization, but could not find any interfaces.  */
+  if (num_ifs_local == 0)
+    return;
+
+  /* The code below accesses ifaddrs, so we need to ensure that the
+     initialization happens-before this point.
+
+     The actual initialization is sequenced-before the release store
+     to num_ifs, and sequenced-before the end of the critical section.
+
+     This means there are three possible executions:
+
+     (1) The thread that initialized the data also uses it, so
+         sequenced-before is sufficient to ensure happens-before.
+
+     (2) The release MO store of num_ifs synchronizes-with the acquire
+         MO load, and the acquire MO load is sequenced before the use
+         of the initialized data below.
+
+     (3) We enter the critical section, and the relaxed MO load of
+         num_ifs yields a positive value.  The write to ifaddrs is
+         sequenced-before leaving the critical section.  Leaving the
+         critical section happens-before we entered the critical
+         section ourselves, which means that the write to ifaddrs
+         happens-before this point.
+
+     Consequently, all potential writes to ifaddrs (and the data it
+     points to) happens-before this point.  */
+
+  /* Find an address for which we have a direct connection.  */
+  for (i = 0; hp->h_addr_list[i]; ++i)
+    {
+      struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
+
+      for (j = 0; j < num_ifs_local; ++j)
+	{
+	  u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
+	  u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
+
+	  if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
+	    {
+	      void *tmp;
+
+	      tmp = hp->h_addr_list[i];
+	      hp->h_addr_list[i] = hp->h_addr_list[0];
+	      hp->h_addr_list[0] = tmp;
+	      return;
+	    }
+	}
+    }
+#endif /* defined(SIOCGIFCONF) && ... */
+}
+
+
+/* If HOSTNAME has a postfix matching any of the trimdomains, trim away
+   that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
+   original code applied all trimdomains in order, meaning that the
+   same domainname could be trimmed multiple times.  I believe this
+   was unintentional.  */
+void
+_res_hconf_trim_domain (char *hostname)
+{
+  size_t hostname_len, trim_len;
+  int i;
+
+  hostname_len = strlen (hostname);
+
+  for (i = 0; i < _res_hconf.num_trimdomains; ++i)
+    {
+      const char *trim = _res_hconf.trimdomain[i];
+
+      trim_len = strlen (trim);
+      if (hostname_len > trim_len
+	  && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
+	{
+	  hostname[hostname_len - trim_len] = '\0';
+	  break;
+	}
+    }
+}
+
+
+/* Trim all hostnames/aliases in HP according to the trimdomain list.
+   Notice that HP is modified inplace!  */
+void
+_res_hconf_trim_domains (struct hostent *hp)
+{
+  int i;
+
+  if (_res_hconf.num_trimdomains == 0)
+    return;
+
+  _res_hconf_trim_domain (hp->h_name);
+  for (i = 0; hp->h_aliases[i]; ++i)
+    _res_hconf_trim_domain (hp->h_aliases[i]);
+}
+#endif