155682Smarkm/*
290926Snectar * Copyright (c) 1999 - 2001 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef HAVE_CONFIG_H
3555682Smarkm#include <config.h>
36178825SdfrRCSID("$Id: getaddrinfo.c 15417 2005-06-16 17:49:29Z lha $");
3755682Smarkm#endif
3855682Smarkm
3955682Smarkm#include "roken.h"
4055682Smarkm
4155682Smarkm/*
4255682Smarkm * uses hints->ai_socktype and hints->ai_protocol
4355682Smarkm */
4455682Smarkm
4555682Smarkmstatic int
4655682Smarkmget_port_protocol_socktype (const char *servname,
4755682Smarkm			    const struct addrinfo *hints,
4855682Smarkm			    int *port,
4955682Smarkm			    int *protocol,
5055682Smarkm			    int *socktype)
5155682Smarkm{
5255682Smarkm    struct servent *se;
5355682Smarkm    const char *proto_str = NULL;
5455682Smarkm
5555682Smarkm    *socktype = 0;
5655682Smarkm
5755682Smarkm    if (hints != NULL && hints->ai_protocol != 0) {
5855682Smarkm	struct protoent *protoent = getprotobynumber (hints->ai_protocol);
5955682Smarkm
6055682Smarkm	if (protoent == NULL)
6155682Smarkm	    return EAI_SOCKTYPE; /* XXX */
6255682Smarkm
6355682Smarkm	proto_str = protoent->p_name;
6455682Smarkm	*protocol = protoent->p_proto;
6555682Smarkm    }
6655682Smarkm
6755682Smarkm    if (hints != NULL)
6855682Smarkm	*socktype = hints->ai_socktype;
6955682Smarkm
7055682Smarkm    if (*socktype == SOCK_STREAM) {
7155682Smarkm	se = getservbyname (servname, proto_str ? proto_str : "tcp");
7255682Smarkm	if (proto_str == NULL)
7355682Smarkm	    *protocol = IPPROTO_TCP;
7455682Smarkm    } else if (*socktype == SOCK_DGRAM) {
7555682Smarkm	se = getservbyname (servname, proto_str ? proto_str : "udp");
7655682Smarkm	if (proto_str == NULL)
7755682Smarkm	    *protocol = IPPROTO_UDP;
7855682Smarkm    } else if (*socktype == 0) {
7955682Smarkm	if (proto_str != NULL) {
8055682Smarkm	    se = getservbyname (servname, proto_str);
8155682Smarkm	} else {
8255682Smarkm	    se = getservbyname (servname, "tcp");
8355682Smarkm	    *protocol = IPPROTO_TCP;
8455682Smarkm	    *socktype = SOCK_STREAM;
8555682Smarkm	    if (se == NULL) {
8655682Smarkm		se = getservbyname (servname, "udp");
8755682Smarkm		*protocol = IPPROTO_UDP;
8855682Smarkm		*socktype = SOCK_DGRAM;
8955682Smarkm	    }
9055682Smarkm	}
9155682Smarkm    } else
9255682Smarkm	return EAI_SOCKTYPE;
9355682Smarkm
9455682Smarkm    if (se == NULL) {
9555682Smarkm	char *endstr;
9655682Smarkm
9755682Smarkm	*port = htons(strtol (servname, &endstr, 10));
9855682Smarkm	if (servname == endstr)
9955682Smarkm	    return EAI_NONAME;
10055682Smarkm    } else {
10155682Smarkm	*port = se->s_port;
10255682Smarkm    }
10355682Smarkm    return 0;
10455682Smarkm}
10555682Smarkm
10655682Smarkmstatic int
10755682Smarkmadd_one (int port, int protocol, int socktype,
10855682Smarkm	 struct addrinfo ***ptr,
10955682Smarkm	 int (*func)(struct addrinfo *, void *data, int port),
11055682Smarkm	 void *data,
11155682Smarkm	 char *canonname)
11255682Smarkm{
11355682Smarkm    struct addrinfo *a;
11455682Smarkm    int ret;
11555682Smarkm
11655682Smarkm    a = malloc (sizeof (*a));
11755682Smarkm    if (a == NULL)
11855682Smarkm	return EAI_MEMORY;
11955682Smarkm    memset (a, 0, sizeof(*a));
12055682Smarkm    a->ai_flags     = 0;
12155682Smarkm    a->ai_next      = NULL;
12255682Smarkm    a->ai_protocol  = protocol;
12355682Smarkm    a->ai_socktype  = socktype;
12455682Smarkm    a->ai_canonname = canonname;
12555682Smarkm    ret = (*func)(a, data, port);
12655682Smarkm    if (ret) {
12755682Smarkm	free (a);
12855682Smarkm	return ret;
12955682Smarkm    }
13055682Smarkm    **ptr = a;
13155682Smarkm    *ptr = &a->ai_next;
13255682Smarkm    return 0;
13355682Smarkm}
13455682Smarkm
13555682Smarkmstatic int
13655682Smarkmconst_v4 (struct addrinfo *a, void *data, int port)
13755682Smarkm{
138178825Sdfr    struct sockaddr_in *sin4;
13955682Smarkm    struct in_addr *addr = (struct in_addr *)data;
14055682Smarkm
14155682Smarkm    a->ai_family  = PF_INET;
142178825Sdfr    a->ai_addrlen = sizeof(*sin4);
143178825Sdfr    a->ai_addr    = malloc (sizeof(*sin4));
14455682Smarkm    if (a->ai_addr == NULL)
14555682Smarkm	return EAI_MEMORY;
146178825Sdfr    sin4 = (struct sockaddr_in *)a->ai_addr;
147178825Sdfr    memset (sin4, 0, sizeof(*sin4));
148178825Sdfr    sin4->sin_family = AF_INET;
149178825Sdfr    sin4->sin_port   = port;
150178825Sdfr    sin4->sin_addr   = *addr;
15155682Smarkm    return 0;
15255682Smarkm}
15355682Smarkm
15455682Smarkm#ifdef HAVE_IPV6
15555682Smarkmstatic int
15655682Smarkmconst_v6 (struct addrinfo *a, void *data, int port)
15755682Smarkm{
15855682Smarkm    struct sockaddr_in6 *sin6;
15955682Smarkm    struct in6_addr *addr = (struct in6_addr *)data;
16055682Smarkm
16155682Smarkm    a->ai_family  = PF_INET6;
16255682Smarkm    a->ai_addrlen = sizeof(*sin6);
16355682Smarkm    a->ai_addr    = malloc (sizeof(*sin6));
16455682Smarkm    if (a->ai_addr == NULL)
16555682Smarkm	return EAI_MEMORY;
16655682Smarkm    sin6 = (struct sockaddr_in6 *)a->ai_addr;
16755682Smarkm    memset (sin6, 0, sizeof(*sin6));
16855682Smarkm    sin6->sin6_family = AF_INET6;
16955682Smarkm    sin6->sin6_port   = port;
17055682Smarkm    sin6->sin6_addr   = *addr;
17155682Smarkm    return 0;
17255682Smarkm}
17355682Smarkm#endif
17455682Smarkm
17590926Snectar/* this is mostly a hack for some versions of AIX that has a prototype
17690926Snectar   for in6addr_loopback but no actual symbol in libc */
17790926Snectar#if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT)
17890926Snectar#define in6addr_loopback _roken_in6addr_loopback
17990926Snectarstruct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
18090926Snectar#endif
18190926Snectar
18255682Smarkmstatic int
18355682Smarkmget_null (const struct addrinfo *hints,
18455682Smarkm	  int port, int protocol, int socktype,
18555682Smarkm	  struct addrinfo **res)
18655682Smarkm{
18755682Smarkm    struct in_addr v4_addr;
18855682Smarkm#ifdef HAVE_IPV6
18955682Smarkm    struct in6_addr v6_addr;
19055682Smarkm#endif
19155682Smarkm    struct addrinfo *first = NULL;
19255682Smarkm    struct addrinfo **current = &first;
19355682Smarkm    int family = PF_UNSPEC;
19455682Smarkm    int ret;
19555682Smarkm
19655682Smarkm    if (hints != NULL)
19755682Smarkm	family = hints->ai_family;
19855682Smarkm
19955682Smarkm    if (hints && hints->ai_flags & AI_PASSIVE) {
20055682Smarkm	v4_addr.s_addr = INADDR_ANY;
20155682Smarkm#ifdef HAVE_IPV6
20255682Smarkm	v6_addr        = in6addr_any;
20355682Smarkm#endif
20455682Smarkm    } else {
20555682Smarkm	v4_addr.s_addr = htonl(INADDR_LOOPBACK);
20655682Smarkm#ifdef HAVE_IPV6
20755682Smarkm	v6_addr        = in6addr_loopback;
20855682Smarkm#endif
20955682Smarkm    }
21055682Smarkm
21155682Smarkm#ifdef HAVE_IPV6
21255682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
21355682Smarkm	ret = add_one (port, protocol, socktype,
21455682Smarkm		       &current, const_v6, &v6_addr, NULL);
21555682Smarkm    }
21655682Smarkm#endif
21755682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
21855682Smarkm	ret = add_one (port, protocol, socktype,
21955682Smarkm		       &current, const_v4, &v4_addr, NULL);
22055682Smarkm    }
22155682Smarkm    *res = first;
22255682Smarkm    return 0;
22355682Smarkm}
22455682Smarkm
22555682Smarkmstatic int
22655682Smarkmadd_hostent (int port, int protocol, int socktype,
22755682Smarkm	     struct addrinfo ***current,
22855682Smarkm	     int (*func)(struct addrinfo *, void *data, int port),
22955682Smarkm	     struct hostent *he, int *flags)
23055682Smarkm{
23155682Smarkm    int ret;
23255682Smarkm    char *canonname = NULL;
23372445Sassar    char **h;
23455682Smarkm
23555682Smarkm    if (*flags & AI_CANONNAME) {
23672445Sassar	struct hostent *he2 = NULL;
23790926Snectar	const char *tmp_canon;
23855682Smarkm
23990926Snectar	tmp_canon = hostent_find_fqdn (he);
24090926Snectar	if (strchr (tmp_canon, '.') == NULL) {
24172445Sassar	    int error;
24272445Sassar
24372445Sassar	    he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
24472445Sassar				   he->h_addrtype, &error);
24572445Sassar	    if (he2 != NULL) {
24690926Snectar		const char *tmp = hostent_find_fqdn (he2);
24772445Sassar
24872445Sassar		if (strchr (tmp, '.') != NULL)
24990926Snectar		    tmp_canon = tmp;
25055682Smarkm	    }
25172445Sassar	}
25272445Sassar
25390926Snectar	canonname = strdup (tmp_canon);
25472445Sassar	if (he2 != NULL)
25572445Sassar	    freehostent (he2);
25655682Smarkm	if (canonname == NULL)
25755682Smarkm	    return EAI_MEMORY;
25855682Smarkm    }
25955682Smarkm
26055682Smarkm    for (h = he->h_addr_list; *h != NULL; ++h) {
26155682Smarkm	ret = add_one (port, protocol, socktype,
26255682Smarkm		       current, func, *h, canonname);
26355682Smarkm	if (ret)
26455682Smarkm	    return ret;
26555682Smarkm	if (*flags & AI_CANONNAME) {
26655682Smarkm	    *flags &= ~AI_CANONNAME;
26755682Smarkm	    canonname = NULL;
26855682Smarkm	}
26955682Smarkm    }
27055682Smarkm    return 0;
27155682Smarkm}
27255682Smarkm
27355682Smarkmstatic int
27455682Smarkmget_number (const char *nodename,
27555682Smarkm	    const struct addrinfo *hints,
27655682Smarkm	    int port, int protocol, int socktype,
27755682Smarkm	    struct addrinfo **res)
27855682Smarkm{
27955682Smarkm    struct addrinfo *first = NULL;
28055682Smarkm    struct addrinfo **current = &first;
28155682Smarkm    int family = PF_UNSPEC;
28255682Smarkm    int ret;
28355682Smarkm
28455682Smarkm    if (hints != NULL) {
28555682Smarkm	family = hints->ai_family;
28655682Smarkm    }
28755682Smarkm
28855682Smarkm#ifdef HAVE_IPV6
28955682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
29055682Smarkm	struct in6_addr v6_addr;
29155682Smarkm
29255682Smarkm	if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
29355682Smarkm	    ret = add_one (port, protocol, socktype,
29455682Smarkm			   &current, const_v6, &v6_addr, NULL);
29555682Smarkm	    *res = first;
29655682Smarkm	    return ret;
29755682Smarkm	}
29855682Smarkm    }
29955682Smarkm#endif
30055682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
30155682Smarkm	struct in_addr v4_addr;
30255682Smarkm
30355682Smarkm	if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
30455682Smarkm	    ret = add_one (port, protocol, socktype,
30555682Smarkm			   &current, const_v4, &v4_addr, NULL);
30655682Smarkm	    *res = first;
30755682Smarkm	    return ret;
30855682Smarkm	}
30955682Smarkm    }
31055682Smarkm    return EAI_NONAME;
31155682Smarkm}
31255682Smarkm
31355682Smarkmstatic int
31455682Smarkmget_nodes (const char *nodename,
31555682Smarkm	   const struct addrinfo *hints,
31655682Smarkm	   int port, int protocol, int socktype,
31755682Smarkm	   struct addrinfo **res)
31855682Smarkm{
31955682Smarkm    struct addrinfo *first = NULL;
32055682Smarkm    struct addrinfo **current = &first;
32155682Smarkm    int family = PF_UNSPEC;
32255682Smarkm    int flags  = 0;
32355682Smarkm    int ret = EAI_NONAME;
32455682Smarkm    int error;
32555682Smarkm
32655682Smarkm    if (hints != NULL) {
32755682Smarkm	family = hints->ai_family;
32855682Smarkm	flags  = hints->ai_flags;
32955682Smarkm    }
33055682Smarkm
33155682Smarkm#ifdef HAVE_IPV6
33255682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
33355682Smarkm	struct hostent *he;
33455682Smarkm
33555682Smarkm	he = getipnodebyname (nodename, PF_INET6, 0, &error);
33655682Smarkm
33755682Smarkm	if (he != NULL) {
33855682Smarkm	    ret = add_hostent (port, protocol, socktype,
33955682Smarkm			       &current, const_v6, he, &flags);
34055682Smarkm	    freehostent (he);
34155682Smarkm	}
34255682Smarkm    }
34355682Smarkm#endif
34455682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
34555682Smarkm	struct hostent *he;
34655682Smarkm
34755682Smarkm	he = getipnodebyname (nodename, PF_INET, 0, &error);
34855682Smarkm
34955682Smarkm	if (he != NULL) {
35055682Smarkm	    ret = add_hostent (port, protocol, socktype,
35155682Smarkm			       &current, const_v4, he, &flags);
35255682Smarkm	    freehostent (he);
35355682Smarkm	}
35455682Smarkm    }
35555682Smarkm    *res = first;
35655682Smarkm    return ret;
35755682Smarkm}
35855682Smarkm
35955682Smarkm/*
36055682Smarkm * hints:
36155682Smarkm *
36255682Smarkm * struct addrinfo {
36355682Smarkm *     int    ai_flags;
36455682Smarkm *     int    ai_family;
36555682Smarkm *     int    ai_socktype;
36655682Smarkm *     int    ai_protocol;
36755682Smarkm * ...
36855682Smarkm * };
36955682Smarkm */
37055682Smarkm
371178825Sdfrint ROKEN_LIB_FUNCTION
37255682Smarkmgetaddrinfo(const char *nodename,
37355682Smarkm	    const char *servname,
37455682Smarkm	    const struct addrinfo *hints,
37555682Smarkm	    struct addrinfo **res)
37655682Smarkm{
37755682Smarkm    int ret;
37855682Smarkm    int port     = 0;
37955682Smarkm    int protocol = 0;
38055682Smarkm    int socktype = 0;
38155682Smarkm
38255682Smarkm    *res = NULL;
38355682Smarkm
38455682Smarkm    if (servname == NULL && nodename == NULL)
38555682Smarkm	return EAI_NONAME;
38655682Smarkm
38755682Smarkm    if (hints != NULL
38855682Smarkm	&& hints->ai_family != PF_UNSPEC
38955682Smarkm	&& hints->ai_family != PF_INET
39055682Smarkm#ifdef HAVE_IPV6
39155682Smarkm	&& hints->ai_family != PF_INET6
39255682Smarkm#endif
39355682Smarkm	)
39455682Smarkm	return EAI_FAMILY;
39555682Smarkm
39655682Smarkm    if (servname != NULL) {
39755682Smarkm	ret = get_port_protocol_socktype (servname, hints,
39855682Smarkm					  &port, &protocol, &socktype);
39955682Smarkm	if (ret)
40055682Smarkm	    return ret;
40155682Smarkm    }
40255682Smarkm    if (nodename != NULL) {
40355682Smarkm	ret = get_number (nodename, hints, port, protocol, socktype, res);
40455682Smarkm	if (ret) {
40555682Smarkm	    if(hints && hints->ai_flags & AI_NUMERICHOST)
40655682Smarkm		ret = EAI_NONAME;
40755682Smarkm	    else
40855682Smarkm		ret = get_nodes (nodename, hints, port, protocol, socktype,
40955682Smarkm				 res);
41055682Smarkm	}
41155682Smarkm    } else {
41255682Smarkm	ret = get_null (hints, port, protocol, socktype, res);
41355682Smarkm    }
41455682Smarkm    if (ret)
41555682Smarkm	freeaddrinfo (*res);
41655682Smarkm    return ret;
41755682Smarkm}
418