Stop using snprintf in write_hex_to_fd (XCC)
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / getaddrinfo.c
1 /* Copyright (c) 2015 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * getaddrinfo/freeaddrinfo.
6  *
7  * Supports IPv4, TCP, UDP, and maybe a couple protocols/socktypes.
8  *
9  * Doesn't handle service resolution yet, and only returns one addrinfo.
10  */
11
12 #include <netdb.h>
13 #include <malloc.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <arpa/inet.h>
17 #include <netinet/in.h>
18 #include <unistd.h>
19
20 /* Helper, given the serv string, figures out the port and protocol.  Returns 0
21  * on success or an EAI_ error. */
22 static int serv_get_portprotocol(const char *serv, unsigned long *port_ret,
23                                  int *protocol_ret, struct addrinfo *hints)
24 {
25         char *strtoul_end = 0;
26         unsigned long port = 0; /* uninitialized, up to the main caller */
27         int protocol = 0;               /* any protocol, will assume TCP and UDP later */
28         if (serv) {
29                 if (serv[0] == '\0')
30                         return EAI_NONAME;
31                 port = strtoul(serv, &strtoul_end, 0);
32                 /* Already checked that serv != \0 */
33                 if (*strtoul_end != '\0') {
34                         if (hints->ai_flags & AI_NUMERICSERV)
35                                 return EAI_NONAME;
36                         /* CS lookup */
37                         /* TODO: get a port, maybe a protocol.  If we have a restriction on
38                          * the protocol from hints, check that here.  Probably need to
39                          * rework this a bit if we have multiple protocols. */
40                         return EAI_NONAME;
41                 }
42         }
43         *port_ret = port;
44         *protocol_ret = protocol;
45         return 0;
46 }
47
48 /* Helper: fills in all of the addr related info in ai for an ipv4 addr/port.
49  * addr is in network order.  port is in host order. */
50 static void addrinfo_filladdr_ipv4(struct addrinfo *ai, in_addr_t addr,
51                                    uint16_t port)
52 {
53         struct sockaddr_in *sa_in4 = (struct sockaddr_in*)ai->ai_addr;
54         ai->ai_family = AF_INET;
55         ai->ai_addrlen = sizeof(struct sockaddr_in);
56         sa_in4->sin_family = AF_INET;
57         sa_in4->sin_addr.s_addr = addr;
58         sa_in4->sin_port = htons(port);
59 }
60
61 static int resolve_name_to_ipv4(const char *name, struct in_addr *hostaddr)
62 {
63         struct hostent host, *result;
64         char buf[1024];
65         int _herrno;
66         int ret;
67         ret = gethostbyname2_r(name, AF_INET, &host, buf, sizeof(buf),
68                                &result, &_herrno);
69         /* TODO: deal with the herrno errors and errno errors. */
70         if (ret)
71                 return -1;
72         assert(result == &host);
73         memcpy(hostaddr, host.h_addr_list[0], host.h_length);
74         return 0;
75 }
76
77 /* Given the node string, fills in the addr related info in ai: the ai_family,
78  * ai_addr, and ai_addrlen.  The ai_addr needs the port too, though that's a big
79  * ugly.  We only support ipv4, so there's just one of these.  Returns 0 on
80  * success, EAI_ error o/w. */
81 static int node_fill_addrinfo(const char *node, struct addrinfo *ai,
82                               uint16_t port, struct addrinfo *hints)
83 {
84         struct in_addr local_addr;
85         if (!node) {
86                 /* AI_PASSIVE only matters with no node name */
87                 if (hints->ai_flags & AI_PASSIVE) {
88                         /* Caller wants to bind */
89                         addrinfo_filladdr_ipv4(ai, INADDR_ANY, port);
90                 } else {
91                         /* Caller wants to send from localhost */
92                         addrinfo_filladdr_ipv4(ai, INADDR_LOOPBACK, port);
93                 }
94         } else {
95                 if (inet_pton(AF_INET, node, &local_addr) == 1) {
96                         addrinfo_filladdr_ipv4(ai, local_addr.s_addr, port);
97                 } else {
98                         if (hints->ai_flags & AI_NUMERICHOST)
99                                 return EAI_NONAME;
100                         if (resolve_name_to_ipv4(node, &local_addr))
101                                 return EAI_FAIL;
102                         addrinfo_filladdr_ipv4(ai, local_addr.s_addr, port);
103                 }
104         }
105         return 0;
106 }
107
108 static int proto_to_socktype(int protocol)
109 {
110         switch (protocol) {
111                 case IPPROTO_IP:
112                 case IPPROTO_TCP:
113                         return SOCK_STREAM;
114                 case IPPROTO_ICMP:
115                 case IPPROTO_UDP:
116                         return SOCK_DGRAM;
117                 case IPPROTO_RAW:
118                         return SOCK_RAW;
119         }
120         return -1;
121 }
122
123 static int socktype_to_proto(int socktype)
124 {
125         switch (socktype) {
126                 case SOCK_STREAM:
127                         return IPPROTO_TCP;
128                 case SOCK_DGRAM:
129                         return IPPROTO_UDP;
130                 case SOCK_RAW:
131                         return IPPROTO_RAW;
132         }
133         return -1;
134 }
135
136 int getaddrinfo(const char *node, const char *serv,
137                 const struct addrinfo *hints, struct addrinfo **res)
138 {
139         struct addrinfo *ai;
140         struct addrinfo local_hints;
141
142         local_hints.ai_socktype = 0;
143         local_hints.ai_protocol = 0;
144         local_hints.ai_family = AF_UNSPEC;
145         local_hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
146
147         unsigned long port = 0; /* AF agnostic, hope a long is enough */
148         int protocol = 0;
149         int ret;
150
151         if (hints)
152                 local_hints = *hints;
153
154         if (!node && !serv)
155                 return EAI_NONAME;
156
157         /* Only support IPv4 for now */
158         if ((local_hints.ai_family != AF_UNSPEC) &&
159             (local_hints.ai_family != AF_INET))
160                 return EAI_FAMILY;
161
162         ai = malloc(sizeof(struct addrinfo));
163         memset(ai, 0, sizeof(struct addrinfo));
164         ai->ai_addr = malloc(sizeof(struct sockaddr_storage));
165         memset(ai->ai_addr, 0, sizeof(struct sockaddr_storage));
166
167         /* Only supporting TCP and UDP for now.  If protocol is 0, later we'll make
168          * addrinfos for both (TODO).  Likewise, we only support DGRAM or STREAM for
169          * socktype. */
170         if ((ret = serv_get_portprotocol(serv, &port, &protocol, &local_hints))) {
171                 freeaddrinfo(ai);
172                 return ret;
173         }
174         if ((ret = node_fill_addrinfo(node, ai, port, &local_hints))) {
175                 freeaddrinfo(ai);
176                 return ret;
177         }
178         /* We have a mostly full addrinfo.  Still missing ai_protocol, socktype,
179          * flags (already 0) and canonname (already 0).
180          *
181          * We might have restrictions on our protocol from the hints or from what
182          * serv specifies.  If we don't have a protocol yet (== 0), that means we
183          * have no restrictions from serv. */
184         if (local_hints.ai_protocol) {
185                 if (protocol && protocol != local_hints.ai_protocol) {
186                         /* requested protocol wasn't available */
187                         freeaddrinfo(ai);
188                         return EAI_SERVICE;
189                 }
190                 ai->ai_protocol = local_hints.ai_protocol;
191         } else if (protocol) {
192                 ai->ai_protocol = protocol;
193         } else if (local_hints.ai_socktype) {
194                 ai->ai_protocol = socktype_to_proto(local_hints.ai_socktype);
195         } else {
196                 /* no serv restrictions, no preferences */
197                 ai->ai_protocol = IPPROTO_TCP;
198                 /* TODO: consider building a second addrinfo for UDP.  while you're at
199                  * it, support IPv6 and a bunch of other options! */
200         }
201         if (ai->ai_protocol == -1) {
202                 freeaddrinfo(ai);
203                 return EAI_SERVICE;
204         }
205         ai->ai_socktype = proto_to_socktype(ai->ai_protocol);
206         if (local_hints.ai_socktype &&
207             (local_hints.ai_socktype != ai->ai_socktype)) {
208                 /* they requested a socktype, but we can't handle it */
209                 freeaddrinfo(ai);
210                 return EAI_SOCKTYPE;
211         }
212         /* TODO: support AI_CANONNAME, only in the first ai */
213         *res = ai;
214         return 0;
215 }
216 libc_hidden_def(getaddrinfo)
217
218 void freeaddrinfo(struct addrinfo *ai)
219 {
220         struct addrinfo *next;
221         if (!ai)
222                 return;
223         free(ai->ai_addr);
224         free(ai->ai_canonname);
225         next = ai->ai_next;
226         free(ai);
227         freeaddrinfo(next);
228 }
229 libc_hidden_def(freeaddrinfo)