Home | History | Annotate | Line # | Download | only in roken
getaddrinfo.c revision 1.1.1.1.32.1
      1 /*	$NetBSD: getaddrinfo.c,v 1.1.1.1.32.1 2017/04/21 16:50:50 bouyer Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1999 - 2001 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <config.h>
     37 
     38 #include <krb5/roken.h>
     39 
     40 /*
     41  * uses hints->ai_socktype and hints->ai_protocol
     42  */
     43 
     44 static int
     45 get_port_protocol_socktype (const char *servname,
     46 			    const struct addrinfo *hints,
     47 			    int *port,
     48 			    int *protocol,
     49 			    int *socktype)
     50 {
     51     struct servent *se;
     52     const char *proto_str = NULL;
     53 
     54     *socktype = 0;
     55 
     56     if (hints != NULL && hints->ai_protocol != 0) {
     57 	struct protoent *protoent = getprotobynumber (hints->ai_protocol);
     58 
     59 	if (protoent == NULL)
     60 	    return EAI_SOCKTYPE; /* XXX */
     61 
     62 	proto_str = protoent->p_name;
     63 	*protocol = protoent->p_proto;
     64     }
     65 
     66     if (hints != NULL)
     67 	*socktype = hints->ai_socktype;
     68 
     69     if (*socktype == SOCK_STREAM) {
     70 	se = getservbyname (servname, proto_str ? proto_str : "tcp");
     71 	if (proto_str == NULL)
     72 	    *protocol = IPPROTO_TCP;
     73     } else if (*socktype == SOCK_DGRAM) {
     74 	se = getservbyname (servname, proto_str ? proto_str : "udp");
     75 	if (proto_str == NULL)
     76 	    *protocol = IPPROTO_UDP;
     77     } else if (*socktype == 0) {
     78 	if (proto_str != NULL) {
     79 	    se = getservbyname (servname, proto_str);
     80 	} else {
     81 	    se = getservbyname (servname, "tcp");
     82 	    *protocol = IPPROTO_TCP;
     83 	    *socktype = SOCK_STREAM;
     84 	    if (se == NULL) {
     85 		se = getservbyname (servname, "udp");
     86 		*protocol = IPPROTO_UDP;
     87 		*socktype = SOCK_DGRAM;
     88 	    }
     89 	}
     90     } else
     91 	return EAI_SOCKTYPE;
     92 
     93     if (se == NULL) {
     94 	char *endstr;
     95 
     96 	*port = htons(strtol (servname, &endstr, 10));
     97 	if (servname == endstr)
     98 	    return EAI_NONAME;
     99     } else {
    100 	*port = se->s_port;
    101     }
    102     return 0;
    103 }
    104 
    105 static int
    106 add_one (int port, int protocol, int socktype,
    107 	 struct addrinfo ***ptr,
    108 	 int (*func)(struct addrinfo *, void *data, int port),
    109 	 void *data,
    110 	 char *canonname)
    111 {
    112     struct addrinfo *a;
    113     int ret;
    114 
    115     a = malloc (sizeof (*a));
    116     if (a == NULL)
    117 	return EAI_MEMORY;
    118     memset (a, 0, sizeof(*a));
    119     a->ai_flags     = 0;
    120     a->ai_next      = NULL;
    121     a->ai_protocol  = protocol;
    122     a->ai_socktype  = socktype;
    123     a->ai_canonname = canonname;
    124     ret = (*func)(a, data, port);
    125     if (ret) {
    126 	free (a);
    127 	return ret;
    128     }
    129     **ptr = a;
    130     *ptr = &a->ai_next;
    131     return 0;
    132 }
    133 
    134 static int
    135 const_v4 (struct addrinfo *a, void *data, int port)
    136 {
    137     struct sockaddr_in *sin4;
    138     struct in_addr *addr = (struct in_addr *)data;
    139 
    140     a->ai_family  = PF_INET;
    141     a->ai_addrlen = sizeof(*sin4);
    142     a->ai_addr    = malloc (sizeof(*sin4));
    143     if (a->ai_addr == NULL)
    144 	return EAI_MEMORY;
    145     sin4 = (struct sockaddr_in *)a->ai_addr;
    146     memset (sin4, 0, sizeof(*sin4));
    147     sin4->sin_family = AF_INET;
    148     sin4->sin_port   = port;
    149     sin4->sin_addr   = *addr;
    150     return 0;
    151 }
    152 
    153 #ifdef HAVE_IPV6
    154 static int
    155 const_v6 (struct addrinfo *a, void *data, int port)
    156 {
    157     struct sockaddr_in6 *sin6;
    158     struct in6_addr *addr = (struct in6_addr *)data;
    159 
    160     a->ai_family  = PF_INET6;
    161     a->ai_addrlen = sizeof(*sin6);
    162     a->ai_addr    = malloc (sizeof(*sin6));
    163     if (a->ai_addr == NULL)
    164 	return EAI_MEMORY;
    165     sin6 = (struct sockaddr_in6 *)a->ai_addr;
    166     memset (sin6, 0, sizeof(*sin6));
    167     sin6->sin6_family = AF_INET6;
    168     sin6->sin6_port   = port;
    169     sin6->sin6_addr   = *addr;
    170     return 0;
    171 }
    172 #endif
    173 
    174 /* this is mostly a hack for some versions of AIX that has a prototype
    175    for in6addr_loopback but no actual symbol in libc */
    176 #if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT)
    177 #define in6addr_loopback _roken_in6addr_loopback
    178 struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
    179 #endif
    180 
    181 static int
    182 get_null (const struct addrinfo *hints,
    183 	  int port, int protocol, int socktype,
    184 	  struct addrinfo **res)
    185 {
    186     struct in_addr v4_addr;
    187 #ifdef HAVE_IPV6
    188     struct in6_addr v6_addr;
    189 #endif
    190     struct addrinfo *first = NULL;
    191     struct addrinfo **current = &first;
    192     int family = PF_UNSPEC;
    193     int ret;
    194 
    195     if (hints != NULL)
    196 	family = hints->ai_family;
    197 
    198     if (hints && hints->ai_flags & AI_PASSIVE) {
    199 	v4_addr.s_addr = INADDR_ANY;
    200 #ifdef HAVE_IPV6
    201 	v6_addr        = in6addr_any;
    202 #endif
    203     } else {
    204 	v4_addr.s_addr = htonl(INADDR_LOOPBACK);
    205 #ifdef HAVE_IPV6
    206 	v6_addr        = in6addr_loopback;
    207 #endif
    208     }
    209 
    210 #ifdef HAVE_IPV6
    211     if (family == PF_INET6 || family == PF_UNSPEC) {
    212 	ret = add_one (port, protocol, socktype,
    213 		       &current, const_v6, &v6_addr, NULL);
    214     }
    215 #endif
    216     if (family == PF_INET || family == PF_UNSPEC) {
    217 	ret = add_one (port, protocol, socktype,
    218 		       &current, const_v4, &v4_addr, NULL);
    219     }
    220     *res = first;
    221     return 0;
    222 }
    223 
    224 static int
    225 add_hostent (int port, int protocol, int socktype,
    226 	     struct addrinfo ***current,
    227 	     int (*func)(struct addrinfo *, void *data, int port),
    228 	     struct hostent *he, int *flags)
    229 {
    230     int ret;
    231     char *canonname = NULL;
    232     char **h;
    233 
    234     if (*flags & AI_CANONNAME) {
    235 	struct hostent *he2 = NULL;
    236 	const char *tmp_canon;
    237 
    238 	tmp_canon = hostent_find_fqdn (he);
    239 	if (strchr (tmp_canon, '.') == NULL) {
    240 	    int error;
    241 
    242 	    he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
    243 				   he->h_addrtype, &error);
    244 	    if (he2 != NULL) {
    245 		const char *tmp = hostent_find_fqdn (he2);
    246 
    247 		if (strchr (tmp, '.') != NULL)
    248 		    tmp_canon = tmp;
    249 	    }
    250 	}
    251 
    252 	canonname = strdup (tmp_canon);
    253 	if (he2 != NULL)
    254 	    freehostent (he2);
    255 	if (canonname == NULL)
    256 	    return EAI_MEMORY;
    257     }
    258 
    259     for (h = he->h_addr_list; *h != NULL; ++h) {
    260 	ret = add_one (port, protocol, socktype,
    261 		       current, func, *h, canonname);
    262 	if (ret)
    263 	    return ret;
    264 	if (*flags & AI_CANONNAME) {
    265 	    *flags &= ~AI_CANONNAME;
    266 	    canonname = NULL;
    267 	}
    268     }
    269     return 0;
    270 }
    271 
    272 static int
    273 get_number (const char *nodename,
    274 	    const struct addrinfo *hints,
    275 	    int port, int protocol, int socktype,
    276 	    struct addrinfo **res)
    277 {
    278     struct addrinfo *first = NULL;
    279     struct addrinfo **current = &first;
    280     int family = PF_UNSPEC;
    281     int ret;
    282 
    283     if (hints != NULL) {
    284 	family = hints->ai_family;
    285     }
    286 
    287 #ifdef HAVE_IPV6
    288     if (family == PF_INET6 || family == PF_UNSPEC) {
    289 	struct in6_addr v6_addr;
    290 
    291 	if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
    292 	    ret = add_one (port, protocol, socktype,
    293 			   &current, const_v6, &v6_addr, NULL);
    294 	    *res = first;
    295 	    return ret;
    296 	}
    297     }
    298 #endif
    299     if (family == PF_INET || family == PF_UNSPEC) {
    300 	struct in_addr v4_addr;
    301 
    302 	if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
    303 	    ret = add_one (port, protocol, socktype,
    304 			   &current, const_v4, &v4_addr, NULL);
    305 	    *res = first;
    306 	    return ret;
    307 	}
    308     }
    309     return EAI_NONAME;
    310 }
    311 
    312 static int
    313 get_nodes (const char *nodename,
    314 	   const struct addrinfo *hints,
    315 	   int port, int protocol, int socktype,
    316 	   struct addrinfo **res)
    317 {
    318     struct addrinfo *first = NULL;
    319     struct addrinfo **current = &first;
    320     int family = PF_UNSPEC;
    321     int flags  = 0;
    322     int ret = EAI_NONAME;
    323     int error;
    324 
    325     if (hints != NULL) {
    326 	family = hints->ai_family;
    327 	flags  = hints->ai_flags;
    328     }
    329 
    330 #ifdef HAVE_IPV6
    331     if (family == PF_INET6 || family == PF_UNSPEC) {
    332 	struct hostent *he;
    333 
    334 	he = getipnodebyname (nodename, PF_INET6, 0, &error);
    335 
    336 	if (he != NULL) {
    337 	    ret = add_hostent (port, protocol, socktype,
    338 			       &current, const_v6, he, &flags);
    339 	    freehostent (he);
    340 	}
    341     }
    342 #endif
    343     if (family == PF_INET || family == PF_UNSPEC) {
    344 	struct hostent *he;
    345 
    346 	he = getipnodebyname (nodename, PF_INET, 0, &error);
    347 
    348 	if (he != NULL) {
    349 	    ret = add_hostent (port, protocol, socktype,
    350 			       &current, const_v4, he, &flags);
    351 	    freehostent (he);
    352 	}
    353     }
    354     *res = first;
    355     return ret;
    356 }
    357 
    358 /*
    359  * hints:
    360  *
    361  * struct addrinfo {
    362  *     int    ai_flags;
    363  *     int    ai_family;
    364  *     int    ai_socktype;
    365  *     int    ai_protocol;
    366  * ...
    367  * };
    368  */
    369 
    370 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    371 getaddrinfo(const char *nodename,
    372 	    const char *servname,
    373 	    const struct addrinfo *hints,
    374 	    struct addrinfo **res)
    375 {
    376     int ret;
    377     int port     = 0;
    378     int protocol = 0;
    379     int socktype = 0;
    380 
    381     *res = NULL;
    382 
    383     if (servname == NULL && nodename == NULL)
    384 	return EAI_NONAME;
    385 
    386     if (hints != NULL
    387 	&& hints->ai_family != PF_UNSPEC
    388 	&& hints->ai_family != PF_INET
    389 #ifdef HAVE_IPV6
    390 	&& hints->ai_family != PF_INET6
    391 #endif
    392 	)
    393 	return EAI_FAMILY;
    394 
    395     if (servname != NULL) {
    396 	ret = get_port_protocol_socktype (servname, hints,
    397 					  &port, &protocol, &socktype);
    398 	if (ret)
    399 	    return ret;
    400     }
    401     if (nodename != NULL) {
    402 	ret = get_number (nodename, hints, port, protocol, socktype, res);
    403 	if (ret) {
    404 	    if(hints && hints->ai_flags & AI_NUMERICHOST)
    405 		ret = EAI_NONAME;
    406 	    else
    407 		ret = get_nodes (nodename, hints, port, protocol, socktype,
    408 				 res);
    409 	}
    410     } else {
    411 	ret = get_null (hints, port, protocol, socktype, res);
    412     }
    413     if (ret)
    414 	freeaddrinfo (*res);
    415     return ret;
    416 }
    417