Back to index

glibc  2.9
dns-network.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007, 2008
00002    Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /* Parts of this file are plain copies of the file `getnetnamadr.c' from
00022    the bind package and it has the following copyright.  */
00023 
00024 /* Copyright (c) 1993 Carlos Leandro and Rui Salgueiro
00025  *      Dep. Matematica Universidade de Coimbra, Portugal, Europe
00026  *
00027  * Permission to use, copy, modify, and distribute this software for any
00028  * purpose with or without fee is hereby granted, provided that the above
00029  * copyright notice and this permission notice appear in all copies.
00030  */
00031 /*
00032  * Copyright (c) 1983, 1993
00033  *      The Regents of the University of California.  All rights reserved.
00034  *
00035  * Redistribution and use in source and binary forms, with or without
00036  * modification, are permitted provided that the following conditions
00037  * are met:
00038  * 1. Redistributions of source code must retain the above copyright
00039  *    notice, this list of conditions and the following disclaimer.
00040  * 2. Redistributions in binary form must reproduce the above copyright
00041  *    notice, this list of conditions and the following disclaimer in the
00042  *    documentation and/or other materials provided with the distribution.
00043  * 4. Neither the name of the University nor the names of its contributors
00044  *    may be used to endorse or promote products derived from this software
00045  *    without specific prior written permission.
00046  *
00047  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00048  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00049  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00050  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00051  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00052  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00053  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00054  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00055  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00056  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00057  * SUCH DAMAGE.
00058  */
00059 
00060 #include <ctype.h>
00061 #include <errno.h>
00062 #include <netdb.h>
00063 #include <stdio.h>
00064 #include <stdlib.h>
00065 #include <string.h>
00066 
00067 #include "nsswitch.h"
00068 #include <arpa/inet.h>
00069 
00070 /* Maximum number of aliases we allow.  */
00071 #define MAX_NR_ALIASES      48
00072 
00073 
00074 #if PACKETSZ > 65536
00075 # define MAXPACKET   PACKETSZ
00076 #else
00077 # define MAXPACKET   65536
00078 #endif
00079 
00080 
00081 typedef enum
00082 {
00083   BYADDR,
00084   BYNAME
00085 } lookup_method;
00086 
00087 
00088 /* We need this time later.  */
00089 typedef union querybuf
00090 {
00091   HEADER hdr;
00092   u_char buf[MAXPACKET];
00093 } querybuf;
00094 
00095 /* These functions are defined in res_comp.c.  */
00096 #define NS_MAXCDNAME 255    /* maximum compressed domain name */
00097 extern int __ns_name_ntop (const u_char *, char *, size_t) __THROW;
00098 extern int __ns_name_unpack (const u_char *, const u_char *,
00099                           const u_char *, u_char *, size_t) __THROW;
00100 
00101 
00102 /* Prototypes for local functions.  */
00103 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
00104                                 struct netent *result, char *buffer,
00105                                 size_t buflen, int *errnop, int *h_errnop,
00106                                 lookup_method net_i);
00107 
00108 
00109 enum nss_status
00110 _nss_dns_getnetbyname_r (const char *name, struct netent *result,
00111                       char *buffer, size_t buflen, int *errnop,
00112                       int *herrnop)
00113 {
00114   /* Return entry for network with NAME.  */
00115   union
00116   {
00117     querybuf *buf;
00118     u_char *ptr;
00119   } net_buffer;
00120   querybuf *orig_net_buffer;
00121   int anslen;
00122   char *qbuf;
00123   enum nss_status status;
00124 
00125   if (__res_maybe_init (&_res, 0) == -1)
00126     return NSS_STATUS_UNAVAIL;
00127 
00128   qbuf = strdupa (name);
00129 
00130   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
00131 
00132   anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
00133                             1024, &net_buffer.ptr, NULL, NULL, NULL);
00134   if (anslen < 0)
00135     {
00136       /* Nothing found.  */
00137       *errnop = errno;
00138       if (net_buffer.buf != orig_net_buffer)
00139        free (net_buffer.buf);
00140       return (errno == ECONNREFUSED
00141              || errno == EPFNOSUPPORT
00142              || errno == EAFNOSUPPORT)
00143        ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
00144     }
00145 
00146   status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
00147                      errnop, herrnop, BYNAME);
00148   if (net_buffer.buf != orig_net_buffer)
00149     free (net_buffer.buf);
00150   return status;
00151 }
00152 
00153 
00154 enum nss_status
00155 _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
00156                       char *buffer, size_t buflen, int *errnop,
00157                       int *herrnop)
00158 {
00159   /* Return entry for network with NAME.  */
00160   enum nss_status status;
00161   union
00162   {
00163     querybuf *buf;
00164     u_char *ptr;
00165   } net_buffer;
00166   querybuf *orig_net_buffer;
00167   unsigned int net_bytes[4];
00168   char qbuf[MAXDNAME];
00169   int cnt, anslen;
00170   u_int32_t net2;
00171   int olderr = errno;
00172 
00173   /* No net address lookup for IPv6 yet.  */
00174   if (type != AF_INET)
00175     return NSS_STATUS_UNAVAIL;
00176 
00177   if (__res_maybe_init (&_res, 0) == -1)
00178     return NSS_STATUS_UNAVAIL;
00179 
00180   net2 = (u_int32_t) net;
00181   for (cnt = 4; net2 != 0; net2 >>= 8)
00182     net_bytes[--cnt] = net2 & 0xff;
00183 
00184   switch (cnt)
00185     {
00186     case 3:
00187       /* Class A network.  */
00188       sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]);
00189       break;
00190     case 2:
00191       /* Class B network.  */
00192       sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]);
00193       break;
00194     case 1:
00195       /* Class C network.  */
00196       sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
00197               net_bytes[1]);
00198       break;
00199     case 0:
00200       /* Class D - E network.  */
00201       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
00202               net_bytes[1], net_bytes[0]);
00203       break;
00204     }
00205 
00206   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
00207 
00208   anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
00209                            1024, &net_buffer.ptr, NULL, NULL, NULL);
00210   if (anslen < 0)
00211     {
00212       /* Nothing found.  */
00213       int err = errno;
00214       __set_errno (olderr);
00215       if (net_buffer.buf != orig_net_buffer)
00216        free (net_buffer.buf);
00217       return (err == ECONNREFUSED
00218              || err == EPFNOSUPPORT
00219              || err == EAFNOSUPPORT)
00220        ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
00221     }
00222 
00223   status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
00224                      errnop, herrnop, BYADDR);
00225   if (net_buffer.buf != orig_net_buffer)
00226     free (net_buffer.buf);
00227   if (status == NSS_STATUS_SUCCESS)
00228     {
00229       /* Strip trailing zeros.  */
00230       unsigned int u_net = net;    /* Maybe net should be unsigned?  */
00231 
00232       while ((u_net & 0xff) == 0 && u_net != 0)
00233        u_net >>= 8;
00234       result->n_net = u_net;
00235     }
00236 
00237   return status;
00238 }
00239 
00240 
00241 #undef offsetof
00242 #define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member)
00243 
00244 static enum nss_status
00245 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
00246             char *buffer, size_t buflen, int *errnop, int *h_errnop,
00247             lookup_method net_i)
00248 {
00249   /*
00250    * Find first satisfactory answer
00251    *
00252    *      answer --> +------------+  ( MESSAGE )
00253    *                 |   Header   |
00254    *                 +------------+
00255    *                 |  Question  | the question for the name server
00256    *                 +------------+
00257    *                 |   Answer   | RRs answering the question
00258    *                 +------------+
00259    *                 | Authority  | RRs pointing toward an authority
00260    *                 | Additional | RRs holding additional information
00261    *                 +------------+
00262    */
00263   struct net_data
00264   {
00265     char *aliases[MAX_NR_ALIASES];
00266     char linebuffer[0];
00267   } *net_data;
00268 
00269   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct net_data);
00270   buffer += pad;
00271 
00272   if (__builtin_expect (buflen < sizeof (*net_data) + pad, 0))
00273     {
00274       /* The buffer is too small.  */
00275     too_small:
00276       *errnop = ERANGE;
00277       *h_errnop = NETDB_INTERNAL;
00278       return NSS_STATUS_TRYAGAIN;
00279     }
00280   buflen -= pad;
00281 
00282   net_data = (struct net_data *) buffer;
00283   int linebuflen = buflen - offsetof (struct net_data, linebuffer);
00284   if (buflen - offsetof (struct net_data, linebuffer) != linebuflen)
00285     linebuflen = INT_MAX;
00286   const unsigned char *end_of_message = &answer->buf[anslen];
00287   const HEADER *header_pointer = &answer->hdr;
00288   /* #/records in the answer section.  */
00289   int answer_count =  ntohs (header_pointer->ancount);
00290   /* #/entries in the question section.  */
00291   int question_count = ntohs (header_pointer->qdcount);
00292   char *bp = net_data->linebuffer;
00293   const unsigned char *cp = &answer->buf[HFIXEDSZ];
00294   char **alias_pointer;
00295   int have_answer;
00296   char *ans;
00297   u_char packtmp[NS_MAXCDNAME];
00298 
00299   if (question_count == 0)
00300     {
00301       /* FIXME: the Sun version uses for host name lookup an additional
00302         parameter for pointing to h_errno.  this is missing here.
00303         OSF/1 has a per-thread h_errno variable.  */
00304       if (header_pointer->aa != 0)
00305        {
00306          __set_h_errno (HOST_NOT_FOUND);
00307          return NSS_STATUS_NOTFOUND;
00308        }
00309       else
00310        {
00311          __set_h_errno (TRY_AGAIN);
00312          return NSS_STATUS_TRYAGAIN;
00313        }
00314     }
00315 
00316   /* Skip the question part.  */
00317   while (question_count-- > 0)
00318     {
00319       int n = __dn_skipname (cp, end_of_message);
00320       if (n < 0 || end_of_message - (cp + n) < QFIXEDSZ)
00321        {
00322          __set_h_errno (NO_RECOVERY);
00323          return NSS_STATUS_UNAVAIL;
00324        }
00325       cp += n + QFIXEDSZ;
00326     }
00327 
00328   alias_pointer = result->n_aliases = &net_data->aliases[0];
00329   *alias_pointer = NULL;
00330   have_answer = 0;
00331   ans = NULL;
00332 
00333   while (--answer_count >= 0 && cp < end_of_message)
00334     {
00335       int n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
00336       int type, class;
00337 
00338       n = __ns_name_unpack (answer->buf, end_of_message, cp,
00339                          packtmp, sizeof packtmp);
00340       if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
00341        {
00342          if (errno == EMSGSIZE)
00343            goto too_small;
00344 
00345          n = -1;
00346        }
00347 
00348       if (n > 0 && bp[0] == '.')
00349        bp[0] = '\0';
00350 
00351       if (n < 0 || res_dnok (bp) == 0)
00352        break;
00353       cp += n;
00354       ans = strdupa (bp);
00355       GETSHORT (type, cp);
00356       GETSHORT (class, cp);
00357       cp += INT32SZ;        /* TTL */
00358       GETSHORT (n, cp);
00359 
00360       if (class == C_IN && type == T_PTR)
00361        {
00362          n = __ns_name_unpack (answer->buf, end_of_message, cp,
00363                             packtmp, sizeof packtmp);
00364          if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
00365            {
00366              if (errno == EMSGSIZE)
00367               goto too_small;
00368 
00369              n = -1;
00370            }
00371 
00372          if (n < 0 || !res_hnok (bp))
00373            {
00374              /* XXX What does this mean?  The original form from bind
00375                returns NULL. Incrementing cp has no effect in any case.
00376                What should I return here. ??? */
00377              cp += n;
00378              return NSS_STATUS_UNAVAIL;
00379            }
00380          cp += n;
00381          if (alias_pointer + 2 < &net_data->aliases[MAX_NR_ALIASES])
00382            {
00383              *alias_pointer++ = bp;
00384              n = strlen (bp) + 1;
00385              bp += n;
00386              linebuflen -= n;
00387              result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
00388              ++have_answer;
00389            }
00390        }
00391     }
00392 
00393   if (have_answer)
00394     {
00395       *alias_pointer = NULL;
00396       switch (net_i)
00397        {
00398        case BYADDR:
00399          result->n_name = *result->n_aliases++;
00400          result->n_net = 0L;
00401          return NSS_STATUS_SUCCESS;
00402 
00403        case BYNAME:
00404          {
00405            char **ap = result->n_aliases++;
00406            while (*ap != NULL)
00407              {
00408               /* Check each alias name for being of the forms:
00409                  4.3.2.1.in-addr.arpa            = net 1.2.3.4
00410                  3.2.1.in-addr.arpa              = net 0.1.2.3
00411                  2.1.in-addr.arpa         = net 0.0.1.2
00412                  1.in-addr.arpa           = net 0.0.0.1
00413               */
00414               uint32_t val = 0;    /* Accumulator for n_net value.  */
00415               unsigned int shift = 0; /* Which part we are parsing now.  */
00416               const char *p = *ap; /* Consuming the string.  */
00417               do
00418                 {
00419                   /* Match the leading 0 or 0[xX] base indicator.  */
00420                   unsigned int base = 10;
00421                   if (*p == '0' && p[1] != '.')
00422                     {
00423                      base = 8;
00424                      ++p;
00425                      if (*p == 'x' || *p == 'X')
00426                        {
00427                          base = 16;
00428                          ++p;
00429                          if (*p == '.')
00430                            break; /* No digit here.  Give up on alias.  */
00431                        }
00432                      if (*p == '\0')
00433                        break;
00434                     }
00435 
00436                   uint32_t part = 0; /* Accumulates this part's number.  */
00437                   do
00438                     {
00439                      if (isdigit (*p) && (*p - '0' < base))
00440                        part = (part * base) + (*p - '0');
00441                      else if (base == 16 && isxdigit (*p))
00442                        part = (part << 4) + 10 + (tolower (*p) - 'a');
00443                      ++p;
00444                     } while (*p != '\0' && *p != '.');
00445 
00446                   if (*p != '.')
00447                     break;  /* Bad form.  Give up on this name.  */
00448 
00449                   /* Install this as the next more significant byte.  */
00450                   val |= part << shift;
00451                   shift += 8;
00452                   ++p;
00453 
00454                   /* If we are out of digits now, there are two cases:
00455                      1. We are done with digits and now see "in-addr.arpa".
00456                      2. This is not the droid we are looking for.  */
00457                   if (!isdigit (*p) && !strcasecmp (p, "in-addr.arpa"))
00458                     {
00459                      result->n_net = val;
00460                      return NSS_STATUS_SUCCESS;
00461                     }
00462 
00463                   /* Keep going when we have seen fewer than 4 parts.  */
00464                 } while (shift < 32);
00465              }
00466          }
00467          break;
00468        }
00469     }
00470 
00471   __set_h_errno (TRY_AGAIN);
00472   return NSS_STATUS_TRYAGAIN;
00473 }