]> jfr.im git - solanum.git/blame - authd/getaddrinfo.c
authd/res: clean up some horribly indented code
[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 32#include "getaddrinfo.h"
cf623e08 33#include "stdinc.h"
76ebf6c4 34
76ebf6c4
AC
35static const char in_addrany[] = { 0, 0, 0, 0 };
36static const char in_loopback[] = { 127, 0, 0, 1 };
37static const char in6_addrany[] = {
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
39};
40static const char in6_loopback[] = {
41 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
42};
43
44static const struct afd {
45 int a_af;
46 int a_addrlen;
47 int a_socklen;
48 int a_off;
49 const char *a_addrany;
50 const char *a_loopback;
51 int a_scoped;
52} afdl [] = {
53#define N_INET6 0
54#ifdef IPV6
55 {PF_INET6, sizeof(struct in6_addr),
56 sizeof(struct sockaddr_in6),
57 offsetof(struct sockaddr_in6, sin6_addr),
58 in6_addrany, in6_loopback, 1},
59#endif
60#define N_INET 1
61 {PF_INET, sizeof(struct in_addr),
62 sizeof(struct sockaddr_in),
63 offsetof(struct sockaddr_in, sin_addr),
64 in_addrany, in_loopback, 0},
65 {0, 0, 0, 0, NULL, NULL, 0},
66};
67
68struct explore {
69 int e_af;
70 int e_socktype;
71 int e_protocol;
72 const char *e_protostr;
73 int e_wild;
74#define WILD_AF(ex) ((ex)->e_wild & 0x01)
75#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
76#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
77};
78
79static const struct explore explore[] = {
80#ifdef IPV6
81 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
82 { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
83 { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
84#endif
85 { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
86 { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
87 { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
88 { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
89 { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
90 { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
91 { -1, 0, 0, NULL, 0 },
92};
93
94#define PTON_MAX 16
95
3889fc11 96static bool str_isnumber(const char *);
76ebf6c4
AC
97static int explore_null(const struct rb_addrinfo *,
98 const char *, struct rb_addrinfo **);
99static int explore_numeric(const struct rb_addrinfo *, const char *,
100 const char *, struct rb_addrinfo **);
101static struct rb_addrinfo *get_ai(const struct rb_addrinfo *,
102 const struct afd *, const char *);
103static int get_portmatch(const struct rb_addrinfo *, const char *);
104static int get_port(struct rb_addrinfo *, const char *, int);
105static const struct afd *find_afd(int);
106#if 0
107/* We will need this should we ever want gai_strerror() */
108static char *ai_errlist[] = {
109 "Success",
110 "Address family for hostname not supported", /* EAI_ADDRFAMILY */
111 "Temporary failure in name resolution", /* EAI_AGAIN */
112 "Invalid value for ai_flags", /* EAI_BADFLAGS */
113 "Non-recoverable failure in name resolution", /* EAI_FAIL */
114 "ai_family not supported", /* EAI_FAMILY */
115 "Memory allocation failure", /* EAI_MEMORY */
116 "No address associated with hostname", /* EAI_NODATA */
117 "hostname nor servname provided, or not known", /* EAI_NONAME */
118 "servname not supported for ai_socktype", /* EAI_SERVICE */
119 "ai_socktype not supported", /* EAI_SOCKTYPE */
120 "System error returned in errno", /* EAI_SYSTEM */
121 "Invalid value for hints", /* EAI_BADHINTS */
122 "Resolved protocol is unknown", /* EAI_PROTOCOL */
123 "Unknown error", /* EAI_MAX */
124};
125#endif
126/* XXX macros that make external reference is BAD. */
127
128#define GET_AI(ai, afd, addr) \
129do { \
130 /* external reference: pai, error, and label free */ \
131 (ai) = get_ai(pai, (afd), (addr)); \
132 if ((ai) == NULL) { \
133 error = EAI_MEMORY; \
134 goto free; \
135 } \
136} while (/*CONSTCOND*/0)
137
138#define GET_PORT(ai, serv) \
139do { \
140 /* external reference: error and label free */ \
141 error = get_port((ai), (serv), 0); \
142 if (error != 0) \
143 goto free; \
144} while (/*CONSTCOND*/0)
145
146#define ERR(err) \
147do { \
148 /* external reference: error, and label bad */ \
149 error = (err); \
150 goto bad; \
151 /*NOTREACHED*/ \
152} while (/*CONSTCOND*/0)
153
154#define MATCH_FAMILY(x, y, w) \
155 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
156#define MATCH(x, y, w) \
157 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))
158
159#if 0
160/* We will need this should we ever want gai_strerror() */
161char *
162gai_strerror(int ecode)
163{
164 if (ecode < 0 || ecode > EAI_MAX)
165 ecode = EAI_MAX;
166 return ai_errlist[ecode];
167}
168#endif
169
170void
171rb_freeaddrinfo(struct rb_addrinfo *ai)
172{
173 struct rb_addrinfo *next;
174
175 do {
176 next = ai->ai_next;
177 if (ai->ai_canonname)
178 rb_free(ai->ai_canonname);
179 /* no need to free(ai->ai_addr) */
180 rb_free(ai);
181 ai = next;
182 } while (ai);
183}
184
3889fc11 185static bool
76ebf6c4
AC
186str_isnumber(const char *p)
187{
188 char *ep;
189
190 if (*p == '\0')
cf623e08
MU
191 return false;
192
76ebf6c4
AC
193 ep = NULL;
194 errno = 0;
195 (void)strtoul(p, &ep, 10);
196 if (errno == 0 && ep && *ep == '\0')
3889fc11 197 return true;
76ebf6c4 198 else
3889fc11 199 return false;
76ebf6c4
AC
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;
c056dba2 500
76ebf6c4
AC
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;
c056dba2 520 memcpy(&xai, ai, sizeof(struct rb_addrinfo));
76ebf6c4
AC
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