155682Smarkm/*
2178825Sdfr * 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#include <config.h>
3555682Smarkm
36178825Sdfr#include "roken.h"
3755682Smarkm
38178825Sdfr/*
39178825Sdfr * uses hints->ai_socktype and hints->ai_protocol
40178825Sdfr */
41178825Sdfr
42178825Sdfrstatic int
43178825Sdfrget_port_protocol_socktype (const char *servname,
44178825Sdfr			    const struct addrinfo *hints,
45178825Sdfr			    int *port,
46178825Sdfr			    int *protocol,
47178825Sdfr			    int *socktype)
48178825Sdfr{
49178825Sdfr    struct servent *se;
50178825Sdfr    const char *proto_str = NULL;
51178825Sdfr
52178825Sdfr    *socktype = 0;
53178825Sdfr
54178825Sdfr    if (hints != NULL && hints->ai_protocol != 0) {
55178825Sdfr	struct protoent *protoent = getprotobynumber (hints->ai_protocol);
56178825Sdfr
57178825Sdfr	if (protoent == NULL)
58178825Sdfr	    return EAI_SOCKTYPE; /* XXX */
59178825Sdfr
60178825Sdfr	proto_str = protoent->p_name;
61178825Sdfr	*protocol = protoent->p_proto;
62178825Sdfr    }
63178825Sdfr
64178825Sdfr    if (hints != NULL)
65178825Sdfr	*socktype = hints->ai_socktype;
66178825Sdfr
67178825Sdfr    if (*socktype == SOCK_STREAM) {
68178825Sdfr	se = getservbyname (servname, proto_str ? proto_str : "tcp");
69178825Sdfr	if (proto_str == NULL)
70178825Sdfr	    *protocol = IPPROTO_TCP;
71178825Sdfr    } else if (*socktype == SOCK_DGRAM) {
72178825Sdfr	se = getservbyname (servname, proto_str ? proto_str : "udp");
73178825Sdfr	if (proto_str == NULL)
74178825Sdfr	    *protocol = IPPROTO_UDP;
75178825Sdfr    } else if (*socktype == 0) {
76178825Sdfr	if (proto_str != NULL) {
77178825Sdfr	    se = getservbyname (servname, proto_str);
78178825Sdfr	} else {
79178825Sdfr	    se = getservbyname (servname, "tcp");
80178825Sdfr	    *protocol = IPPROTO_TCP;
81178825Sdfr	    *socktype = SOCK_STREAM;
82178825Sdfr	    if (se == NULL) {
83178825Sdfr		se = getservbyname (servname, "udp");
84178825Sdfr		*protocol = IPPROTO_UDP;
85178825Sdfr		*socktype = SOCK_DGRAM;
86178825Sdfr	    }
87178825Sdfr	}
88178825Sdfr    } else
89178825Sdfr	return EAI_SOCKTYPE;
90178825Sdfr
91178825Sdfr    if (se == NULL) {
92178825Sdfr	char *endstr;
93178825Sdfr
94178825Sdfr	*port = htons(strtol (servname, &endstr, 10));
95178825Sdfr	if (servname == endstr)
96178825Sdfr	    return EAI_NONAME;
97178825Sdfr    } else {
98178825Sdfr	*port = se->s_port;
99178825Sdfr    }
100178825Sdfr    return 0;
10155682Smarkm}
10255682Smarkm
10390926Snectarstatic int
10490926Snectaradd_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)
134178825Sdfr{
13555682Smarkm    struct sockaddr_in *sin4;
13657416Smarkm    struct in_addr *addr = (struct in_addr *)data;
13755682Smarkm
13855682Smarkm    a->ai_family  = PF_INET;
13955682Smarkm    a->ai_addrlen = sizeof(*sin4);
14055682Smarkm    a->ai_addr    = malloc (sizeof(*sin4));
14155682Smarkm    if (a->ai_addr == NULL)
14255682Smarkm	return EAI_MEMORY;
14355682Smarkm    sin4 = (struct sockaddr_in *)a->ai_addr;
14455682Smarkm    memset (sin4, 0, sizeof(*sin4));
14555682Smarkm    sin4->sin_family = AF_INET;
14655682Smarkm    sin4->sin_port   = port;
14755682Smarkm    sin4->sin_addr   = *addr;
14855682Smarkm    return 0;
14955682Smarkm}
15055682Smarkm
151178825Sdfr#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;
15990926Snectar    a->ai_addrlen = sizeof(*sin6);
16055682Smarkm    a->ai_addr    = malloc (sizeof(*sin6));
16155682Smarkm    if (a->ai_addr == NULL)
16290926Snectar	return EAI_MEMORY;
16390926Snectar    sin6 = (struct sockaddr_in6 *)a->ai_addr;
16490926Snectar    memset (sin6, 0, sizeof(*sin6));
16590926Snectar    sin6->sin6_family = AF_INET6;
16655682Smarkm    sin6->sin6_port   = port;
16755682Smarkm    sin6->sin6_addr   = *addr;
16855682Smarkm    return 0;
16955682Smarkm}
17055682Smarkm#endif
17155682Smarkm
17255682Smarkm/* this is mostly a hack for some versions of AIX that has a prototype
173178825Sdfr   for in6addr_loopback but no actual symbol in libc */
17490926Snectar#if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT)
17555682Smarkm#define in6addr_loopback _roken_in6addr_loopback
17655682Smarkmstruct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
17755682Smarkm#endif
178178825Sdfr
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
186178825Sdfr    struct in6_addr v6_addr;
18755682Smarkm#endif
18855682Smarkm    struct addrinfo *first = NULL;
18955682Smarkm    struct addrinfo **current = &first;
19055682Smarkm    int family = PF_UNSPEC;
191102644Snectar    int ret;
192102644Snectar
193102644Snectar    if (hints != NULL)
194102644Snectar	family = hints->ai_family;
195102644Snectar
196102644Snectar    if (hints && hints->ai_flags & AI_PASSIVE) {
197102644Snectar	v4_addr.s_addr = INADDR_ANY;
198102644Snectar#ifdef HAVE_IPV6
199178825Sdfr	v6_addr        = in6addr_any;
200102644Snectar#endif
201102644Snectar    } else {
202102644Snectar	v4_addr.s_addr = htonl(INADDR_LOOPBACK);
203178825Sdfr#ifdef HAVE_IPV6
204102644Snectar	v6_addr        = in6addr_loopback;
205102644Snectar#endif
206102644Snectar    }
207102644Snectar
208102644Snectar#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
214178825Sdfr    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}
22157416Smarkm
22255682Smarkmstatic int
223102644Snectaradd_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{
228178825Sdfr    int ret;
22955682Smarkm    char *canonname = NULL;
23055682Smarkm    char **h;
23155682Smarkm
23255682Smarkm    if (*flags & AI_CANONNAME) {
233102644Snectar	struct hostent *he2 = NULL;
234102644Snectar	const char *tmp_canon;
235102644Snectar
236102644Snectar	tmp_canon = hostent_find_fqdn (he);
237102644Snectar	if (strchr (tmp_canon, '.') == NULL) {
238102644Snectar	    int error;
239102644Snectar
240102644Snectar	    he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
241102644Snectar				   he->h_addrtype, &error);
242102644Snectar	    if (he2 != NULL) {
243102644Snectar		const char *tmp = hostent_find_fqdn (he2);
244102644Snectar
245102644Snectar		if (strchr (tmp, '.') != NULL)
246102644Snectar		    tmp_canon = tmp;
247102644Snectar	    }
24855682Smarkm	}
24955682Smarkm
25055682Smarkm	canonname = strdup (tmp_canon);
251102644Snectar	if (he2 != NULL)
252102644Snectar	    freehostent (he2);
25355682Smarkm	if (canonname == NULL)
254102644Snectar	    return EAI_MEMORY;
255102644Snectar    }
256102644Snectar
25755682Smarkm    for (h = he->h_addr_list; *h != NULL; ++h) {
25855682Smarkm	ret = add_one (port, protocol, socktype,
25955682Smarkm		       current, func, *h, canonname);
260178825Sdfr	if (ret)
261178825Sdfr	    return ret;
26255682Smarkm	if (*flags & AI_CANONNAME) {
26355682Smarkm	    *flags &= ~AI_CANONNAME;
26455682Smarkm	    canonname = NULL;
26555682Smarkm	}
26655682Smarkm    }
26755682Smarkm    return 0;
268178825Sdfr}
26955682Smarkm
270178825Sdfrstatic int
27155682Smarkmget_number (const char *nodename,
272178825Sdfr	    const struct addrinfo *hints,
273178825Sdfr	    int port, int protocol, int socktype,
27455682Smarkm	    struct addrinfo **res)
275178825Sdfr{
276178825Sdfr    struct addrinfo *first = NULL;
27778527Sassar    struct addrinfo **current = &first;
27878527Sassar    int family = PF_UNSPEC;
27978527Sassar    int ret;
280178825Sdfr
281178825Sdfr    if (hints != NULL) {
28278527Sassar	family = hints->ai_family;
28378527Sassar    }
284178825Sdfr
285178825Sdfr#ifdef HAVE_IPV6
286178825Sdfr    if (family == PF_INET6 || family == PF_UNSPEC) {
287178825Sdfr	struct in6_addr v6_addr;
288178825Sdfr
289178825Sdfr	if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
290178825Sdfr	    ret = add_one (port, protocol, socktype,
291178825Sdfr			   &current, const_v6, &v6_addr, NULL);
292178825Sdfr	    *res = first;
293178825Sdfr	    return ret;
294178825Sdfr	}
295178825Sdfr    }
296178825Sdfr#endif
297178825Sdfr    if (family == PF_INET || family == PF_UNSPEC) {
298178825Sdfr	struct in_addr v4_addr;
299178825Sdfr
300178825Sdfr	if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
301178825Sdfr	    ret = add_one (port, protocol, socktype,
302178825Sdfr			   &current, const_v4, &v4_addr, NULL);
303178825Sdfr	    *res = first;
304178825Sdfr	    return ret;
305178825Sdfr	}
30655682Smarkm    }
30755682Smarkm    return EAI_NONAME;
30855682Smarkm}
30955682Smarkm
31055682Smarkmstatic int
311178825Sdfrget_nodes (const char *nodename,
31255682Smarkm	   const struct addrinfo *hints,
31355682Smarkm	   int port, int protocol, int socktype,
314178825Sdfr	   struct addrinfo **res)
31555682Smarkm{
31655682Smarkm    struct addrinfo *first = NULL;
317178825Sdfr    struct addrinfo **current = &first;
318178825Sdfr    int family = PF_UNSPEC;
319178825Sdfr    int flags  = 0;
320178825Sdfr    int ret = EAI_NONAME;
321178825Sdfr    int error;
322178825Sdfr
323178825Sdfr    if (hints != NULL) {
324178825Sdfr	family = hints->ai_family;
325178825Sdfr	flags  = hints->ai_flags;
326178825Sdfr    }
327178825Sdfr
328178825Sdfr#ifdef HAVE_IPV6
329178825Sdfr    if (family == PF_INET6 || family == PF_UNSPEC) {
330178825Sdfr	struct hostent *he;
331178825Sdfr
332178825Sdfr	he = getipnodebyname (nodename, PF_INET6, 0, &error);
333178825Sdfr
334178825Sdfr	if (he != NULL) {
335178825Sdfr	    ret = add_hostent (port, protocol, socktype,
336178825Sdfr			       &current, const_v6, he, &flags);
337178825Sdfr	    freehostent (he);
338178825Sdfr	}
33955682Smarkm    }
340178825Sdfr#endif
34155682Smarkm    if (family == PF_INET || family == PF_UNSPEC) {
342178825Sdfr	struct hostent *he;
34378527Sassar
34455682Smarkm	he = getipnodebyname (nodename, PF_INET, 0, &error);
34578527Sassar
346178825Sdfr	if (he != NULL) {
34755682Smarkm	    ret = add_hostent (port, protocol, socktype,
348178825Sdfr			       &current, const_v4, he, &flags);
349178825Sdfr	    freehostent (he);
35055682Smarkm	}
35155682Smarkm    }
352178825Sdfr    *res = first;
353178825Sdfr    return ret;
354178825Sdfr}
35578527Sassar
35655682Smarkm/*
35778527Sassar * hints:
358178825Sdfr *
35955682Smarkm * struct addrinfo {
360178825Sdfr *     int    ai_flags;
361178825Sdfr *     int    ai_family;
36255682Smarkm *     int    ai_socktype;
36355682Smarkm *     int    ai_protocol;
36455682Smarkm * ...
36572445Sassar * };
366178825Sdfr */
36755682Smarkm
36855682SmarkmROKEN_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;
38078527Sassar
38155682Smarkm    if (servname == NULL && nodename == NULL)
38255682Smarkm	return EAI_NONAME;
38355682Smarkm
38490926Snectar    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,
39572445Sassar					  &port, &protocol, &socktype);
39672445Sassar	if (ret)
397178825Sdfr	    return ret;
39872445Sassar    }
39972445Sassar    if (nodename != NULL) {
40072445Sassar	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}
41555682Smarkm