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