]> jfr.im git - solanum.git/blame - authd/getaddrinfo.c
Merge pull request #175 from staticfox/datatypes
[solanum.git] / authd / getaddrinfo.c
CommitLineData
76ebf6c4
AC
1/*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifdef _WIN32
2dea53a2 31#include <rb_lib.h>
76ebf6c4
AC
32#include "getaddrinfo.h"
33
34/* $Id$ */
35
36static const char in_addrany[] = { 0, 0, 0, 0 };
37static const char in_loopback[] = { 127, 0, 0, 1 };
38static const char in6_addrany[] = {
39 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
40};
41static const char in6_loopback[] = {
42 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
43};
44
45static const struct afd {
46 int a_af;
47 int a_addrlen;
48 int a_socklen;
49 int a_off;
50 const char *a_addrany;
51 const char *a_loopback;
52 int a_scoped;
53} afdl [] = {
54#define N_INET6 0
55#ifdef IPV6
56 {PF_INET6, sizeof(struct in6_addr),
57 sizeof(struct sockaddr_in6),
58 offsetof(struct sockaddr_in6, sin6_addr),
59 in6_addrany, in6_loopback, 1},
60#endif
61#define N_INET 1
62 {PF_INET, sizeof(struct in_addr),
63 sizeof(struct sockaddr_in),
64 offsetof(struct sockaddr_in, sin_addr),
65 in_addrany, in_loopback, 0},
66 {0, 0, 0, 0, NULL, NULL, 0},
67};
68
69struct explore {
70 int e_af;
71 int e_socktype;
72 int e_protocol;
73 const char *e_protostr;
74 int e_wild;
75#define WILD_AF(ex) ((ex)->e_wild & 0x01)
76#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
77#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
78};
79
80static const struct explore explore[] = {
81#ifdef IPV6
82 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
83 { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
84 { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
85#endif
86 { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
87 { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
88 { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
89 { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
90 { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
91 { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
92 { -1, 0, 0, NULL, 0 },
93};
94
95#define PTON_MAX 16
96
97static int str_isnumber(const char *);
98static int explore_null(const struct rb_addrinfo *,
99 const char *, struct rb_addrinfo **);
100static int explore_numeric(const struct rb_addrinfo *, const char *,
101 const char *, struct rb_addrinfo **);
102static struct rb_addrinfo *get_ai(const struct rb_addrinfo *,
103 const struct afd *, const char *);
104static int get_portmatch(const struct rb_addrinfo *, const char *);
105static int get_port(struct rb_addrinfo *, const char *, int);
106static const struct afd *find_afd(int);
107#if 0
108/* We will need this should we ever want gai_strerror() */
109static char *ai_errlist[] = {
110 "Success",
111 "Address family for hostname not supported", /* EAI_ADDRFAMILY */
112 "Temporary failure in name resolution", /* EAI_AGAIN */
113 "Invalid value for ai_flags", /* EAI_BADFLAGS */
114 "Non-recoverable failure in name resolution", /* EAI_FAIL */
115 "ai_family not supported", /* EAI_FAMILY */
116 "Memory allocation failure", /* EAI_MEMORY */
117 "No address associated with hostname", /* EAI_NODATA */
118 "hostname nor servname provided, or not known", /* EAI_NONAME */
119 "servname not supported for ai_socktype", /* EAI_SERVICE */
120 "ai_socktype not supported", /* EAI_SOCKTYPE */
121 "System error returned in errno", /* EAI_SYSTEM */
122 "Invalid value for hints", /* EAI_BADHINTS */
123 "Resolved protocol is unknown", /* EAI_PROTOCOL */
124 "Unknown error", /* EAI_MAX */
125};
126#endif
127/* XXX macros that make external reference is BAD. */
128
129#define GET_AI(ai, afd, addr) \
130do { \
131 /* external reference: pai, error, and label free */ \
132 (ai) = get_ai(pai, (afd), (addr)); \
133 if ((ai) == NULL) { \
134 error = EAI_MEMORY; \
135 goto free; \
136 } \
137} while (/*CONSTCOND*/0)
138
139#define GET_PORT(ai, serv) \
140do { \
141 /* external reference: error and label free */ \
142 error = get_port((ai), (serv), 0); \
143 if (error != 0) \
144 goto free; \
145} while (/*CONSTCOND*/0)
146
147#define ERR(err) \
148do { \
149 /* external reference: error, and label bad */ \
150 error = (err); \
151 goto bad; \
152 /*NOTREACHED*/ \
153} while (/*CONSTCOND*/0)
154
155#define MATCH_FAMILY(x, y, w) \
156 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
157#define MATCH(x, y, w) \
158 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))
159
160#if 0
161/* We will need this should we ever want gai_strerror() */
162char *
163gai_strerror(int ecode)
164{
165 if (ecode < 0 || ecode > EAI_MAX)
166 ecode = EAI_MAX;
167 return ai_errlist[ecode];
168}
169#endif
170
171void
172rb_freeaddrinfo(struct rb_addrinfo *ai)
173{
174 struct rb_addrinfo *next;
175
176 do {
177 next = ai->ai_next;
178 if (ai->ai_canonname)
179 rb_free(ai->ai_canonname);
180 /* no need to free(ai->ai_addr) */
181 rb_free(ai);
182 ai = next;
183 } while (ai);
184}
185
186static int
187str_isnumber(const char *p)
188{
189 char *ep;
190
191 if (*p == '\0')
192 return NO;
193 ep = NULL;
194 errno = 0;
195 (void)strtoul(p, &ep, 10);
196 if (errno == 0 && ep && *ep == '\0')
197 return YES;
198 else
199 return NO;
200}
201
202int
203rb_getaddrinfo(const char *hostname, const char *servname,
204 const struct rb_addrinfo *hints, struct rb_addrinfo **res)
205{
206 struct rb_addrinfo sentinel;
207 struct rb_addrinfo *cur;
208 int error = 0;
209 struct rb_addrinfo ai;
210 struct rb_addrinfo ai0;
211 struct rb_addrinfo *pai;
212 const struct explore *ex;
213
214 memset(&sentinel, 0, sizeof(sentinel));
215 cur = &sentinel;
216 pai = &ai;
217 pai->ai_flags = 0;
218 pai->ai_family = PF_UNSPEC;
219 pai->ai_socktype = ANY;
220 pai->ai_protocol = ANY;
221 pai->ai_addrlen = 0;
222 pai->ai_canonname = NULL;
223 pai->ai_addr = NULL;
224 pai->ai_next = NULL;
225
226 if (hostname == NULL && servname == NULL)
227 return EAI_NONAME;
228 if (hints) {
229 /* error check for hints */
230 if (hints->ai_addrlen || hints->ai_canonname ||
231 hints->ai_addr || hints->ai_next)
232 ERR(EAI_BADHINTS); /* xxx */
233 if (hints->ai_flags & ~AI_MASK)
234 ERR(EAI_BADFLAGS);
235 switch (hints->ai_family) {
236 case PF_UNSPEC:
237 case PF_INET:
238#ifdef IPV6
239 case PF_INET6:
240#endif
241 break;
242 default:
243 ERR(EAI_FAMILY);
244 }
245 memcpy(pai, hints, sizeof(*pai));
246
247 /*
248 * if both socktype/protocol are specified, check if they
249 * are meaningful combination.
250 */
251 if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
252 for (ex = explore; ex->e_af >= 0; ex++) {
253 if (pai->ai_family != ex->e_af)
254 continue;
255 if (ex->e_socktype == ANY)
256 continue;
257 if (ex->e_protocol == ANY)
258 continue;
259 if (pai->ai_socktype == ex->e_socktype &&
260 pai->ai_protocol != ex->e_protocol) {
261 ERR(EAI_BADHINTS);
262 }
263 }
264 }
265 }
266
267 /*
268 * check for special cases. (1) numeric servname is disallowed if
269 * socktype/protocol are left unspecified. (2) servname is disallowed
270 * for raw and other inet{,6} sockets.
271 */
272 if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
273#ifdef IPV6
274 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
275#endif
276 ) {
277 ai0 = *pai; /* backup *pai */
278
279 if (pai->ai_family == PF_UNSPEC) {
280#ifdef IPV6
281 pai->ai_family = PF_INET6;
282#else
283 pai->ai_family = PF_INET;
284#endif
285 }
286 error = get_portmatch(pai, servname);
287 if (error)
288 ERR(error);
289
290 *pai = ai0;
291 }
292
293 ai0 = *pai;
294
295 /* NULL hostname, or numeric hostname */
296 for (ex = explore; ex->e_af >= 0; ex++) {
297 *pai = ai0;
298
299 /* PF_UNSPEC entries are prepared for DNS queries only */
300 if (ex->e_af == PF_UNSPEC)
301 continue;
302
303 if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
304 continue;
305 if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
306 continue;
307 if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
308 continue;
309
310 if (pai->ai_family == PF_UNSPEC)
311 pai->ai_family = ex->e_af;
312 if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
313 pai->ai_socktype = ex->e_socktype;
314 if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
315 pai->ai_protocol = ex->e_protocol;
316
317 if (hostname == NULL)
318 error = explore_null(pai, servname, &cur->ai_next);
319 else
320 error = explore_numeric(pai, hostname, servname, &cur->ai_next);
321
322 if (error)
323 goto free;
324
325 while (cur && cur->ai_next)
326 cur = cur->ai_next;
327 }
328
329 /*
330 * XXX
331 * If numreic representation of AF1 can be interpreted as FQDN
332 * representation of AF2, we need to think again about the code below.
333 */
334 if (sentinel.ai_next)
335 goto good;
336
337 if (pai->ai_flags & AI_NUMERICHOST)
338 ERR(EAI_NONAME);
339 if (hostname == NULL)
340 ERR(EAI_NODATA);
341
342 /* XXX */
343 if (sentinel.ai_next)
344 error = 0;
345
346 if (error)
347 goto free;
348 if (error == 0) {
349 if (sentinel.ai_next) {
350 good:
351 *res = sentinel.ai_next;
352 return SUCCESS;
353 } else
354 error = EAI_FAIL;
355 }
356 free:
357 bad:
358 if (sentinel.ai_next)
359 rb_freeaddrinfo(sentinel.ai_next);
360 *res = NULL;
361 return error;
362}
363
364/*
365 * hostname == NULL.
366 * passive socket -> anyaddr (0.0.0.0 or ::)
367 * non-passive socket -> localhost (127.0.0.1 or ::1)
368 */
369static int
370explore_null(const struct rb_addrinfo *pai, const char *servname, struct rb_addrinfo **res)
371{
372 int s;
373 const struct afd *afd;
374 struct rb_addrinfo *cur;
375 struct rb_addrinfo sentinel;
376 int error;
377
378 *res = NULL;
379 sentinel.ai_next = NULL;
380 cur = &sentinel;
381
382 /*
383 * filter out AFs that are not supported by the kernel
384 * XXX errno?
385 */
386 s = socket(pai->ai_family, SOCK_DGRAM, 0);
387 if (s < 0) {
388#ifdef _WIN32
389 errno = WSAGetLastError();
390#endif
391 if (errno != EMFILE)
392 return 0;
393 } else
394#ifdef _WIN32
395 closesocket(s);
396#else
397 close(s);
398#endif
399
400 /*
401 * if the servname does not match socktype/protocol, ignore it.
402 */
403 if (get_portmatch(pai, servname) != 0)
404 return 0;
405
406 afd = find_afd(pai->ai_family);
407 if (afd == NULL)
408 return 0;
409
410 if (pai->ai_flags & AI_PASSIVE) {
411 GET_AI(cur->ai_next, afd, afd->a_addrany);
412 GET_PORT(cur->ai_next, servname);
413 } else {
414 GET_AI(cur->ai_next, afd, afd->a_loopback);
415 GET_PORT(cur->ai_next, servname);
416 }
417 cur = cur->ai_next;
418
419 *res = sentinel.ai_next;
420 return 0;
421
422free:
423 if (sentinel.ai_next)
424 rb_freeaddrinfo(sentinel.ai_next);
425 return error;
426}
427
428/*
429 * numeric hostname
430 */
431static int
432explore_numeric(const struct rb_addrinfo *pai, const char *hostname,
433 const char *servname, struct rb_addrinfo **res)
434{
435 const struct afd *afd;
436 struct rb_addrinfo *cur;
437 struct rb_addrinfo sentinel;
438 int error;
439 char pton[PTON_MAX];
440
441 *res = NULL;
442 sentinel.ai_next = NULL;
443 cur = &sentinel;
444
445 /*
446 * if the servname does not match socktype/protocol, ignore it.
447 */
448 if (get_portmatch(pai, servname) != 0)
449 return 0;
450
451 afd = find_afd(pai->ai_family);
452 if (afd == NULL)
453 return 0;
454
455 switch (afd->a_af) {
456#if 0 /*X/Open spec*/
457 case AF_INET:
458 if (rb_inet_pton
459 if (inet_aton(hostname, (struct in_addr *)pton) == 1) {
460 if (pai->ai_family == afd->a_af ||
461 pai->ai_family == PF_UNSPEC /*?*/) {
462 GET_AI(cur->ai_next, afd, pton);
463 GET_PORT(cur->ai_next, servname);
464 while (cur && cur->ai_next)
465 cur = cur->ai_next;
466 } else
467 ERR(EAI_FAMILY); /*xxx*/
468 }
469 break;
470#endif
471 default:
472 if (rb_inet_pton(afd->a_af, hostname, pton) == 1) {
473 if (pai->ai_family == afd->a_af ||
474 pai->ai_family == PF_UNSPEC /*?*/) {
475 GET_AI(cur->ai_next, afd, pton);
476 GET_PORT(cur->ai_next, servname);
477 while (cur && cur->ai_next)
478 cur = cur->ai_next;
479 } else
480 ERR(EAI_FAMILY); /* XXX */
481 }
482 break;
483 }
484
485 *res = sentinel.ai_next;
486 return 0;
487
488free:
489bad:
490 if (sentinel.ai_next)
491 rb_freeaddrinfo(sentinel.ai_next);
492 return error;
493}
494
495static struct rb_addrinfo *
496get_ai(const struct rb_addrinfo *pai, const struct afd *afd, const char *addr)
497{
498 char *p;
499 struct rb_addrinfo *ai;
500
501 ai = (struct rb_addrinfo *)rb_malloc(sizeof(struct rb_addrinfo)
502 + (afd->a_socklen));
503 if (ai == NULL)
504 return NULL;
505
506 memcpy(ai, pai, sizeof(struct rb_addrinfo));
507 ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
508 memset(ai->ai_addr, 0, (size_t)afd->a_socklen);
509 ai->ai_addrlen = afd->a_socklen;
510 ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
511 p = (char *)(void *)(ai->ai_addr);
512 memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);
513 return ai;
514}
515
516static int
517get_portmatch(const struct rb_addrinfo *ai, const char *servname)
518{
519 struct rb_addrinfo xai;
520 memcpy(&xai, ai, sizeof(struct rb_addrinfo));
521 return(get_port(&xai, servname, 1));
522}
523
524static int
525get_port(struct rb_addrinfo *ai, const char *servname, int matchonly)
526{
527 const char *proto;
528 struct servent *sp;
529 int port;
530 int allownumeric;
531
532 if (servname == NULL)
533 return 0;
534 switch (ai->ai_family) {
535 case AF_INET:
536#ifdef AF_INET6
537 case AF_INET6:
538#endif
539 break;
540 default:
541 return 0;
542 }
543
544 switch (ai->ai_socktype) {
545 case SOCK_RAW:
546 return EAI_SERVICE;
547 case SOCK_DGRAM:
548 case SOCK_STREAM:
549 allownumeric = 1;
550 break;
551 case ANY:
552 allownumeric = 0;
553 break;
554 default:
555 return EAI_SOCKTYPE;
556 }
557
558 if (str_isnumber(servname)) {
559 if (!allownumeric)
560 return EAI_SERVICE;
561 port = atoi(servname);
562 if (port < 0 || port > 65535)
563 return EAI_SERVICE;
564 port = htons(port);
565 } else {
566 switch (ai->ai_socktype) {
567 case SOCK_DGRAM:
568 proto = "udp";
569 break;
570 case SOCK_STREAM:
571 proto = "tcp";
572 break;
573 default:
574 proto = NULL;
575 break;
576 }
577
578 if ((sp = getservbyname(servname, proto)) == NULL)
579 return EAI_SERVICE;
580 port = sp->s_port;
581 }
582
583 if (!matchonly) {
584 switch (ai->ai_family) {
585 case AF_INET:
586 ((struct sockaddr_in *)(void *)
587 ai->ai_addr)->sin_port = port;
588 break;
589#ifdef IPV6
590 case AF_INET6:
591 ((struct sockaddr_in6 *)(void *)
592 ai->ai_addr)->sin6_port = port;
593 break;
594#endif
595 }
596 }
597
598 return 0;
599}
600
601static const struct afd *
602find_afd(int af)
603{
604 const struct afd *afd;
605
606 if (af == PF_UNSPEC)
607 return(NULL);
608
609 for (afd = afdl; afd->a_af; afd++)
610 {
611 if (afd->a_af == af)
612 return(afd);
613 }
614
615 return(NULL);
616}
617#endif