]> git.ipfire.org Git - thirdparty/strongswan.git/blob - lib/liblwres/getaddrinfo.c
- import of strongswan-2.7.0
[thirdparty/strongswan.git] / lib / liblwres / getaddrinfo.c
1 /*
2 * Copyright (C) 1999-2001 Internet Software Consortium.
3 *
4 * This code is derived from software contributed to Internet Software
5 * Consortium by Berkeley Software Design, Inc.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM AND
12 * BERKELEY SOFTWARE DESIGN, INC DISCLAIM ALL WARRANTIES WITH REGARD TO
13 * THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
14 * FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM OR BERKELEY
15 * SOFTWARE DESIGN, INC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
17 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 /* $Id: getaddrinfo.c,v 1.1 2004/03/15 20:35:25 as Exp $ */
23
24 #include <config.h>
25
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29
30 #include <lwres/lwres.h>
31 #include <lwres/net.h>
32 #include <lwres/netdb.h>
33
34 #define SA(addr) ((struct sockaddr *)(addr))
35 #define SIN(addr) ((struct sockaddr_in *)(addr))
36 #define SIN6(addr) ((struct sockaddr_in6 *)(addr))
37 #define SUN(addr) ((struct sockaddr_un *)(addr))
38
39 static struct addrinfo
40 *ai_reverse(struct addrinfo *oai),
41 *ai_clone(struct addrinfo *oai, int family),
42 *ai_alloc(int family, int addrlen);
43 #ifdef AF_LOCAL
44 static int get_local(const char *name, int socktype, struct addrinfo **res);
45 #endif
46
47 static int add_ipv4(const char *hostname, int flags, struct addrinfo **aip,
48 int socktype, int port);
49 static int add_ipv6(const char *hostname, int flags, struct addrinfo **aip,
50 int socktype, int port);
51 static void set_order(int, int (**)(const char *, int, struct addrinfo **,
52 int, int));
53
54 #define FOUND_IPV4 0x1
55 #define FOUND_IPV6 0x2
56 #define FOUND_MAX 2
57
58 #define ISC_AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)
59
60 int
61 lwres_getaddrinfo(const char *hostname, const char *servname,
62 const struct addrinfo *hints, struct addrinfo **res)
63 {
64 struct servent *sp;
65 const char *proto;
66 int family, socktype, flags, protocol;
67 struct addrinfo *ai, *ai_list;
68 int port, err, i;
69 int (*net_order[FOUND_MAX+1])(const char *, int, struct addrinfo **,
70 int, int);
71
72 if (hostname == NULL && servname == NULL)
73 return (EAI_NONAME);
74
75 proto = NULL;
76 if (hints != NULL) {
77 if ((hints->ai_flags & ~(ISC_AI_MASK)) != 0)
78 return (EAI_BADFLAGS);
79 if (hints->ai_addrlen || hints->ai_canonname ||
80 hints->ai_addr || hints->ai_next) {
81 errno = EINVAL;
82 return (EAI_SYSTEM);
83 }
84 family = hints->ai_family;
85 socktype = hints->ai_socktype;
86 protocol = hints->ai_protocol;
87 flags = hints->ai_flags;
88 switch (family) {
89 case AF_UNSPEC:
90 switch (hints->ai_socktype) {
91 case SOCK_STREAM:
92 proto = "tcp";
93 break;
94 case SOCK_DGRAM:
95 proto = "udp";
96 break;
97 }
98 break;
99 case AF_INET:
100 case AF_INET6:
101 switch (hints->ai_socktype) {
102 case 0:
103 break;
104 case SOCK_STREAM:
105 proto = "tcp";
106 break;
107 case SOCK_DGRAM:
108 proto = "udp";
109 break;
110 case SOCK_RAW:
111 break;
112 default:
113 return (EAI_SOCKTYPE);
114 }
115 break;
116 #ifdef AF_LOCAL
117 case AF_LOCAL:
118 switch (hints->ai_socktype) {
119 case 0:
120 break;
121 case SOCK_STREAM:
122 break;
123 case SOCK_DGRAM:
124 break;
125 default:
126 return (EAI_SOCKTYPE);
127 }
128 break;
129 #endif
130 default:
131 return (EAI_FAMILY);
132 }
133 } else {
134 protocol = 0;
135 family = 0;
136 socktype = 0;
137 flags = 0;
138 }
139
140 #ifdef AF_LOCAL
141 /*
142 * First, deal with AF_LOCAL. If the family was not set,
143 * then assume AF_LOCAL if the first character of the
144 * hostname/servname is '/'.
145 */
146
147 if (hostname != NULL &&
148 (family == AF_LOCAL || (family == 0 && *hostname == '/')))
149 return (get_local(hostname, socktype, res));
150
151 if (servname != NULL &&
152 (family == AF_LOCAL || (family == 0 && *servname == '/')))
153 return (get_local(servname, socktype, res));
154 #endif
155
156 /*
157 * Ok, only AF_INET and AF_INET6 left.
158 */
159 ai_list = NULL;
160
161 /*
162 * First, look up the service name (port) if it was
163 * requested. If the socket type wasn't specified, then
164 * try and figure it out.
165 */
166 if (servname != NULL) {
167 char *e;
168
169 port = strtol(servname, &e, 10);
170 if (*e == '\0') {
171 if (socktype == 0)
172 return (EAI_SOCKTYPE);
173 if (port < 0 || port > 65535)
174 return (EAI_SERVICE);
175 port = htons((unsigned short) port);
176 } else {
177 sp = getservbyname(servname, proto);
178 if (sp == NULL)
179 return (EAI_SERVICE);
180 port = sp->s_port;
181 if (socktype == 0) {
182 if (strcmp(sp->s_proto, "tcp") == 0)
183 socktype = SOCK_STREAM;
184 else if (strcmp(sp->s_proto, "udp") == 0)
185 socktype = SOCK_DGRAM;
186 }
187 }
188 } else
189 port = 0;
190
191 /*
192 * Next, deal with just a service name, and no hostname.
193 * (we verified that one of them was non-null up above).
194 */
195 if (hostname == NULL && (flags & AI_PASSIVE) != 0) {
196 if (family == AF_INET || family == 0) {
197 ai = ai_alloc(AF_INET, sizeof(struct sockaddr_in));
198 if (ai == NULL)
199 return (EAI_MEMORY);
200 ai->ai_socktype = socktype;
201 ai->ai_protocol = protocol;
202 SIN(ai->ai_addr)->sin_port = port;
203 ai->ai_next = ai_list;
204 ai_list = ai;
205 }
206
207 if (family == AF_INET6 || family == 0) {
208 ai = ai_alloc(AF_INET6, sizeof(struct sockaddr_in6));
209 if (ai == NULL) {
210 lwres_freeaddrinfo(ai_list);
211 return (EAI_MEMORY);
212 }
213 ai->ai_socktype = socktype;
214 ai->ai_protocol = protocol;
215 SIN6(ai->ai_addr)->sin6_port = port;
216 ai->ai_next = ai_list;
217 ai_list = ai;
218 }
219
220 *res = ai_list;
221 return (0);
222 }
223
224 /*
225 * If the family isn't specified or AI_NUMERICHOST specified,
226 * check first to see if it is a numeric address.
227 * Though the gethostbyname2() routine
228 * will recognize numeric addresses, it will only recognize
229 * the format that it is being called for. Thus, a numeric
230 * AF_INET address will be treated by the AF_INET6 call as
231 * a domain name, and vice versa. Checking for both numerics
232 * here avoids that.
233 */
234 if (hostname != NULL &&
235 (family == 0 || (flags & AI_NUMERICHOST) != 0)) {
236 char abuf[sizeof(struct in6_addr)];
237 char nbuf[NI_MAXHOST];
238 int addrsize, addroff;
239 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
240 char *p, *ep;
241 char ntmp[NI_MAXHOST];
242 lwres_uint32_t scopeid;
243 #endif
244
245 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
246 /*
247 * Scope identifier portion.
248 */
249 ntmp[0] = '\0';
250 if (strchr(hostname, '%') != NULL) {
251 strncpy(ntmp, hostname, sizeof(ntmp) - 1);
252 ntmp[sizeof(ntmp) - 1] = '\0';
253 p = strchr(ntmp, '%');
254 ep = NULL;
255
256 /*
257 * Vendors may want to support non-numeric
258 * scopeid around here.
259 */
260
261 if (p != NULL)
262 scopeid = (lwres_uint32_t)strtoul(p + 1,
263 &ep, 10);
264 if (p != NULL && ep != NULL && ep[0] == '\0')
265 *p = '\0';
266 else {
267 ntmp[0] = '\0';
268 scopeid = 0;
269 }
270 } else
271 scopeid = 0;
272 #endif
273
274 if (lwres_net_pton(AF_INET, hostname, (struct in_addr *)abuf)
275 == 1)
276 {
277 if (family == AF_INET6) {
278 /*
279 * Convert to a V4 mapped address.
280 */
281 struct in6_addr *a6 = (struct in6_addr *)abuf;
282 memcpy(&a6->s6_addr[12], &a6->s6_addr[0], 4);
283 memset(&a6->s6_addr[10], 0xff, 2);
284 memset(&a6->s6_addr[0], 0, 10);
285 goto inet6_addr;
286 }
287 addrsize = sizeof(struct in_addr);
288 addroff = (char *)(&SIN(0)->sin_addr) - (char *)0;
289 family = AF_INET;
290 goto common;
291 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
292 } else if (ntmp[0] != '\0' &&
293 lwres_net_pton(AF_INET6, ntmp, abuf) == 1)
294 {
295 if (family && family != AF_INET6)
296 return (EAI_NONAME);
297 addrsize = sizeof(struct in6_addr);
298 addroff = (char *)(&SIN6(0)->sin6_addr) - (char *)0;
299 family = AF_INET6;
300 goto common;
301 #endif
302 } else if (lwres_net_pton(AF_INET6, hostname, abuf) == 1) {
303 if (family != 0 && family != AF_INET6)
304 return (EAI_NONAME);
305 inet6_addr:
306 addrsize = sizeof(struct in6_addr);
307 addroff = (char *)(&SIN6(0)->sin6_addr) - (char *)0;
308 family = AF_INET6;
309
310 common:
311 ai = ai_clone(ai_list, family);
312 if (ai == NULL)
313 return (EAI_MEMORY);
314 ai_list = ai;
315 ai->ai_socktype = socktype;
316 SIN(ai->ai_addr)->sin_port = port;
317 memcpy((char *)ai->ai_addr + addroff, abuf, addrsize);
318 if (flags & AI_CANONNAME) {
319 #if defined(LWRES_HAVE_SIN6_SCOPE_ID)
320 if (ai->ai_family == AF_INET6)
321 SIN6(ai->ai_addr)->sin6_scope_id =
322 scopeid;
323 #endif
324 if (lwres_getnameinfo(ai->ai_addr,
325 ai->ai_addrlen, nbuf, sizeof(nbuf),
326 NULL, 0,
327 NI_NUMERICHOST) == 0) {
328 ai->ai_canonname = strdup(nbuf);
329 if (ai->ai_canonname == NULL)
330 return (EAI_MEMORY);
331 } else {
332 /* XXX raise error? */
333 ai->ai_canonname = NULL;
334 }
335 }
336 goto done;
337 } else if ((flags & AI_NUMERICHOST) != 0) {
338 return (EAI_NONAME);
339 }
340 }
341
342 set_order(family, net_order);
343 for (i = 0; i < FOUND_MAX; i++) {
344 if (net_order[i] == NULL)
345 break;
346 err = (net_order[i])(hostname, flags, &ai_list,
347 socktype, port);
348 if (err != 0)
349 return (err);
350 }
351
352 if (ai_list == NULL)
353 return (EAI_NODATA);
354
355 done:
356 ai_list = ai_reverse(ai_list);
357
358 *res = ai_list;
359 return (0);
360 }
361
362 static char *
363 lwres_strsep(char **stringp, const char *delim) {
364 char *string = *stringp;
365 char *s;
366 const char *d;
367 char sc, dc;
368
369 if (string == NULL)
370 return (NULL);
371
372 for (s = string; *s != '\0'; s++) {
373 sc = *s;
374 for (d = delim; (dc = *d) != '\0'; d++)
375 if (sc == dc) {
376 *s++ = '\0';
377 *stringp = s;
378 return (string);
379 }
380 }
381 *stringp = NULL;
382 return (string);
383 }
384
385 static void
386 set_order(int family, int (**net_order)(const char *, int, struct addrinfo **,
387 int, int))
388 {
389 char *order, *tok;
390 int found;
391
392 if (family) {
393 switch (family) {
394 case AF_INET:
395 *net_order++ = add_ipv4;
396 break;
397 case AF_INET6:
398 *net_order++ = add_ipv6;
399 break;
400 }
401 } else {
402 order = getenv("NET_ORDER");
403 found = 0;
404 while (order != NULL) {
405 /*
406 * We ignore any unknown names.
407 */
408 tok = lwres_strsep(&order, ":");
409 if (strcasecmp(tok, "inet6") == 0) {
410 if ((found & FOUND_IPV6) == 0)
411 *net_order++ = add_ipv6;
412 found |= FOUND_IPV6;
413 } else if (strcasecmp(tok, "inet") == 0 ||
414 strcasecmp(tok, "inet4") == 0) {
415 if ((found & FOUND_IPV4) == 0)
416 *net_order++ = add_ipv4;
417 found |= FOUND_IPV4;
418 }
419 }
420
421 /*
422 * Add in anything that we didn't find.
423 */
424 if ((found & FOUND_IPV4) == 0)
425 *net_order++ = add_ipv4;
426 if ((found & FOUND_IPV6) == 0)
427 *net_order++ = add_ipv6;
428 }
429 *net_order = NULL;
430 return;
431 }
432
433 static char v4_loop[4] = { 127, 0, 0, 1 };
434
435 /*
436 * The test against 0 is there to keep the Solaris compiler
437 * from complaining about "end-of-loop code not reached".
438 */
439 #define ERR(code) \
440 do { result = (code); \
441 if (result != 0) goto cleanup; \
442 } while (0)
443
444 static int
445 add_ipv4(const char *hostname, int flags, struct addrinfo **aip,
446 int socktype, int port)
447 {
448 struct addrinfo *ai;
449 lwres_context_t *lwrctx = NULL;
450 lwres_gabnresponse_t *by = NULL;
451 lwres_addr_t *addr;
452 lwres_result_t lwres;
453 int result = 0;
454
455 lwres = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
456 if (lwres != LWRES_R_SUCCESS)
457 ERR(EAI_FAIL);
458 (void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
459 if (hostname == NULL && (flags & AI_PASSIVE) == 0) {
460 ai = ai_clone(*aip, AF_INET);
461 if (ai == NULL) {
462 lwres_freeaddrinfo(*aip);
463 ERR(EAI_MEMORY);
464 }
465
466 *aip = ai;
467 ai->ai_socktype = socktype;
468 SIN(ai->ai_addr)->sin_port = port;
469 memcpy(&SIN(ai->ai_addr)->sin_addr, v4_loop, 4);
470 } else {
471 lwres = lwres_getaddrsbyname(lwrctx, hostname,
472 LWRES_ADDRTYPE_V4, &by);
473 if (lwres != LWRES_R_SUCCESS) {
474 if (lwres == LWRES_R_NOTFOUND)
475 goto cleanup;
476 else
477 ERR(EAI_FAIL);
478 }
479 addr = LWRES_LIST_HEAD(by->addrs);
480 while (addr != NULL) {
481 ai = ai_clone(*aip, AF_INET);
482 if (ai == NULL) {
483 lwres_freeaddrinfo(*aip);
484 ERR(EAI_MEMORY);
485 }
486 *aip = ai;
487 ai->ai_socktype = socktype;
488 SIN(ai->ai_addr)->sin_port = port;
489 memcpy(&SIN(ai->ai_addr)->sin_addr,
490 addr->address, 4);
491 if (flags & AI_CANONNAME) {
492 ai->ai_canonname = strdup(by->realname);
493 if (ai->ai_canonname == NULL)
494 ERR(EAI_MEMORY);
495 }
496 addr = LWRES_LIST_NEXT(addr, link);
497 }
498 }
499 cleanup:
500 if (by != NULL)
501 lwres_gabnresponse_free(lwrctx, &by);
502 if (lwrctx != NULL) {
503 lwres_conf_clear(lwrctx);
504 lwres_context_destroy(&lwrctx);
505 }
506 return (result);
507 }
508
509 static char v6_loop[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
510
511 static int
512 add_ipv6(const char *hostname, int flags, struct addrinfo **aip,
513 int socktype, int port)
514 {
515 struct addrinfo *ai;
516 lwres_context_t *lwrctx = NULL;
517 lwres_gabnresponse_t *by = NULL;
518 lwres_addr_t *addr;
519 lwres_result_t lwres;
520 int result = 0;
521
522 lwres = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
523 if (lwres != LWRES_R_SUCCESS)
524 ERR(EAI_FAIL);
525 (void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
526
527 if (hostname == NULL && (flags & AI_PASSIVE) == 0) {
528 ai = ai_clone(*aip, AF_INET6);
529 if (ai == NULL) {
530 lwres_freeaddrinfo(*aip);
531 ERR(EAI_MEMORY);
532 }
533
534 *aip = ai;
535 ai->ai_socktype = socktype;
536 SIN6(ai->ai_addr)->sin6_port = port;
537 memcpy(&SIN6(ai->ai_addr)->sin6_addr, v6_loop, 16);
538 } else {
539 lwres = lwres_getaddrsbyname(lwrctx, hostname,
540 LWRES_ADDRTYPE_V6, &by);
541 if (lwres != LWRES_R_SUCCESS) {
542 if (lwres == LWRES_R_NOTFOUND)
543 goto cleanup;
544 else
545 ERR(EAI_FAIL);
546 }
547 addr = LWRES_LIST_HEAD(by->addrs);
548 while (addr != NULL) {
549 ai = ai_clone(*aip, AF_INET6);
550 if (ai == NULL) {
551 lwres_freeaddrinfo(*aip);
552 ERR(EAI_MEMORY);
553 }
554 *aip = ai;
555 ai->ai_socktype = socktype;
556 SIN6(ai->ai_addr)->sin6_port = port;
557 memcpy(&SIN6(ai->ai_addr)->sin6_addr,
558 addr->address, 16);
559 if (flags & AI_CANONNAME) {
560 ai->ai_canonname = strdup(by->realname);
561 if (ai->ai_canonname == NULL)
562 ERR(EAI_MEMORY);
563 }
564 addr = LWRES_LIST_NEXT(addr, link);
565 }
566 }
567 cleanup:
568 if (by != NULL)
569 lwres_gabnresponse_free(lwrctx, &by);
570 if (lwrctx != NULL) {
571 lwres_conf_clear(lwrctx);
572 lwres_context_destroy(&lwrctx);
573 }
574 return (result);
575 }
576
577 void
578 lwres_freeaddrinfo(struct addrinfo *ai) {
579 struct addrinfo *ai_next;
580
581 while (ai != NULL) {
582 ai_next = ai->ai_next;
583 if (ai->ai_addr != NULL)
584 free(ai->ai_addr);
585 if (ai->ai_canonname)
586 free(ai->ai_canonname);
587 free(ai);
588 ai = ai_next;
589 }
590 }
591
592 #ifdef AF_LOCAL
593 static int
594 get_local(const char *name, int socktype, struct addrinfo **res) {
595 struct addrinfo *ai;
596 struct sockaddr_un *sun;
597
598 if (socktype == 0)
599 return (EAI_SOCKTYPE);
600
601 ai = ai_alloc(AF_LOCAL, sizeof(*sun));
602 if (ai == NULL)
603 return (EAI_MEMORY);
604
605 sun = SUN(ai->ai_addr);
606 strncpy(sun->sun_path, name, sizeof(sun->sun_path));
607
608 ai->ai_socktype = socktype;
609 /*
610 * ai->ai_flags, ai->ai_protocol, ai->ai_canonname,
611 * and ai->ai_next were initialized to zero.
612 */
613
614 *res = ai;
615 return (0);
616 }
617 #endif
618
619 /*
620 * Allocate an addrinfo structure, and a sockaddr structure
621 * of the specificed length. We initialize:
622 * ai_addrlen
623 * ai_family
624 * ai_addr
625 * ai_addr->sa_family
626 * ai_addr->sa_len (LWRES_PLATFORM_HAVESALEN)
627 * and everything else is initialized to zero.
628 */
629 static struct addrinfo *
630 ai_alloc(int family, int addrlen) {
631 struct addrinfo *ai;
632
633 ai = (struct addrinfo *)calloc(1, sizeof(*ai));
634 if (ai == NULL)
635 return (NULL);
636
637 ai->ai_addr = SA(calloc(1, addrlen));
638 if (ai->ai_addr == NULL) {
639 free(ai);
640 return (NULL);
641 }
642 ai->ai_addrlen = addrlen;
643 ai->ai_family = family;
644 ai->ai_addr->sa_family = family;
645 #ifdef LWRES_PLATFORM_HAVESALEN
646 ai->ai_addr->sa_len = addrlen;
647 #endif
648 return (ai);
649 }
650
651 static struct addrinfo *
652 ai_clone(struct addrinfo *oai, int family) {
653 struct addrinfo *ai;
654
655 ai = ai_alloc(family, ((family == AF_INET6) ?
656 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)));
657
658 if (ai == NULL) {
659 lwres_freeaddrinfo(oai);
660 return (NULL);
661 }
662 if (oai == NULL)
663 return (ai);
664
665 ai->ai_flags = oai->ai_flags;
666 ai->ai_socktype = oai->ai_socktype;
667 ai->ai_protocol = oai->ai_protocol;
668 ai->ai_canonname = NULL;
669 ai->ai_next = oai;
670 return (ai);
671 }
672
673 static struct addrinfo *
674 ai_reverse(struct addrinfo *oai) {
675 struct addrinfo *nai, *tai;
676
677 nai = NULL;
678
679 while (oai != NULL) {
680 /*
681 * Grab one off the old list.
682 */
683 tai = oai;
684 oai = oai->ai_next;
685 /*
686 * Put it on the front of the new list.
687 */
688 tai->ai_next = nai;
689 nai = tai;
690 }
691 return (nai);
692 }