1 /* The Inner Net License, Version 2.00
3 The author(s) grant permission for redistribution and use in source and
4 binary forms, with or without modification, of the software and documentation
5 provided that the following conditions are met:
7 0. If you receive a version of the software that is specifically labelled
8 as not being for redistribution (check the version message and/or README),
9 you are not permitted to redistribute that version of the software in any
11 1. All terms of the all other applicable copyrights and licenses must be
13 2. Redistributions of source code must retain the authors' copyright
14 notice(s), this list of conditions, and the following disclaimer.
15 3. Redistributions in binary form must reproduce the authors' copyright
16 notice(s), this list of conditions, and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 4. All advertising materials mentioning features or use of this software
19 must display the following acknowledgement with the name(s) of the
20 authors as specified in the copyright notice(s) substituted where
23 This product includes software developed by <name(s)>, The Inner
24 Net, and other contributors.
26 5. Neither the name(s) of the author(s) nor the names of its contributors
27 may be used to endorse or promote products derived from this software
28 without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 If these license terms cause you a real problem, contact the author. */
43 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
45 /* getaddrinfo() v1.13 */
47 #include <sys/types.h>
50 #include <sys/socket.h>
53 #include <sys/utsname.h>
55 #include <netinet/in.h>
58 #include <arpa/inet.h>
60 #define GAIH_OKIFUNSPEC 0x0100
61 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
64 #define UNIX_PATH_MAX 108
75 struct gaih_servtuple *next;
81 static struct gaih_servtuple nullserv = { NULL, 0, 0, 0 };
85 struct gaih_addrtuple *next;
97 static struct gaih_typeproto gaih_inet_typeproto[] =
100 { SOCK_STREAM, IPPROTO_TCP, (char *) "tcp" },
101 { SOCK_DGRAM, IPPROTO_UDP, (char *) "udp" },
108 int (*gaih)(const char *name, const struct gaih_service *service,
109 const struct addrinfo *req, struct addrinfo **pai);
112 static struct addrinfo default_hints =
113 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
117 gaih_local (const char *name, const struct gaih_service *service,
118 const struct addrinfo *req, struct addrinfo **pai)
120 struct utsname utsname;
122 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
123 if (uname (&utsname))
128 if (strcmp(name, "localhost") &&
129 strcmp(name, "local") &&
130 strcmp(name, "unix") &&
131 strcmp(name, utsname.nodename))
132 return GAIH_OKIFUNSPEC | -EAI_NONAME;
135 if (req->ai_protocol || req->ai_socktype)
137 struct gaih_typeproto *tp = gaih_inet_typeproto;
139 for (tp++; tp->name &&
140 ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
141 ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
142 if (tp->name == NULL)
144 if (req->ai_socktype)
145 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
147 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
151 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
152 + ((req->ai_flags & AI_CANONNAME)
153 ? (strlen(utsname.nodename) + 1): 0));
157 (*pai)->ai_next = NULL;
158 (*pai)->ai_flags = req->ai_flags;
159 (*pai)->ai_family = AF_LOCAL;
160 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
161 (*pai)->ai_protocol = req->ai_protocol;
162 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
163 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
166 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
167 sizeof (struct sockaddr_un);
170 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
171 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
175 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
177 if (strchr (service->name, '/') != NULL)
179 if (strlen (service->name) >= sizeof (sunp->sun_path))
180 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
182 strcpy (sunp->sun_path, service->name);
186 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
187 sizeof (sunp->sun_path))
188 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
190 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
195 if (tmpnam (((struct sockaddr_un *) (*pai)->ai_addr)->sun_path) == NULL)
199 if (req->ai_flags & AI_CANONNAME)
200 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
201 + sizeof (struct sockaddr_un),
204 (*pai)->ai_canonname = NULL;
209 gaih_inet_serv (const char *servicename, struct gaih_typeproto *tp,
210 struct gaih_servtuple *st)
213 size_t tmpbuflen = 1024;
220 tmpbuf = __alloca (tmpbuflen);
222 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
224 if (r != 0 || s == NULL)
229 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
235 st->socktype = tp->socktype;
236 st->protocol = tp->protocol;
237 st->port = s->s_port;
242 #define gethosts(_family, _type) \
251 tmpbuf = __alloca (tmpbuflen); \
252 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
253 tmpbuflen, &h, &herrno); \
254 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
255 if (rc != 0 && herrno == NETDB_INTERNAL) \
257 __set_h_errno (herrno); \
258 return -EAI_SYSTEM; \
262 for (i = 0; h->h_addr_list[i]; i++) \
265 *pat = __alloca (sizeof(struct gaih_addrtuple)); \
266 (*pat)->next = NULL; \
267 (*pat)->family = _family; \
268 memcpy ((*pat)->addr, h->h_addr_list[i], \
270 pat = &((*pat)->next); \
273 no_data = rc != 0 && herrno == NO_DATA; \
277 gaih_inet (const char *name, const struct gaih_service *service,
278 const struct addrinfo *req, struct addrinfo **pai)
280 struct gaih_typeproto *tp = gaih_inet_typeproto;
281 struct gaih_servtuple *st = &nullserv;
282 struct gaih_addrtuple *at = NULL;
285 if (req->ai_protocol || req->ai_socktype)
287 for (tp++; tp->name &&
288 ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
289 ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
290 if (tp->name == NULL)
292 if (req->ai_socktype)
293 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
295 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
301 if (service->num < 0)
303 if (tp->name != NULL)
305 st = (struct gaih_servtuple *)
306 __alloca (sizeof (struct gaih_servtuple));
308 if ((rc = gaih_inet_serv (service->name, tp, st)))
313 struct gaih_servtuple **pst = &st;
314 for (tp++; tp->name; tp++)
316 struct gaih_servtuple *newp = (struct gaih_servtuple *)
317 __alloca (sizeof (struct gaih_servtuple));
319 if ((rc = gaih_inet_serv (service->name, tp, newp)))
321 if (rc & GAIH_OKIFUNSPEC)
330 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
335 st = __alloca (sizeof (struct gaih_servtuple));
337 st->socktype = tp->socktype;
338 st->protocol = tp->protocol;
339 st->port = htons (service->num);
345 at = __alloca (sizeof (struct gaih_addrtuple));
347 at->family = AF_UNSPEC;
350 if (inet_pton (AF_INET, name, at->addr) > 0)
352 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
353 at->family = AF_INET;
355 return -EAI_ADDRFAMILY;
358 if (at->family == AF_UNSPEC && inet_pton (AF_INET6, name, at->addr) > 0)
360 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
361 at->family = AF_INET6;
363 return -EAI_ADDRFAMILY;
366 if (at->family == AF_UNSPEC)
369 struct gaih_addrtuple **pat = &at;
373 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
374 gethosts (AF_INET6, struct in6_addr);
375 no_inet6_data = no_data;
377 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
378 gethosts (AF_INET, struct in_addr);
380 if (no_data != 0 && no_inet6_data != 0)
381 /* We made requests but they turned out no data. The name
383 return (GAIH_OKIFUNSPEC | -EAI_NODATA);
386 if (at->family == AF_UNSPEC)
387 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
391 struct gaih_addrtuple *atr;
392 atr = at = __alloca (sizeof (struct gaih_addrtuple));
393 memset (at, '\0', sizeof (struct gaih_addrtuple));
395 if (req->ai_family == 0)
397 at->next = __alloca (sizeof (struct gaih_addrtuple));
398 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
401 if (req->ai_family == 0 || req->ai_family == AF_INET6)
403 at->family = AF_INET6;
404 if ((req->ai_flags & AI_PASSIVE) == 0)
405 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
409 if (req->ai_family == 0 || req->ai_family == AF_INET)
411 atr->family = AF_INET;
412 if ((req->ai_flags & AI_PASSIVE) == 0)
413 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
421 const char *c = NULL;
422 struct gaih_servtuple *st2;
423 struct gaih_addrtuple *at2 = at;
424 size_t socklen, namelen;
427 buffer is the size of an unformatted IPv6 address in printable format.
429 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
433 if (req->ai_flags & AI_CANONNAME)
435 struct hostent *h = NULL;
439 size_t tmpbuflen = 512;
445 tmpbuf = __alloca (tmpbuflen);
450 rc = __gethostbyaddr_r (at2->addr,
451 ((at2->family == AF_INET6)
452 ? sizeof(struct in6_addr)
453 : sizeof(struct in_addr)),
454 at2->family, &th, tmpbuf, tmpbuflen,
458 while (rc == errno && herrno == NETDB_INTERNAL);
460 if (rc != 0 && herrno == NETDB_INTERNAL)
462 __set_h_errno (herrno);
467 c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
472 return GAIH_OKIFUNSPEC | -EAI_NONAME;
474 namelen = strlen (c) + 1;
479 if (at2->family == AF_INET6)
480 socklen = sizeof (struct sockaddr_in6);
482 socklen = sizeof (struct sockaddr_in);
484 for (st2 = st; st2 != NULL; st2 = st2->next)
486 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
490 (*pai)->ai_flags = req->ai_flags;
491 (*pai)->ai_family = at2->family;
492 (*pai)->ai_socktype = st2->socktype;
493 (*pai)->ai_protocol = st2->protocol;
494 (*pai)->ai_addrlen = socklen;
495 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
497 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_len = i;
499 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_family = at2->family;
500 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_port = st2->port;
502 if (at2->family == AF_INET6)
504 struct sockaddr_in6 *sin6p =
505 (struct sockaddr_in6 *) (*pai)->ai_addr;
507 sin6p->sin6_flowinfo = 0;
508 memcpy (&sin6p->sin6_addr,
509 at2->addr, sizeof (struct in6_addr));
513 struct sockaddr_in *sinp =
514 (struct sockaddr_in *) (*pai)->ai_addr;
515 memcpy (&sinp->sin_addr,
516 at2->addr, sizeof (struct in_addr));
517 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
522 (*pai)->ai_canonname = ((void *) (*pai) +
523 sizeof (struct addrinfo) + socklen);
524 strcpy ((*pai)->ai_canonname, c);
527 (*pai)->ai_canonname = NULL;
529 (*pai)->ai_next = NULL;
530 pai = &((*pai)->ai_next);
539 static struct gaih gaih[] =
541 { PF_INET6, gaih_inet },
542 { PF_INET, gaih_inet },
543 { PF_LOCAL, gaih_local },
548 getaddrinfo (const char *name, const char *service,
549 const struct addrinfo *hints, struct addrinfo **pai)
551 int i = 0, j = 0, last_i = 0;
552 struct addrinfo *p = NULL, **end;
553 struct gaih *g = gaih, *pg = NULL;
554 struct gaih_service gaih_service, *pservice;
556 if (name != NULL && name[0] == '*' && name[1] == 0)
559 if (service != NULL && service[0] == '*' && service[1] == 0)
562 if (name == NULL && service == NULL)
566 hints = &default_hints;
568 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
571 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
574 if (service && service[0])
577 gaih_service.name = service;
578 gaih_service.num = strtoul (gaih_service.name, &c, 10);
580 gaih_service.num = -1;
582 /* Can't specify a numerical socket unless a protocol family was
584 if (hints->ai_socktype == 0)
586 pservice = &gaih_service;
598 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
601 if (pg == NULL || pg->gaih != g->gaih)
604 i = g->gaih (name, pservice, hints, end);
607 /* EAI_NODATA is a more specific result as it says that
608 we found a result but it is not usable. */
609 if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
612 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
618 return -(i & GAIH_EAI);
621 while(*end) end = &((*end)->ai_next);
636 if (pai == NULL && last_i == 0)
642 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
646 freeaddrinfo (struct addrinfo *ai)