Home | History | Annotate | Line # | Download | only in lib
getaddrinfo.c revision 1.1.1.1.26.1
      1 /* Get address information (partial implementation).
      2    Copyright (C) 1997, 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
      3    Contributed by Simon Josefsson <simon (at) josefsson.org>.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program 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
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: getaddrinfo.c,v 1.1.1.1.26.1 2017/05/13 06:23:23 snj Exp $");
     20 
     21 
     22 #ifdef HAVE_CONFIG_H
     23 # include <config.h>
     24 #endif
     25 
     26 #include "getaddrinfo.h"
     27 
     28 /* Get calloc. */
     29 #include <stdlib.h>
     30 
     31 /* Get memcpy. */
     32 #include <string.h>
     33 
     34 #include <stdbool.h>
     35 
     36 #include "gettext.h"
     37 #define _(String) gettext (String)
     38 #define N_(String) String
     39 
     40 #include "strdup.h"
     41 
     42 static inline bool
     43 validate_family (int family)
     44 {
     45   /* FIXME: Support more families. */
     46 #if HAVE_IPV4
     47      if (family == PF_INET)
     48        return true;
     49 #endif
     50 #if HAVE_IPV6
     51      if (family == PF_INET6)
     52        return true;
     53 #endif
     54      if (family == PF_UNSPEC)
     55        return true;
     56      return false;
     57 }
     58 
     59 /* Translate name of a service location and/or a service name to set of
     60    socket addresses. */
     61 int
     62 getaddrinfo (const char *restrict nodename,
     63 	     const char *restrict servname,
     64 	     const struct addrinfo *restrict hints,
     65 	     struct addrinfo **restrict res)
     66 {
     67   struct addrinfo *tmp;
     68   struct servent *se;
     69   struct hostent *he;
     70   size_t sinlen;
     71 
     72   if (hints && (hints->ai_flags & ~AI_CANONNAME))
     73     /* FIXME: Support more flags. */
     74     return EAI_BADFLAGS;
     75 
     76   if (hints && !validate_family (hints->ai_family))
     77     return EAI_FAMILY;
     78 
     79   if (hints &&
     80       hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
     81     /* FIXME: Support other socktype. */
     82     return EAI_SOCKTYPE; /* FIXME: Better return code? */
     83 
     84   if (!nodename)
     85     /* FIXME: Support server bind mode. */
     86     return EAI_NONAME;
     87 
     88   if (servname)
     89     {
     90       const char *proto =
     91 	(hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
     92 
     93       /* FIXME: Use getservbyname_r if available. */
     94       se = getservbyname (servname, proto);
     95 
     96       if (!se)
     97 	return EAI_SERVICE;
     98     }
     99 
    100   /* FIXME: Use gethostbyname_r if available. */
    101   he = gethostbyname (nodename);
    102   if (!he || he->h_addr_list[0] == NULL)
    103     return EAI_NONAME;
    104 
    105   switch (he->h_addrtype)
    106     {
    107 #if HAVE_IPV6
    108     case PF_INET6:
    109       sinlen = sizeof (struct sockaddr_in6);
    110       break;
    111 #endif
    112 
    113 #if HAVE_IPV4
    114     case PF_INET:
    115       sinlen = sizeof (struct sockaddr_in);
    116       break;
    117 #endif
    118 
    119     default:
    120       return EAI_NODATA;
    121     }
    122 
    123   tmp = calloc (1, sizeof (*tmp) + sinlen);
    124   if (!tmp)
    125     return EAI_MEMORY;
    126 
    127   switch (he->h_addrtype)
    128     {
    129 #if HAVE_IPV6
    130     case PF_INET6:
    131       {
    132 	struct sockaddr_in6 *sinp = (char *) tmp + sizeof (*tmp);
    133 
    134 	if (se)
    135 	  sinp->sin6_port = se->s_port;
    136 
    137 	if (he->h_length != sizeof (sinp->sin6_addr))
    138 	  return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
    139 
    140 	memcpy (&sinp->sin6_addr, he->h_addr_list[0], he->h_length);
    141 
    142 	tmp->ai_addr = (struct sockaddr *) sinp;
    143 	tmp->ai_addrlen = sinlen;
    144       }
    145       break;
    146 #endif
    147 
    148 #if HAVE_IPV4
    149     case PF_INET:
    150       {
    151 	struct sockaddr_in *sinp = (char *) tmp + sizeof (*tmp);
    152 
    153 	if (se)
    154 	  sinp->sin_port = se->s_port;
    155 
    156 	if (he->h_length != sizeof (sinp->sin_addr))
    157 	  return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
    158 
    159 	memcpy (&sinp->sin_addr, he->h_addr_list[0], he->h_length);
    160 
    161 	tmp->ai_addr = (struct sockaddr *) sinp;
    162 	tmp->ai_addrlen = sinlen;
    163       }
    164       break;
    165 #endif
    166 
    167     default:
    168       free (tmp);
    169       return EAI_NODATA;
    170     }
    171 
    172   if (hints && hints->ai_flags & AI_CANONNAME)
    173     {
    174       const char *cn;
    175       if (he->h_name)
    176 	cn = he->h_name;
    177       else
    178 	cn = nodename;
    179 
    180       tmp->ai_canonname = strdup (cn);
    181       if (!tmp->ai_canonname)
    182 	{
    183 	  free (tmp);
    184 	  return EAI_MEMORY;
    185 	}
    186     }
    187 
    188   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
    189   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
    190   tmp->ai_addr->sa_family = he->h_addrtype;
    191 
    192   /* FIXME: If more than one address, create linked list of addrinfo's. */
    193 
    194   *res = tmp;
    195 
    196   return 0;
    197 }
    198 
    199 /* Free `addrinfo' structure AI including associated storage.  */
    200 void
    201 freeaddrinfo (struct addrinfo *ai)
    202 {
    203   while (ai)
    204     {
    205       struct addrinfo *cur;
    206 
    207       cur = ai;
    208       ai = ai->ai_next;
    209 
    210       if (cur->ai_canonname) free (cur->ai_canonname);
    211       free (cur);
    212     }
    213 }
    214