Add heimdal-0.6.3
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / roken / getaddrinfo.c
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 }