c88d558c49811d249690eeffdf75a2ed9c5c7b4e
[musl] / src / network / getaddrinfo.c
1 #include <stdlib.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <netdb.h>
5 #include <string.h>
6 #include "lookup.h"
7
8 int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
9 {
10         struct service ports[MAXSERVS];
11         struct address addrs[MAXADDRS];
12         char canon[256], *outcanon;
13         int nservs, naddrs, nais, canon_len, i, j, k;
14         int family = AF_UNSPEC, flags = 0, proto = 0, socktype = 0;
15         struct aibuf {
16                 struct addrinfo ai;
17                 union sa {
18                         struct sockaddr_in sin;
19                         struct sockaddr_in6 sin6;
20                 } sa;
21         } *out;
22
23         if (hint) {
24                 family = hint->ai_family;
25                 flags = hint->ai_flags;
26                 proto = hint->ai_protocol;
27                 socktype = hint->ai_socktype;
28
29                 const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
30                         AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV;
31                 if ((flags & mask) != flags)
32                         return EAI_BADFLAGS;
33
34                 switch (family) {
35                 case AF_INET:
36                 case AF_INET6:
37                 case AF_UNSPEC:
38                         break;
39                 default:
40                         return EAI_FAMILY;
41                 }
42         }
43
44         nservs = __lookup_serv(ports, serv, proto, socktype, flags);
45         if (nservs < 0) return nservs;
46
47         naddrs = __lookup_name(addrs, canon, host, family, flags);
48         if (naddrs < 0) return naddrs;
49
50         nais = nservs * naddrs;
51         canon_len = strlen(canon);
52         out = calloc(1, nais * sizeof(*out) + canon_len + 1);
53         if (!out) return EAI_MEMORY;
54
55         if (canon_len) {
56                 outcanon = (void *)&out[nais];
57                 memcpy(outcanon, canon, canon_len+1);
58         } else {
59                 outcanon = 0;
60         }
61
62         for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) {
63                 out[k].ai = (struct addrinfo){
64                         .ai_family = addrs[i].family,
65                         .ai_socktype = ports[j].socktype,
66                         .ai_protocol = ports[j].proto,
67                         .ai_addrlen = addrs[i].family == AF_INET
68                                 ? sizeof(struct sockaddr_in)
69                                 : sizeof(struct sockaddr_in6),
70                         .ai_addr = (void *)&out[k].sa,
71                         .ai_canonname = outcanon,
72                         .ai_next = &out[k+1].ai };
73                 switch (addrs[i].family) {
74                 case AF_INET:
75                         out[k].sa.sin.sin_family = AF_INET;
76                         out[k].sa.sin.sin_port = htons(ports[j].port);
77                         memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4);
78                         break;
79                 case AF_INET6:
80                         out[k].sa.sin6.sin6_family = AF_INET6;
81                         out[k].sa.sin6.sin6_port = htons(ports[j].port);
82                         out[k].sa.sin6.sin6_scope_id = addrs[i].scopeid;
83                         memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16);
84                         break;                  
85                 }
86         }
87         out[nais-1].ai.ai_next = 0;
88         *res = &out->ai;
89         return 0;
90 }