155682Smarkm/*
2233294Sstas * Copyright (c) 1999 - 2001 Kungliga Tekniska H��gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
5233294Sstas *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
9233294Sstas *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
12233294Sstas *
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.
16233294Sstas *
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.
20233294Sstas *
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#include <config.h>
3555682Smarkm
3655682Smarkm#include "roken.h"
3755682Smarkm
3855682Smarkm/*
3955682Smarkm * uses hints->ai_socktype and hints->ai_protocol
4055682Smarkm */
4155682Smarkm
4255682Smarkmstatic int
4355682Smarkmget_port_protocol_socktype (const char *servname,
4455682Smarkm			    const struct addrinfo *hints,
4555682Smarkm			    int *port,
4655682Smarkm			    int *protocol,
4755682Smarkm			    int *socktype)
4855682Smarkm{
4955682Smarkm    struct servent *se;
5055682Smarkm    const char *proto_str = NULL;
5155682Smarkm
5255682Smarkm    *socktype = 0;
5355682Smarkm
5455682Smarkm    if (hints != NULL && hints->ai_protocol != 0) {
5555682Smarkm	struct protoent *protoent = getprotobynumber (hints->ai_protocol);
5655682Smarkm
5755682Smarkm	if (protoent == NULL)
5855682Smarkm	    return EAI_SOCKTYPE; /* XXX */
5955682Smarkm
6055682Smarkm	proto_str = protoent->p_name;
6155682Smarkm	*protocol = protoent->p_proto;
6255682Smarkm    }
6355682Smarkm
6455682Smarkm    if (hints != NULL)
6555682Smarkm	*socktype = hints->ai_socktype;
6655682Smarkm
6755682Smarkm    if (*socktype == SOCK_STREAM) {
6855682Smarkm	se = getservbyname (servname, proto_str ? proto_str : "tcp");
6955682Smarkm	if (proto_str == NULL)
7055682Smarkm	    *protocol = IPPROTO_TCP;
7155682Smarkm    } else if (*socktype == SOCK_DGRAM) {
7255682Smarkm	se = getservbyname (servname, proto_str ? proto_str : "udp");
7355682Smarkm	if (proto_str == NULL)
7455682Smarkm	    *protocol = IPPROTO_UDP;
7555682Smarkm    } else if (*socktype == 0) {
7655682Smarkm	if (proto_str != NULL) {
7755682Smarkm	    se = getservbyname (servname, proto_str);
7855682Smarkm	} else {
7955682Smarkm	    se = getservbyname (servname, "tcp");
8055682Smarkm	    *protocol = IPPROTO_TCP;
8155682Smarkm	    *socktype = SOCK_STREAM;
8255682Smarkm	    if (se == NULL) {
8355682Smarkm		se = getservbyname (servname, "udp");
8455682Smarkm		*protocol = IPPROTO_UDP;
8555682Smarkm		*socktype = SOCK_DGRAM;
8655682Smarkm	    }
8755682Smarkm	}
8855682Smarkm    } else
8955682Smarkm	return EAI_SOCKTYPE;
9055682Smarkm
9155682Smarkm    if (se == NULL) {
9255682Smarkm	char *endstr;
9355682Smarkm
9455682Smarkm	*port = htons(strtol (servname, &endstr, 10));
9555682Smarkm	if (servname == endstr)
9655682Smarkm	    return EAI_NONAME;
9755682Smarkm    } else {
9855682Smarkm	*port = se->s_port;
9955682Smarkm    }
10055682Smarkm    return 0;
10155682Smarkm}
10255682Smarkm
10355682Smarkmstatic int
10455682Smarkmadd_one (int port, int protocol, int socktype,
10555682Smarkm	 struct addrinfo ***ptr,
10655682Smarkm	 int (*func)(struct addrinfo *, void *data, int port),
10755682Smarkm	 void *data,
10855682Smarkm	 char *canonname)
10955682Smarkm{
11055682Smarkm    struct addrinfo *a;
11155682Smarkm    int ret;
11255682Smarkm
11355682Smarkm    a = malloc (sizeof (*a));
11455682Smarkm    if (a == NULL)
11555682Smarkm	return EAI_MEMORY;
11655682Smarkm    memset (a, 0, sizeof(*a));
11755682Smarkm    a->ai_flags     = 0;
11855682Smarkm    a->ai_next      = NULL;
11955682Smarkm    a->ai_protocol  = protocol;
12055682Smarkm    a->ai_socktype  = socktype;
12155682Smarkm    a->ai_canonname = canonname;
12255682Smarkm    ret = (*func)(a, data, port);
12355682Smarkm    if (ret) {
12455682Smarkm	free (a);
12555682Smarkm	return ret;
12655682Smarkm    }
12755682Smarkm    **ptr = a;
12855682Smarkm    *ptr = &a->ai_next;
12955682Smarkm    return 0;
13055682Smarkm}
13155682Smarkm
13255682Smarkmstatic int
13355682Smarkmconst_v4 (struct addrinfo *a, void *data, int port)
13455682Smarkm{
135178825Sdfr    struct sockaddr_in *sin4;
13655682Smarkm    struct in_addr *addr = (struct in_addr *)data;
13755682Smarkm
13855682Smarkm    a->ai_family  = PF_INET;
139178825Sdfr    a->ai_addrlen = sizeof(*sin4);
140178825Sdfr    a->ai_addr    = malloc (sizeof(*sin4));
14155682Smarkm    if (a->ai_addr == NULL)
14255682Smarkm	return EAI_MEMORY;
143178825Sdfr    sin4 = (struct sockaddr_in *)a->ai_addr;
144178825Sdfr    memset (sin4, 0, sizeof(*sin4));
145178825Sdfr    sin4->sin_family = AF_INET;
146178825Sdfr    sin4->sin_port   = port;
147178825Sdfr    sin4->sin_addr   = *addr;
14855682Smarkm    return 0;
14955682Smarkm}
15055682Smarkm
15155682Smarkm#ifdef HAVE_IPV6
15255682Smarkmstatic int
15355682Smarkmconst_v6 (struct addrinfo *a, void *data, int port)
15455682Smarkm{
15555682Smarkm    struct sockaddr_in6 *sin6;
15655682Smarkm    struct in6_addr *addr = (struct in6_addr *)data;
15755682Smarkm
15855682Smarkm    a->ai_family  = PF_INET6;
15955682Smarkm    a->ai_addrlen = sizeof(*sin6);
16055682Smarkm    a->ai_addr    = malloc (sizeof(*sin6));
16155682Smarkm    if (a->ai_addr == NULL)
16255682Smarkm	return EAI_MEMORY;
16355682Smarkm    sin6 = (struct sockaddr_in6 *)a->ai_addr;
16455682Smarkm    memset (sin6, 0, sizeof(*sin6));
16555682Smarkm    sin6->sin6_family = AF_INET6;
16655682Smarkm    sin6->sin6_port   = port;
16755682Smarkm    sin6->sin6_addr   = *addr;
16855682Smarkm    return 0;
16955682Smarkm}
17055682Smarkm#endif
17155682Smarkm
17290926Snectar/* this is mostly a hack for some versions of AIX that has a prototype
17390926Snectar   for in6addr_loopback but no actual symbol in libc */
17490926Snectar#if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT)
17590926Snectar#define in6addr_loopback _roken_in6addr_loopback
17690926Snectarstruct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
17790926Snectar#endif
17890926Snectar
17955682Smarkmstatic int
18055682Smarkmget_null (const struct addrinfo *hints,
18155682Smarkm	  int port, int protocol, int socktype,
18255682Smarkm	  struct addrinfo **res)
18355682Smarkm{
18455682Smarkm    struct in_addr v4_addr;
18555682Smarkm#ifdef HAVE_IPV6
18655682Smarkm    struct in6_addr v6_addr;
18755682Smarkm#endif
18855682Smarkm    struct addrinfo *first = NULL;
18955682Smarkm    struct addrinfo **current = &first;
19055682Smarkm    int family = PF_UNSPEC;
19155682Smarkm    int ret;
19255682Smarkm
19355682Smarkm    if (hints != NULL)
19455682Smarkm	family = hints->ai_family;
19555682Smarkm
19655682Smarkm    if (hints && hints->ai_flags & AI_PASSIVE) {
19755682Smarkm	v4_addr.s_addr = INADDR_ANY;
19855682Smarkm#ifdef HAVE_IPV6
19955682Smarkm	v6_addr        = in6addr_any;
20055682Smarkm#endif
20155682Smarkm    } else {
20255682Smarkm	v4_addr.s_addr = htonl(INADDR_LOOPBACK);
20355682Smarkm#ifdef HAVE_IPV6
20455682Smarkm	v6_addr        = in6addr_loopback;
20555682Smarkm#endif
20655682Smarkm    }
20755682Smarkm
20855682Smarkm#ifdef HAVE_IPV6
20955682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
21055682Smarkm	ret = add_one (port, protocol, socktype,
21155682Smarkm		       &current, const_v6, &v6_addr, NULL);
21255682Smarkm    }
21355682Smarkm#endif
21455682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
21555682Smarkm	ret = add_one (port, protocol, socktype,
21655682Smarkm		       &current, const_v4, &v4_addr, NULL);
21755682Smarkm    }
21855682Smarkm    *res = first;
21955682Smarkm    return 0;
22055682Smarkm}
22155682Smarkm
22255682Smarkmstatic int
22355682Smarkmadd_hostent (int port, int protocol, int socktype,
22455682Smarkm	     struct addrinfo ***current,
22555682Smarkm	     int (*func)(struct addrinfo *, void *data, int port),
22655682Smarkm	     struct hostent *he, int *flags)
22755682Smarkm{
22855682Smarkm    int ret;
22955682Smarkm    char *canonname = NULL;
23072445Sassar    char **h;
23155682Smarkm
23255682Smarkm    if (*flags & AI_CANONNAME) {
23372445Sassar	struct hostent *he2 = NULL;
23490926Snectar	const char *tmp_canon;
23555682Smarkm
23690926Snectar	tmp_canon = hostent_find_fqdn (he);
23790926Snectar	if (strchr (tmp_canon, '.') == NULL) {
23872445Sassar	    int error;
23972445Sassar
24072445Sassar	    he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
24172445Sassar				   he->h_addrtype, &error);
24272445Sassar	    if (he2 != NULL) {
24390926Snectar		const char *tmp = hostent_find_fqdn (he2);
24472445Sassar
24572445Sassar		if (strchr (tmp, '.') != NULL)
24690926Snectar		    tmp_canon = tmp;
24755682Smarkm	    }
24872445Sassar	}
24972445Sassar
25090926Snectar	canonname = strdup (tmp_canon);
25172445Sassar	if (he2 != NULL)
25272445Sassar	    freehostent (he2);
25355682Smarkm	if (canonname == NULL)
25455682Smarkm	    return EAI_MEMORY;
25555682Smarkm    }
25655682Smarkm
25755682Smarkm    for (h = he->h_addr_list; *h != NULL; ++h) {
25855682Smarkm	ret = add_one (port, protocol, socktype,
25955682Smarkm		       current, func, *h, canonname);
26055682Smarkm	if (ret)
26155682Smarkm	    return ret;
26255682Smarkm	if (*flags & AI_CANONNAME) {
26355682Smarkm	    *flags &= ~AI_CANONNAME;
26455682Smarkm	    canonname = NULL;
26555682Smarkm	}
26655682Smarkm    }
26755682Smarkm    return 0;
26855682Smarkm}
26955682Smarkm
27055682Smarkmstatic int
27155682Smarkmget_number (const char *nodename,
27255682Smarkm	    const struct addrinfo *hints,
27355682Smarkm	    int port, int protocol, int socktype,
27455682Smarkm	    struct addrinfo **res)
27555682Smarkm{
27655682Smarkm    struct addrinfo *first = NULL;
27755682Smarkm    struct addrinfo **current = &first;
27855682Smarkm    int family = PF_UNSPEC;
27955682Smarkm    int ret;
28055682Smarkm
28155682Smarkm    if (hints != NULL) {
28255682Smarkm	family = hints->ai_family;
28355682Smarkm    }
28455682Smarkm
28555682Smarkm#ifdef HAVE_IPV6
28655682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
28755682Smarkm	struct in6_addr v6_addr;
28855682Smarkm
28955682Smarkm	if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
29055682Smarkm	    ret = add_one (port, protocol, socktype,
29155682Smarkm			   &current, const_v6, &v6_addr, NULL);
29255682Smarkm	    *res = first;
29355682Smarkm	    return ret;
29455682Smarkm	}
29555682Smarkm    }
29655682Smarkm#endif
29755682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
29855682Smarkm	struct in_addr v4_addr;
29955682Smarkm
30055682Smarkm	if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
30155682Smarkm	    ret = add_one (port, protocol, socktype,
30255682Smarkm			   &current, const_v4, &v4_addr, NULL);
30355682Smarkm	    *res = first;
30455682Smarkm	    return ret;
30555682Smarkm	}
30655682Smarkm    }
30755682Smarkm    return EAI_NONAME;
30855682Smarkm}
30955682Smarkm
31055682Smarkmstatic int
31155682Smarkmget_nodes (const char *nodename,
31255682Smarkm	   const struct addrinfo *hints,
31355682Smarkm	   int port, int protocol, int socktype,
31455682Smarkm	   struct addrinfo **res)
31555682Smarkm{
31655682Smarkm    struct addrinfo *first = NULL;
31755682Smarkm    struct addrinfo **current = &first;
31855682Smarkm    int family = PF_UNSPEC;
31955682Smarkm    int flags  = 0;
32055682Smarkm    int ret = EAI_NONAME;
32155682Smarkm    int error;
32255682Smarkm
32355682Smarkm    if (hints != NULL) {
32455682Smarkm	family = hints->ai_family;
32555682Smarkm	flags  = hints->ai_flags;
32655682Smarkm    }
32755682Smarkm
32855682Smarkm#ifdef HAVE_IPV6
32955682Smarkm    if (family == PF_INET6 || family == PF_UNSPEC) {
33055682Smarkm	struct hostent *he;
33155682Smarkm
33255682Smarkm	he = getipnodebyname (nodename, PF_INET6, 0, &error);
33355682Smarkm
33455682Smarkm	if (he != NULL) {
33555682Smarkm	    ret = add_hostent (port, protocol, socktype,
33655682Smarkm			       &current, const_v6, he, &flags);
33755682Smarkm	    freehostent (he);
33855682Smarkm	}
33955682Smarkm    }
34055682Smarkm#endif
34155682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
34255682Smarkm	struct hostent *he;
34355682Smarkm
34455682Smarkm	he = getipnodebyname (nodename, PF_INET, 0, &error);
34555682Smarkm
34655682Smarkm	if (he != NULL) {
34755682Smarkm	    ret = add_hostent (port, protocol, socktype,
34855682Smarkm			       &current, const_v4, he, &flags);
34955682Smarkm	    freehostent (he);
35055682Smarkm	}
35155682Smarkm    }
35255682Smarkm    *res = first;
35355682Smarkm    return ret;
35455682Smarkm}
35555682Smarkm
35655682Smarkm/*
35755682Smarkm * hints:
35855682Smarkm *
35955682Smarkm * struct addrinfo {
36055682Smarkm *     int    ai_flags;
36155682Smarkm *     int    ai_family;
36255682Smarkm *     int    ai_socktype;
36355682Smarkm *     int    ai_protocol;
36455682Smarkm * ...
36555682Smarkm * };
36655682Smarkm */
36755682Smarkm
368233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
36955682Smarkmgetaddrinfo(const char *nodename,
37055682Smarkm	    const char *servname,
37155682Smarkm	    const struct addrinfo *hints,
37255682Smarkm	    struct addrinfo **res)
37355682Smarkm{
37455682Smarkm    int ret;
37555682Smarkm    int port     = 0;
37655682Smarkm    int protocol = 0;
37755682Smarkm    int socktype = 0;
37855682Smarkm
37955682Smarkm    *res = NULL;
38055682Smarkm
38155682Smarkm    if (servname == NULL && nodename == NULL)
38255682Smarkm	return EAI_NONAME;
38355682Smarkm
38455682Smarkm    if (hints != NULL
38555682Smarkm	&& hints->ai_family != PF_UNSPEC
38655682Smarkm	&& hints->ai_family != PF_INET
38755682Smarkm#ifdef HAVE_IPV6
38855682Smarkm	&& hints->ai_family != PF_INET6
38955682Smarkm#endif
39055682Smarkm	)
39155682Smarkm	return EAI_FAMILY;
39255682Smarkm
39355682Smarkm    if (servname != NULL) {
39455682Smarkm	ret = get_port_protocol_socktype (servname, hints,
39555682Smarkm					  &port, &protocol, &socktype);
39655682Smarkm	if (ret)
39755682Smarkm	    return ret;
39855682Smarkm    }
39955682Smarkm    if (nodename != NULL) {
40055682Smarkm	ret = get_number (nodename, hints, port, protocol, socktype, res);
40155682Smarkm	if (ret) {
40255682Smarkm	    if(hints && hints->ai_flags & AI_NUMERICHOST)
40355682Smarkm		ret = EAI_NONAME;
40455682Smarkm	    else
40555682Smarkm		ret = get_nodes (nodename, hints, port, protocol, socktype,
40655682Smarkm				 res);
40755682Smarkm	}
40855682Smarkm    } else {
40955682Smarkm	ret = get_null (hints, port, protocol, socktype, res);
41055682Smarkm    }
41155682Smarkm    if (ret)
41255682Smarkm	freeaddrinfo (*res);
41355682Smarkm    return ret;
41455682Smarkm}
415