Home | History | Annotate | Line # | Download | only in roken
getaddrinfo.c revision 1.3
      1 /*	$NetBSD: getaddrinfo.c,v 1.3 2023/06/19 21:41:45 christos 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 = 0;
    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         if (ret)
    215             return ret;
    216     }
    217 #endif
    218     if (family == PF_INET || family == PF_UNSPEC) {
    219 	ret = add_one (port, protocol, socktype,
    220 		       &current, const_v4, &v4_addr, NULL);
    221     }
    222     *res = first;
    223     return ret;
    224 }
    225 
    226 static int
    227 add_hostent (int port, int protocol, int socktype,
    228 	     struct addrinfo ***current,
    229 	     int (*func)(struct addrinfo *, void *data, int port),
    230 	     struct hostent *he, int *flags)
    231 {
    232     int ret;
    233     char *canonname = NULL;
    234     char **h;
    235 
    236     if (*flags & AI_CANONNAME) {
    237 	struct hostent *he2 = NULL;
    238 	const char *tmp_canon;
    239 
    240 	tmp_canon = hostent_find_fqdn (he);
    241 	if (strchr (tmp_canon, '.') == NULL) {
    242 	    int error;
    243 
    244 	    he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
    245 				   he->h_addrtype, &error);
    246 	    if (he2 != NULL) {
    247 		const char *tmp = hostent_find_fqdn (he2);
    248 
    249 		if (strchr (tmp, '.') != NULL)
    250 		    tmp_canon = tmp;
    251 	    }
    252 	}
    253 
    254 	canonname = strdup (tmp_canon);
    255 	if (he2 != NULL)
    256 	    freehostent (he2);
    257 	if (canonname == NULL)
    258 	    return EAI_MEMORY;
    259     }
    260 
    261     for (h = he->h_addr_list; *h != NULL; ++h) {
    262 	ret = add_one (port, protocol, socktype,
    263 		       current, func, *h, canonname);
    264 	if (ret)
    265 	    return ret;
    266 	if (*flags & AI_CANONNAME) {
    267 	    *flags &= ~AI_CANONNAME;
    268 	    canonname = NULL;
    269 	}
    270     }
    271     return 0;
    272 }
    273 
    274 static int
    275 get_number (const char *nodename,
    276 	    const struct addrinfo *hints,
    277 	    int port, int protocol, int socktype,
    278 	    struct addrinfo **res)
    279 {
    280     struct addrinfo *first = NULL;
    281     struct addrinfo **current = &first;
    282     int family = PF_UNSPEC;
    283     int ret;
    284 
    285     if (hints != NULL) {
    286 	family = hints->ai_family;
    287     }
    288 
    289 #ifdef HAVE_IPV6
    290     if (family == PF_INET6 || family == PF_UNSPEC) {
    291 	struct in6_addr v6_addr;
    292 
    293 	if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
    294 	    ret = add_one (port, protocol, socktype,
    295 			   &current, const_v6, &v6_addr, NULL);
    296 	    *res = first;
    297 	    return ret;
    298 	}
    299     }
    300 #endif
    301     if (family == PF_INET || family == PF_UNSPEC) {
    302 	struct in_addr v4_addr;
    303 
    304 	if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
    305 	    ret = add_one (port, protocol, socktype,
    306 			   &current, const_v4, &v4_addr, NULL);
    307 	    *res = first;
    308 	    return ret;
    309 	}
    310     }
    311     return EAI_NONAME;
    312 }
    313 
    314 static int
    315 get_nodes (const char *nodename,
    316 	   const struct addrinfo *hints,
    317 	   int port, int protocol, int socktype,
    318 	   struct addrinfo **res)
    319 {
    320     struct addrinfo *first = NULL;
    321     struct addrinfo **current = &first;
    322     int family = PF_UNSPEC;
    323     int flags  = 0;
    324     int ret = EAI_NONAME;
    325     int error;
    326 
    327     if (hints != NULL) {
    328 	family = hints->ai_family;
    329 	flags  = hints->ai_flags;
    330     }
    331 
    332 #ifdef HAVE_IPV6
    333     if (family == PF_INET6 || family == PF_UNSPEC) {
    334 	struct hostent *he;
    335 
    336 	he = getipnodebyname (nodename, PF_INET6, 0, &error);
    337 
    338 	if (he != NULL) {
    339 	    ret = add_hostent (port, protocol, socktype,
    340 			       &current, const_v6, he, &flags);
    341 	    freehostent (he);
    342 	}
    343     }
    344 #endif
    345     if (family == PF_INET || family == PF_UNSPEC) {
    346 	struct hostent *he;
    347 
    348 	he = getipnodebyname (nodename, PF_INET, 0, &error);
    349 
    350 	if (he != NULL) {
    351 	    ret = add_hostent (port, protocol, socktype,
    352 			       &current, const_v4, he, &flags);
    353 	    freehostent (he);
    354 	}
    355     }
    356     *res = first;
    357     return ret;
    358 }
    359 
    360 /*
    361  * hints:
    362  *
    363  * struct addrinfo {
    364  *     int    ai_flags;
    365  *     int    ai_family;
    366  *     int    ai_socktype;
    367  *     int    ai_protocol;
    368  * ...
    369  * };
    370  */
    371 
    372 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    373 getaddrinfo(const char *nodename,
    374 	    const char *servname,
    375 	    const struct addrinfo *hints,
    376 	    struct addrinfo **res)
    377 {
    378     int ret;
    379     int port     = 0;
    380     int protocol = 0;
    381     int socktype = 0;
    382 
    383     *res = NULL;
    384 
    385     if (servname == NULL && nodename == NULL)
    386 	return EAI_NONAME;
    387 
    388     if (hints != NULL
    389 	&& hints->ai_family != PF_UNSPEC
    390 	&& hints->ai_family != PF_INET
    391 #ifdef HAVE_IPV6
    392 	&& hints->ai_family != PF_INET6
    393 #endif
    394 	)
    395 	return EAI_FAMILY;
    396 
    397     if (servname != NULL) {
    398 	ret = get_port_protocol_socktype (servname, hints,
    399 					  &port, &protocol, &socktype);
    400 	if (ret)
    401 	    return ret;
    402     }
    403     if (nodename != NULL) {
    404 	ret = get_number (nodename, hints, port, protocol, socktype, res);
    405 	if (ret) {
    406 	    if(hints && hints->ai_flags & AI_NUMERICHOST)
    407 		ret = EAI_NONAME;
    408 	    else
    409 		ret = get_nodes (nodename, hints, port, protocol, socktype,
    410 				 res);
    411 	}
    412     } else {
    413 	ret = get_null (hints, port, protocol, socktype, res);
    414     }
    415     if (ret)
    416 	freeaddrinfo (*res);
    417     return ret;
    418 }
    419