Back to index

glibc  2.9
res_hconf.c
Go to the documentation of this file.
00001 /* Copyright (C) 1993, 1995-2006, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by David Mosberger (davidm@azstarnet.com).
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 /* This file provides a Linux /etc/host.conf compatible front end to
00021    the various name resolvers (/etc/hosts, named, NIS server, etc.).
00022    Though mostly compatibly, the following differences exist compared
00023    to the original implementation:
00024 
00025        - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
00026          environment variable (i.e., `off', `nowarn', or `warn').
00027 
00028        - line comments can appear anywhere (not just at the beginning of
00029          a line)
00030 */
00031 
00032 #include <assert.h>
00033 #include <errno.h>
00034 #include <ctype.h>
00035 #include <libintl.h>
00036 #include <memory.h>
00037 #include <stdio.h>
00038 #include <stdio_ext.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <net/if.h>
00042 #include <sys/ioctl.h>
00043 #include <unistd.h>
00044 #include <netinet/in.h>
00045 #include <bits/libc-lock.h>
00046 #include "ifreq.h"
00047 #include "res_hconf.h"
00048 #ifdef USE_IN_LIBIO
00049 # include <wchar.h>
00050 #endif
00051 
00052 #define _PATH_HOSTCONF      "/etc/host.conf"
00053 
00054 /* Environment vars that all user to override default behavior:  */
00055 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
00056 #define ENV_SPOOF    "RESOLV_SPOOF_CHECK"
00057 #define ENV_TRIM_OVERR      "RESOLV_OVERRIDE_TRIM_DOMAINS"
00058 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
00059 #define ENV_MULTI    "RESOLV_MULTI"
00060 #define ENV_REORDER  "RESOLV_REORDER"
00061 
00062 enum parse_cbs
00063   {
00064     CB_none,
00065     CB_arg_trimdomain_list,
00066     CB_arg_spoof,
00067     CB_arg_bool
00068   };
00069 
00070 static const struct cmd
00071 {
00072   const char name[11];
00073   uint8_t cb;
00074   unsigned int arg;
00075 } cmd[] =
00076 {
00077   {"order",          CB_none,             0},
00078   {"trim",           CB_arg_trimdomain_list,     0},
00079   {"spoof",          CB_arg_spoof,        0},
00080   {"multi",          CB_arg_bool,         HCONF_FLAG_MULTI},
00081   {"nospoof",        CB_arg_bool,         HCONF_FLAG_SPOOF},
00082   {"spoofalert",     CB_arg_bool,         HCONF_FLAG_SPOOFALERT},
00083   {"reorder",        CB_arg_bool,         HCONF_FLAG_REORDER}
00084 };
00085 
00086 /* Structure containing the state.  */
00087 struct hconf _res_hconf;
00088 
00089 /* Skip white space.  */
00090 static const char *
00091 skip_ws (const char *str)
00092 {
00093   while (isspace (*str)) ++str;
00094   return str;
00095 }
00096 
00097 
00098 /* Skip until whitespace, comma, end of line, or comment character.  */
00099 static const char *
00100 skip_string (const char *str)
00101 {
00102   while (*str && !isspace (*str) && *str != '#' && *str != ',')
00103     ++str;
00104   return str;
00105 }
00106 
00107 
00108 static const char *
00109 arg_trimdomain_list (const char *fname, int line_num, const char *args)
00110 {
00111   const char * start;
00112   size_t len;
00113 
00114   do
00115     {
00116       start = args;
00117       args = skip_string (args);
00118       len = args - start;
00119 
00120       if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
00121        {
00122          char *buf;
00123 
00124          if (__asprintf (&buf, _("\
00125 %s: line %d: cannot specify more than %d trim domains"),
00126                        fname, line_num, TRIMDOMAINS_MAX) < 0)
00127            return 0;
00128 
00129          __fxprintf (NULL, "%s", buf);
00130 
00131          free (buf);
00132          return 0;
00133        }
00134       _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
00135        __strndup (start, len);
00136       args = skip_ws (args);
00137       switch (*args)
00138        {
00139        case ',': case ';': case ':':
00140          args = skip_ws (++args);
00141          if (!*args || *args == '#')
00142            {
00143              char *buf;
00144 
00145              if (__asprintf (&buf, _("\
00146 %s: line %d: list delimiter not followed by domain"),
00147                            fname, line_num) < 0)
00148               return 0;
00149 
00150              __fxprintf (NULL, "%s", buf);
00151 
00152              free (buf);
00153              return 0;
00154            }
00155        default:
00156          break;
00157        }
00158     }
00159   while (*args && *args != '#');
00160   return args;
00161 }
00162 
00163 
00164 static const char *
00165 arg_spoof (const char *fname, int line_num, const char *args)
00166 {
00167   const char *start = args;
00168   size_t len;
00169 
00170   args = skip_string (args);
00171   len = args - start;
00172 
00173   if (len == 3 && __strncasecmp (start, "off", len) == 0)
00174     _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
00175   else
00176     {
00177       _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
00178       if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
00179          || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
00180        _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
00181     }
00182   return args;
00183 }
00184 
00185 
00186 static const char *
00187 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
00188 {
00189   if (__strncasecmp (args, "on", 2) == 0)
00190     {
00191       args += 2;
00192       _res_hconf.flags |= flag;
00193     }
00194   else if (__strncasecmp (args, "off", 3) == 0)
00195     {
00196       args += 3;
00197       _res_hconf.flags &= ~flag;
00198     }
00199   else
00200     {
00201       char *buf;
00202 
00203       if (__asprintf (&buf,
00204                     _("%s: line %d: expected `on' or `off', found `%s'\n"),
00205                     fname, line_num, args) < 0)
00206        return 0;
00207 
00208       __fxprintf (NULL, "%s", buf);
00209 
00210       free (buf);
00211       return 0;
00212     }
00213   return args;
00214 }
00215 
00216 
00217 static void
00218 parse_line (const char *fname, int line_num, const char *str)
00219 {
00220   const char *start;
00221   const struct cmd *c = 0;
00222   size_t len;
00223   size_t i;
00224 
00225   str = skip_ws (str);
00226 
00227   /* skip line comment and empty lines: */
00228   if (*str == '\0' || *str == '#') return;
00229 
00230   start = str;
00231   str = skip_string (str);
00232   len = str - start;
00233 
00234   for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
00235     {
00236       if (__strncasecmp (start, cmd[i].name, len) == 0
00237          && strlen (cmd[i].name) == len)
00238        {
00239          c = &cmd[i];
00240          break;
00241        }
00242     }
00243   if (c == NULL)
00244     {
00245       char *buf;
00246 
00247       if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
00248                     fname, line_num, start) < 0)
00249        return;
00250 
00251       __fxprintf (NULL, "%s", buf);
00252 
00253       free (buf);
00254       return;
00255     }
00256 
00257   /* process args: */
00258   str = skip_ws (str);
00259 
00260   if (c->cb == CB_arg_trimdomain_list)
00261     str = arg_trimdomain_list (fname, line_num, str);
00262   else if (c->cb == CB_arg_spoof)
00263     str = arg_spoof (fname, line_num, str);
00264   else if (c->cb == CB_arg_bool)
00265     str = arg_bool (fname, line_num, str, c->arg);
00266   else
00267     /* Ignore the line.  */
00268     return;
00269 
00270   if (!str)
00271     return;
00272 
00273   /* rest of line must contain white space or comment only: */
00274   while (*str)
00275     {
00276       if (!isspace (*str)) {
00277        if (*str != '#')
00278          {
00279            char *buf;
00280 
00281            if (__asprintf (&buf,
00282                          _("%s: line %d: ignoring trailing garbage `%s'\n"),
00283                          fname, line_num, str) < 0)
00284              break;
00285 
00286            __fxprintf (NULL, "%s", buf);
00287 
00288            free (buf);
00289          }
00290        break;
00291       }
00292       ++str;
00293     }
00294 }
00295 
00296 
00297 static void
00298 do_init (void)
00299 {
00300   const char *hconf_name;
00301   int line_num = 0;
00302   char buf[256], *envval;
00303   FILE *fp;
00304 
00305   memset (&_res_hconf, '\0', sizeof (_res_hconf));
00306 
00307   hconf_name = getenv (ENV_HOSTCONF);
00308   if (hconf_name == NULL)
00309     hconf_name = _PATH_HOSTCONF;
00310 
00311   fp = fopen (hconf_name, "rc");
00312   if (fp)
00313     {
00314       /* No threads using this stream.  */
00315       __fsetlocking (fp, FSETLOCKING_BYCALLER);
00316 
00317       while (fgets_unlocked (buf, sizeof (buf), fp))
00318        {
00319          ++line_num;
00320          *__strchrnul (buf, '\n') = '\0';
00321          parse_line (hconf_name, line_num, buf);
00322        }
00323       fclose (fp);
00324     }
00325 
00326   envval = getenv (ENV_SPOOF);
00327   if (envval)
00328     arg_spoof (ENV_SPOOF, 1, envval);
00329 
00330   envval = getenv (ENV_MULTI);
00331   if (envval)
00332     arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
00333 
00334   envval = getenv (ENV_REORDER);
00335   if (envval)
00336     arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
00337 
00338   envval = getenv (ENV_TRIM_ADD);
00339   if (envval)
00340     arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
00341 
00342   envval = getenv (ENV_TRIM_OVERR);
00343   if (envval)
00344     {
00345       _res_hconf.num_trimdomains = 0;
00346       arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
00347     }
00348 
00349   _res_hconf.initialized = 1;
00350 }
00351 
00352 
00353 /* Initialize hconf datastructure by reading host.conf file and
00354    environment variables.  */
00355 void
00356 _res_hconf_init (void)
00357 {
00358   __libc_once_define (static, once);
00359 
00360   __libc_once (once, do_init);
00361 }
00362 
00363 
00364 #ifndef NOT_IN_libc
00365 /* List of known interfaces.  */
00366 libc_freeres_ptr (
00367 static struct netaddr
00368 {
00369   int addrtype;
00370   union
00371   {
00372     struct
00373     {
00374       u_int32_t      addr;
00375       u_int32_t      mask;
00376     } ipv4;
00377   } u;
00378 } *ifaddrs);
00379 
00380 /* Reorder addresses returned in a hostent such that the first address
00381    is an address on the local subnet, if there is such an address.
00382    Otherwise, nothing is changed.
00383 
00384    Note that this function currently only handles IPv4 addresses.  */
00385 
00386 void
00387 _res_hconf_reorder_addrs (struct hostent *hp)
00388 {
00389 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
00390   int i, j;
00391   /* Number of interfaces.  */
00392   static int num_ifs = -1;
00393   /* We need to protect the dynamic buffer handling.  */
00394   __libc_lock_define_initialized (static, lock);
00395 
00396   /* Only reorder if we're supposed to.  */
00397   if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
00398     return;
00399 
00400   /* Can't deal with anything but IPv4 for now...  */
00401   if (hp->h_addrtype != AF_INET)
00402     return;
00403 
00404   if (num_ifs <= 0)
00405     {
00406       struct ifreq *ifr, *cur_ifr;
00407       int sd, num, i;
00408       /* Save errno.  */
00409       int save = errno;
00410 
00411       /* Initialize interface table.  */
00412 
00413       /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */
00414       sd = __socket (AF_INET, SOCK_DGRAM, 0);
00415       if (sd < 0)
00416        return;
00417 
00418       /* Get lock.  */
00419       __libc_lock_lock (lock);
00420 
00421       /* Recheck, somebody else might have done the work by done.  */
00422       if (num_ifs <= 0)
00423        {
00424          int new_num_ifs = 0;
00425 
00426          /* Get a list of interfaces.  */
00427          __ifreq (&ifr, &num, sd);
00428          if (!ifr)
00429            goto cleanup;
00430 
00431          ifaddrs = malloc (num * sizeof (ifaddrs[0]));
00432          if (!ifaddrs)
00433            goto cleanup1;
00434 
00435          /* Copy usable interfaces in ifaddrs structure.  */
00436          for (cur_ifr = ifr, i = 0; i < num;
00437               cur_ifr = __if_nextreq (cur_ifr), ++i)
00438            {
00439              if (cur_ifr->ifr_addr.sa_family != AF_INET)
00440               continue;
00441 
00442              ifaddrs[new_num_ifs].addrtype = AF_INET;
00443              ifaddrs[new_num_ifs].u.ipv4.addr =
00444               ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
00445 
00446              if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
00447               continue;
00448 
00449              ifaddrs[new_num_ifs].u.ipv4.mask =
00450               ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
00451 
00452              /* Now we're committed to this entry.  */
00453              ++new_num_ifs;
00454            }
00455          /* Just keep enough memory to hold all the interfaces we want.  */
00456          ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
00457          assert (ifaddrs != NULL);
00458 
00459        cleanup1:
00460          __if_freereq (ifr, num);
00461 
00462        cleanup:
00463          /* Release lock, preserve error value, and close socket.  */
00464          save = errno;
00465 
00466          num_ifs = new_num_ifs;
00467 
00468          __libc_lock_unlock (lock);
00469        }
00470 
00471       __close (sd);
00472     }
00473 
00474   if (num_ifs == 0)
00475     return;
00476 
00477   /* Find an address for which we have a direct connection.  */
00478   for (i = 0; hp->h_addr_list[i]; ++i)
00479     {
00480       struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
00481 
00482       for (j = 0; j < num_ifs; ++j)
00483        {
00484          u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr;
00485          u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
00486 
00487          if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
00488            {
00489              void *tmp;
00490 
00491              tmp = hp->h_addr_list[i];
00492              hp->h_addr_list[i] = hp->h_addr_list[0];
00493              hp->h_addr_list[0] = tmp;
00494              return;
00495            }
00496        }
00497     }
00498 #endif /* defined(SIOCGIFCONF) && ... */
00499 }
00500 
00501 
00502 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
00503    that postfix.  Notice that HOSTNAME is modified inplace.  Also, the
00504    original code applied all trimdomains in order, meaning that the
00505    same domainname could be trimmed multiple times.  I believe this
00506    was unintentional.  */
00507 void
00508 _res_hconf_trim_domain (char *hostname)
00509 {
00510   size_t hostname_len, trim_len;
00511   int i;
00512 
00513   hostname_len = strlen (hostname);
00514 
00515   for (i = 0; i < _res_hconf.num_trimdomains; ++i)
00516     {
00517       const char *trim = _res_hconf.trimdomain[i];
00518 
00519       trim_len = strlen (trim);
00520       if (hostname_len > trim_len
00521          && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
00522        {
00523          hostname[hostname_len - trim_len] = '\0';
00524          break;
00525        }
00526     }
00527 }
00528 
00529 
00530 /* Trim all hostnames/aliases in HP according to the trimdomain list.
00531    Notice that HP is modified inplace!  */
00532 void
00533 _res_hconf_trim_domains (struct hostent *hp)
00534 {
00535   int i;
00536 
00537   if (_res_hconf.num_trimdomains == 0)
00538     return;
00539 
00540   _res_hconf_trim_domain (hp->h_name);
00541   for (i = 0; hp->h_aliases[i]; ++i)
00542     _res_hconf_trim_domain (hp->h_aliases[i]);
00543 }
00544 #endif