OSDN Git Service

refactor getaddrinfo and add support for most remaining features
[android-x86/external-musl-libc.git] / 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;
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
28                 const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
29                         AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV;
30                 if ((flags & mask) != flags)
31                         return EAI_BADFLAGS;
32
33                 switch (family) {
34                 case AF_INET:
35                 case AF_INET6:
36                 case AF_UNSPEC:
37                         break;
38                 default:
39                         return EAI_FAMILY;
40                 }
41
42                 switch (hint->ai_socktype) {
43                 case SOCK_STREAM:
44                         switch (proto) {
45                         case 0:
46                                 proto = IPPROTO_TCP;
47                         case IPPROTO_TCP:
48                                 break;
49                         default:
50                                 return EAI_SERVICE;
51                         }
52                         break;
53                 case SOCK_DGRAM:
54                         switch (proto) {
55                         case 0:
56                                 proto = IPPROTO_UDP;
57                         case IPPROTO_UDP:
58                                 break;
59                         default:
60                                 return EAI_SERVICE;
61                         }
62                 case 0:
63                         break;
64                 default:
65                         return EAI_SOCKTYPE;
66                 }
67         }
68
69         nservs = __lookup_serv(ports, serv, proto, flags);
70         if (nservs < 0) return nservs;
71
72         naddrs = __lookup_name(addrs, canon, host, family, flags);
73         if (naddrs < 0) return naddrs;
74
75         nais = nservs * naddrs;
76         canon_len = strlen(canon);
77         out = calloc(1, nais * sizeof(*out) + canon_len + 1);
78         if (!out) return EAI_MEMORY;
79
80         if (canon_len) {
81                 outcanon = (void *)&out[nais];
82                 memcpy(outcanon, canon, canon_len+1);
83         } else {
84                 outcanon = 0;
85         }
86
87         for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) {
88                 out[k].ai = (struct addrinfo){
89                         .ai_family = addrs[i].family,
90                         .ai_socktype = ports[j].proto == IPPROTO_TCP
91                                 ? SOCK_STREAM : SOCK_DGRAM,
92                         .ai_protocol = ports[j].proto,
93                         .ai_addrlen = addrs[i].family == AF_INET
94                                 ? sizeof(struct sockaddr_in)
95                                 : sizeof(struct sockaddr_in6),
96                         .ai_addr = (void *)&out[k].sa,
97                         .ai_canonname = outcanon,
98                         .ai_next = &out[k+1].ai };
99                 switch (addrs[i].family) {
100                 case AF_INET:
101                         out[k].sa.sin.sin_family = AF_INET;
102                         out[k].sa.sin.sin_port = htons(ports[j].port);
103                         memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4);
104                         break;
105                 case AF_INET6:
106                         out[k].sa.sin6.sin6_family = AF_INET6;
107                         out[k].sa.sin6.sin6_port = htons(ports[j].port);
108                         memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16);
109                         break;                  
110                 }
111         }
112         out[nais-1].ai.ai_next = 0;
113         *res = &out->ai;
114         return 0;
115 }