Back to index

nagios-plugins  1.4.16
getaddrinfo.c
Go to the documentation of this file.
00001 /* Get address information (partial implementation).
00002    Copyright (C) 1997, 2001-2002, 2004-2010 Free Software Foundation, Inc.
00003    Contributed by Simon Josefsson <simon@josefsson.org>.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; either version 3, or (at your option)
00008    any later version.
00009 
00010    This program 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
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
00018 
00019 #include <config.h>
00020 
00021 /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
00022    optimizes away the sa == NULL test below.  */
00023 #define _GL_ARG_NONNULL(params)
00024 
00025 #include <netdb.h>
00026 
00027 #if HAVE_NETINET_IN_H
00028 # include <netinet/in.h>
00029 #endif
00030 
00031 /* Get inet_ntop.  */
00032 #include <arpa/inet.h>
00033 
00034 /* Get calloc. */
00035 #include <stdlib.h>
00036 
00037 /* Get memcpy, strdup. */
00038 #include <string.h>
00039 
00040 /* Get snprintf. */
00041 #include <stdio.h>
00042 
00043 #include <stdbool.h>
00044 
00045 #include "gettext.h"
00046 #define _(String) gettext (String)
00047 #define N_(String) String
00048 
00049 /* BeOS has AF_INET, but not PF_INET.  */
00050 #ifndef PF_INET
00051 # define PF_INET AF_INET
00052 #endif
00053 /* BeOS also lacks PF_UNSPEC.  */
00054 #ifndef PF_UNSPEC
00055 # define PF_UNSPEC 0
00056 #endif
00057 
00058 #if defined _WIN32 || defined __WIN32__
00059 # define WIN32_NATIVE
00060 #endif
00061 
00062 #ifdef WIN32_NATIVE
00063 typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
00064                                         const struct addrinfo*,
00065                                         struct addrinfo**);
00066 typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
00067 typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
00068                                         socklen_t, char*, DWORD,
00069                                         char*, DWORD, int);
00070 
00071 static getaddrinfo_func getaddrinfo_ptr = NULL;
00072 static freeaddrinfo_func freeaddrinfo_ptr = NULL;
00073 static getnameinfo_func getnameinfo_ptr = NULL;
00074 
00075 static int
00076 use_win32_p (void)
00077 {
00078   static int done = 0;
00079   HMODULE h;
00080 
00081   if (done)
00082     return getaddrinfo_ptr ? 1 : 0;
00083 
00084   done = 1;
00085 
00086   h = GetModuleHandle ("ws2_32.dll");
00087 
00088   if (h)
00089     {
00090       getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
00091       freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
00092       getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
00093     }
00094 
00095   /* If either is missing, something is odd. */
00096   if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
00097     {
00098       getaddrinfo_ptr = NULL;
00099       freeaddrinfo_ptr = NULL;
00100       getnameinfo_ptr = NULL;
00101       return 0;
00102     }
00103 
00104   return 1;
00105 }
00106 #endif
00107 
00108 static inline bool
00109 validate_family (int family)
00110 {
00111   /* FIXME: Support more families. */
00112 #if HAVE_IPV4
00113      if (family == PF_INET)
00114        return true;
00115 #endif
00116 #if HAVE_IPV6
00117      if (family == PF_INET6)
00118        return true;
00119 #endif
00120      if (family == PF_UNSPEC)
00121        return true;
00122      return false;
00123 }
00124 
00125 /* Translate name of a service location and/or a service name to set of
00126    socket addresses. */
00127 int
00128 getaddrinfo (const char *restrict nodename,
00129              const char *restrict servname,
00130              const struct addrinfo *restrict hints,
00131              struct addrinfo **restrict res)
00132 {
00133   struct addrinfo *tmp;
00134   int port = 0;
00135   struct hostent *he;
00136   void *storage;
00137   size_t size;
00138 #if HAVE_IPV6
00139   struct v6_pair {
00140     struct addrinfo addrinfo;
00141     struct sockaddr_in6 sockaddr_in6;
00142   };
00143 #endif
00144 #if HAVE_IPV4
00145   struct v4_pair {
00146     struct addrinfo addrinfo;
00147     struct sockaddr_in sockaddr_in;
00148   };
00149 #endif
00150 
00151 #ifdef WIN32_NATIVE
00152   if (use_win32_p ())
00153     return getaddrinfo_ptr (nodename, servname, hints, res);
00154 #endif
00155 
00156   if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE)))
00157     /* FIXME: Support more flags. */
00158     return EAI_BADFLAGS;
00159 
00160   if (hints && !validate_family (hints->ai_family))
00161     return EAI_FAMILY;
00162 
00163   if (hints &&
00164       hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
00165     /* FIXME: Support other socktype. */
00166     return EAI_SOCKTYPE; /* FIXME: Better return code? */
00167 
00168   if (!nodename)
00169     {
00170       if (!(hints->ai_flags & AI_PASSIVE))
00171         return EAI_NONAME;
00172 
00173 #ifdef HAVE_IPV6
00174       nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0";
00175 #else
00176       nodename = "0.0.0.0";
00177 #endif
00178     }
00179 
00180   if (servname)
00181     {
00182       struct servent *se = NULL;
00183       const char *proto =
00184         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
00185 
00186       if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV))
00187         /* FIXME: Use getservbyname_r if available. */
00188         se = getservbyname (servname, proto);
00189 
00190       if (!se)
00191         {
00192           char *c;
00193           if (!(*servname >= '0' && *servname <= '9'))
00194             return EAI_NONAME;
00195           port = strtoul (servname, &c, 10);
00196           if (*c || port > 0xffff)
00197             return EAI_NONAME;
00198           port = htons (port);
00199         }
00200       else
00201         port = se->s_port;
00202     }
00203 
00204   /* FIXME: Use gethostbyname_r if available. */
00205   he = gethostbyname (nodename);
00206   if (!he || he->h_addr_list[0] == NULL)
00207     return EAI_NONAME;
00208 
00209   switch (he->h_addrtype)
00210     {
00211 #if HAVE_IPV6
00212     case PF_INET6:
00213       size = sizeof (struct v6_pair);
00214       break;
00215 #endif
00216 
00217 #if HAVE_IPV4
00218     case PF_INET:
00219       size = sizeof (struct v4_pair);
00220       break;
00221 #endif
00222 
00223     default:
00224       return EAI_NODATA;
00225     }
00226 
00227   storage = calloc (1, size);
00228   if (!storage)
00229     return EAI_MEMORY;
00230 
00231   switch (he->h_addrtype)
00232     {
00233 #if HAVE_IPV6
00234     case PF_INET6:
00235       {
00236         struct v6_pair *p = storage;
00237         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
00238         tmp = &p->addrinfo;
00239 
00240         if (port)
00241           sinp->sin6_port = port;
00242 
00243         if (he->h_length != sizeof (sinp->sin6_addr))
00244           {
00245             free (storage);
00246             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
00247           }
00248 
00249         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
00250 
00251         tmp->ai_addr = (struct sockaddr *) sinp;
00252         tmp->ai_addrlen = sizeof *sinp;
00253       }
00254       break;
00255 #endif
00256 
00257 #if HAVE_IPV4
00258     case PF_INET:
00259       {
00260         struct v4_pair *p = storage;
00261         struct sockaddr_in *sinp = &p->sockaddr_in;
00262         tmp = &p->addrinfo;
00263 
00264         if (port)
00265           sinp->sin_port = port;
00266 
00267         if (he->h_length != sizeof (sinp->sin_addr))
00268           {
00269             free (storage);
00270             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
00271           }
00272 
00273         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
00274 
00275         tmp->ai_addr = (struct sockaddr *) sinp;
00276         tmp->ai_addrlen = sizeof *sinp;
00277       }
00278       break;
00279 #endif
00280 
00281     default:
00282       free (storage);
00283       return EAI_NODATA;
00284     }
00285 
00286   if (hints && hints->ai_flags & AI_CANONNAME)
00287     {
00288       const char *cn;
00289       if (he->h_name)
00290         cn = he->h_name;
00291       else
00292         cn = nodename;
00293 
00294       tmp->ai_canonname = strdup (cn);
00295       if (!tmp->ai_canonname)
00296         {
00297           free (storage);
00298           return EAI_MEMORY;
00299         }
00300     }
00301 
00302   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
00303   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
00304   tmp->ai_addr->sa_family = he->h_addrtype;
00305   tmp->ai_family = he->h_addrtype;
00306 
00307 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
00308   switch (he->h_addrtype)
00309     {
00310 #if HAVE_IPV4
00311     case AF_INET:
00312       tmp->ai_addr->sa_len = sizeof (struct sockaddr_in);
00313       break;
00314 #endif
00315 #if HAVE_IPV6
00316     case AF_INET6:
00317       tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6);
00318       break;
00319 #endif
00320     }
00321 #endif
00322 
00323   /* FIXME: If more than one address, create linked list of addrinfo's. */
00324 
00325   *res = tmp;
00326 
00327   return 0;
00328 }
00329 
00330 /* Free `addrinfo' structure AI including associated storage.  */
00331 void
00332 freeaddrinfo (struct addrinfo *ai)
00333 {
00334 #ifdef WIN32_NATIVE
00335   if (use_win32_p ())
00336     {
00337       freeaddrinfo_ptr (ai);
00338       return;
00339     }
00340 #endif
00341 
00342   while (ai)
00343     {
00344       struct addrinfo *cur;
00345 
00346       cur = ai;
00347       ai = ai->ai_next;
00348 
00349       free (cur->ai_canonname);
00350       free (cur);
00351     }
00352 }
00353 
00354 int
00355 getnameinfo (const struct sockaddr *restrict sa, socklen_t salen,
00356              char *restrict node, socklen_t nodelen,
00357              char *restrict service, socklen_t servicelen,
00358              int flags)
00359 {
00360 #ifdef WIN32_NATIVE
00361   if (use_win32_p ())
00362     return getnameinfo_ptr (sa, salen, node, nodelen,
00363                             service, servicelen, flags);
00364 #endif
00365 
00366   /* FIXME: Support other flags. */
00367   if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
00368       (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
00369       (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
00370     return EAI_BADFLAGS;
00371 
00372   if (sa == NULL || salen < sizeof (sa->sa_family))
00373     return EAI_FAMILY;
00374 
00375   switch (sa->sa_family)
00376     {
00377 #if HAVE_IPV4
00378     case AF_INET:
00379       if (salen < sizeof (struct sockaddr_in))
00380         return EAI_FAMILY;
00381       break;
00382 #endif
00383 #if HAVE_IPV6
00384     case AF_INET6:
00385       if (salen < sizeof (struct sockaddr_in6))
00386         return EAI_FAMILY;
00387       break;
00388 #endif
00389     default:
00390       return EAI_FAMILY;
00391     }
00392 
00393   if (node && nodelen > 0 && flags & NI_NUMERICHOST)
00394     {
00395       switch (sa->sa_family)
00396         {
00397 #if HAVE_IPV4
00398         case AF_INET:
00399           if (!inet_ntop (AF_INET,
00400                           &(((const struct sockaddr_in *) sa)->sin_addr),
00401                           node, nodelen))
00402             return EAI_SYSTEM;
00403           break;
00404 #endif
00405 
00406 #if HAVE_IPV6
00407         case AF_INET6:
00408           if (!inet_ntop (AF_INET6,
00409                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
00410                           node, nodelen))
00411             return EAI_SYSTEM;
00412           break;
00413 #endif
00414 
00415         default:
00416           return EAI_FAMILY;
00417         }
00418     }
00419 
00420   if (service && servicelen > 0 && flags & NI_NUMERICSERV)
00421     switch (sa->sa_family)
00422       {
00423 #if HAVE_IPV4
00424       case AF_INET:
00425 #endif
00426 #if HAVE_IPV6
00427       case AF_INET6:
00428 #endif
00429         {
00430           unsigned short int port
00431             = ntohs (((const struct sockaddr_in *) sa)->sin_port);
00432           if (servicelen <= snprintf (service, servicelen, "%u", port))
00433             return EAI_OVERFLOW;
00434         }
00435         break;
00436       }
00437 
00438   return 0;
00439 }