diff options
Diffstat (limited to 'gl/getaddrinfo.c')
-rw-r--r-- | gl/getaddrinfo.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/gl/getaddrinfo.c b/gl/getaddrinfo.c new file mode 100644 index 00000000..f523f765 --- /dev/null +++ b/gl/getaddrinfo.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* Get address information (partial implementation). | ||
2 | Copyright (C) 1997, 2001, 2002, 2004, 2005, 2006 Free Software | ||
3 | Foundation, Inc. | ||
4 | Contributed by Simon Josefsson <simon@josefsson.org>. | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2, or (at your option) | ||
9 | any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software Foundation, | ||
18 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | ||
19 | |||
20 | #include <config.h> | ||
21 | |||
22 | #include "getaddrinfo.h" | ||
23 | |||
24 | #if HAVE_NETINET_IN_H | ||
25 | # include <netinet/in.h> | ||
26 | #endif | ||
27 | |||
28 | /* Get calloc. */ | ||
29 | #include <stdlib.h> | ||
30 | |||
31 | /* Get memcpy. */ | ||
32 | #include <string.h> | ||
33 | |||
34 | #include <stdbool.h> | ||
35 | |||
36 | #include "gettext.h" | ||
37 | #define _(String) gettext (String) | ||
38 | #define N_(String) String | ||
39 | |||
40 | #include "inet_ntop.h" | ||
41 | #include "snprintf.h" | ||
42 | #include "strdup.h" | ||
43 | |||
44 | /* BeOS has AF_INET, but not PF_INET. */ | ||
45 | #ifndef PF_INET | ||
46 | # define PF_INET AF_INET | ||
47 | #endif | ||
48 | /* BeOS also lacks PF_UNSPEC. */ | ||
49 | #ifndef PF_UNSPEC | ||
50 | # define PF_UNSPEC 0 | ||
51 | #endif | ||
52 | |||
53 | #if defined _WIN32 || defined __WIN32__ | ||
54 | # define WIN32_NATIVE | ||
55 | #endif | ||
56 | |||
57 | #ifdef WIN32_NATIVE | ||
58 | typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*, | ||
59 | const struct addrinfo*, | ||
60 | struct addrinfo**); | ||
61 | typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*); | ||
62 | typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*, | ||
63 | socklen_t, char*, DWORD, | ||
64 | char*, DWORD, int); | ||
65 | |||
66 | static getaddrinfo_func getaddrinfo_ptr = NULL; | ||
67 | static freeaddrinfo_func freeaddrinfo_ptr = NULL; | ||
68 | static getnameinfo_func getnameinfo_ptr = NULL; | ||
69 | |||
70 | static int | ||
71 | use_win32_p (void) | ||
72 | { | ||
73 | static int done = 0; | ||
74 | HMODULE h; | ||
75 | |||
76 | if (done) | ||
77 | return getaddrinfo_ptr ? 1 : 0; | ||
78 | |||
79 | done = 1; | ||
80 | |||
81 | h = GetModuleHandle ("ws2_32.dll"); | ||
82 | |||
83 | if (h) | ||
84 | { | ||
85 | getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo"); | ||
86 | freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo"); | ||
87 | getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo"); | ||
88 | } | ||
89 | |||
90 | /* If either is missing, something is odd. */ | ||
91 | if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr) | ||
92 | { | ||
93 | getaddrinfo_ptr = NULL; | ||
94 | freeaddrinfo_ptr = NULL; | ||
95 | getnameinfo_ptr = NULL; | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | return 1; | ||
100 | } | ||
101 | #endif | ||
102 | |||
103 | static inline bool | ||
104 | validate_family (int family) | ||
105 | { | ||
106 | /* FIXME: Support more families. */ | ||
107 | #if HAVE_IPV4 | ||
108 | if (family == PF_INET) | ||
109 | return true; | ||
110 | #endif | ||
111 | #if HAVE_IPV6 | ||
112 | if (family == PF_INET6) | ||
113 | return true; | ||
114 | #endif | ||
115 | if (family == PF_UNSPEC) | ||
116 | return true; | ||
117 | return false; | ||
118 | } | ||
119 | |||
120 | /* Translate name of a service location and/or a service name to set of | ||
121 | socket addresses. */ | ||
122 | int | ||
123 | getaddrinfo (const char *restrict nodename, | ||
124 | const char *restrict servname, | ||
125 | const struct addrinfo *restrict hints, | ||
126 | struct addrinfo **restrict res) | ||
127 | { | ||
128 | struct addrinfo *tmp; | ||
129 | int port = 0; | ||
130 | struct hostent *he; | ||
131 | void *storage; | ||
132 | size_t size; | ||
133 | #if HAVE_IPV6 | ||
134 | struct v6_pair { | ||
135 | struct addrinfo addrinfo; | ||
136 | struct sockaddr_in6 sockaddr_in6; | ||
137 | }; | ||
138 | #endif | ||
139 | #if HAVE_IPV4 | ||
140 | struct v4_pair { | ||
141 | struct addrinfo addrinfo; | ||
142 | struct sockaddr_in sockaddr_in; | ||
143 | }; | ||
144 | #endif | ||
145 | |||
146 | #ifdef WIN32_NATIVE | ||
147 | if (use_win32_p ()) | ||
148 | return getaddrinfo_ptr (nodename, servname, hints, res); | ||
149 | #endif | ||
150 | |||
151 | if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE))) | ||
152 | /* FIXME: Support more flags. */ | ||
153 | return EAI_BADFLAGS; | ||
154 | |||
155 | if (hints && !validate_family (hints->ai_family)) | ||
156 | return EAI_FAMILY; | ||
157 | |||
158 | if (hints && | ||
159 | hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) | ||
160 | /* FIXME: Support other socktype. */ | ||
161 | return EAI_SOCKTYPE; /* FIXME: Better return code? */ | ||
162 | |||
163 | if (!nodename) | ||
164 | { | ||
165 | if (!(hints->ai_flags & AI_PASSIVE)) | ||
166 | return EAI_NONAME; | ||
167 | |||
168 | #ifdef HAVE_IPV6 | ||
169 | nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0"; | ||
170 | #else | ||
171 | nodename = "0.0.0.0"; | ||
172 | #endif | ||
173 | } | ||
174 | |||
175 | if (servname) | ||
176 | { | ||
177 | struct servent *se = NULL; | ||
178 | const char *proto = | ||
179 | (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; | ||
180 | |||
181 | if (!(hints->ai_flags & AI_NUMERICSERV)) | ||
182 | /* FIXME: Use getservbyname_r if available. */ | ||
183 | se = getservbyname (servname, proto); | ||
184 | |||
185 | if (!se) | ||
186 | { | ||
187 | char *c; | ||
188 | if (!(*servname >= '0' && *servname <= '9')) | ||
189 | return EAI_NONAME; | ||
190 | port = strtoul (servname, &c, 10); | ||
191 | if (*c || port > 0xffff) | ||
192 | return EAI_NONAME; | ||
193 | port = htons (port); | ||
194 | } | ||
195 | else | ||
196 | port = se->s_port; | ||
197 | } | ||
198 | |||
199 | /* FIXME: Use gethostbyname_r if available. */ | ||
200 | he = gethostbyname (nodename); | ||
201 | if (!he || he->h_addr_list[0] == NULL) | ||
202 | return EAI_NONAME; | ||
203 | |||
204 | switch (he->h_addrtype) | ||
205 | { | ||
206 | #if HAVE_IPV6 | ||
207 | case PF_INET6: | ||
208 | size = sizeof (struct v6_pair); | ||
209 | break; | ||
210 | #endif | ||
211 | |||
212 | #if HAVE_IPV4 | ||
213 | case PF_INET: | ||
214 | size = sizeof (struct v4_pair); | ||
215 | break; | ||
216 | #endif | ||
217 | |||
218 | default: | ||
219 | return EAI_NODATA; | ||
220 | } | ||
221 | |||
222 | storage = calloc (1, size); | ||
223 | if (!storage) | ||
224 | return EAI_MEMORY; | ||
225 | |||
226 | switch (he->h_addrtype) | ||
227 | { | ||
228 | #if HAVE_IPV6 | ||
229 | case PF_INET6: | ||
230 | { | ||
231 | struct v6_pair *p = storage; | ||
232 | struct sockaddr_in6 *sinp = &p->sockaddr_in6; | ||
233 | tmp = &p->addrinfo; | ||
234 | |||
235 | if (port) | ||
236 | sinp->sin6_port = port; | ||
237 | |||
238 | if (he->h_length != sizeof (sinp->sin6_addr)) | ||
239 | { | ||
240 | free (storage); | ||
241 | return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ | ||
242 | } | ||
243 | |||
244 | memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr); | ||
245 | |||
246 | tmp->ai_addr = (struct sockaddr *) sinp; | ||
247 | tmp->ai_addrlen = sizeof *sinp; | ||
248 | } | ||
249 | break; | ||
250 | #endif | ||
251 | |||
252 | #if HAVE_IPV4 | ||
253 | case PF_INET: | ||
254 | { | ||
255 | struct v4_pair *p = storage; | ||
256 | struct sockaddr_in *sinp = &p->sockaddr_in; | ||
257 | tmp = &p->addrinfo; | ||
258 | |||
259 | if (port) | ||
260 | sinp->sin_port = port; | ||
261 | |||
262 | if (he->h_length != sizeof (sinp->sin_addr)) | ||
263 | { | ||
264 | free (storage); | ||
265 | return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ | ||
266 | } | ||
267 | |||
268 | memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr); | ||
269 | |||
270 | tmp->ai_addr = (struct sockaddr *) sinp; | ||
271 | tmp->ai_addrlen = sizeof *sinp; | ||
272 | } | ||
273 | break; | ||
274 | #endif | ||
275 | |||
276 | default: | ||
277 | free (storage); | ||
278 | return EAI_NODATA; | ||
279 | } | ||
280 | |||
281 | if (hints && hints->ai_flags & AI_CANONNAME) | ||
282 | { | ||
283 | const char *cn; | ||
284 | if (he->h_name) | ||
285 | cn = he->h_name; | ||
286 | else | ||
287 | cn = nodename; | ||
288 | |||
289 | tmp->ai_canonname = strdup (cn); | ||
290 | if (!tmp->ai_canonname) | ||
291 | { | ||
292 | free (storage); | ||
293 | return EAI_MEMORY; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | tmp->ai_protocol = (hints) ? hints->ai_protocol : 0; | ||
298 | tmp->ai_socktype = (hints) ? hints->ai_socktype : 0; | ||
299 | tmp->ai_addr->sa_family = he->h_addrtype; | ||
300 | tmp->ai_family = he->h_addrtype; | ||
301 | |||
302 | /* FIXME: If more than one address, create linked list of addrinfo's. */ | ||
303 | |||
304 | *res = tmp; | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* Free `addrinfo' structure AI including associated storage. */ | ||
310 | void | ||
311 | freeaddrinfo (struct addrinfo *ai) | ||
312 | { | ||
313 | #ifdef WIN32_NATIVE | ||
314 | if (use_win32_p ()) | ||
315 | { | ||
316 | freeaddrinfo_ptr (ai); | ||
317 | return; | ||
318 | } | ||
319 | #endif | ||
320 | |||
321 | while (ai) | ||
322 | { | ||
323 | struct addrinfo *cur; | ||
324 | |||
325 | cur = ai; | ||
326 | ai = ai->ai_next; | ||
327 | |||
328 | if (cur->ai_canonname) free (cur->ai_canonname); | ||
329 | free (cur); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, | ||
334 | char *restrict node, socklen_t nodelen, | ||
335 | char *restrict service, socklen_t servicelen, | ||
336 | int flags) | ||
337 | { | ||
338 | #ifdef WIN32_NATIVE | ||
339 | if (use_win32_p ()) | ||
340 | return getnameinfo_ptr (sa, salen, node, nodelen, | ||
341 | service, servicelen, flags); | ||
342 | #endif | ||
343 | |||
344 | /* FIXME: Support other flags. */ | ||
345 | if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) || | ||
346 | (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) || | ||
347 | (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV))) | ||
348 | return EAI_BADFLAGS; | ||
349 | |||
350 | if (sa == NULL || salen < sizeof (sa->sa_family)) | ||
351 | return EAI_FAMILY; | ||
352 | |||
353 | switch (sa->sa_family) | ||
354 | { | ||
355 | #if HAVE_IPV4 | ||
356 | case AF_INET: | ||
357 | if (salen < sizeof (struct sockaddr_in)) | ||
358 | return EAI_FAMILY; | ||
359 | break; | ||
360 | #endif | ||
361 | #if HAVE_IPV6 | ||
362 | case AF_INET6: | ||
363 | if (salen < sizeof (struct sockaddr_in6)) | ||
364 | return EAI_FAMILY; | ||
365 | break; | ||
366 | #endif | ||
367 | default: | ||
368 | return EAI_FAMILY; | ||
369 | } | ||
370 | |||
371 | if (node && nodelen > 0 && flags & NI_NUMERICHOST) | ||
372 | { | ||
373 | switch (sa->sa_family) | ||
374 | { | ||
375 | #if HAVE_IPV4 | ||
376 | case AF_INET: | ||
377 | if (!inet_ntop (AF_INET, | ||
378 | &(((const struct sockaddr_in *) sa)->sin_addr), | ||
379 | node, nodelen)) | ||
380 | return EAI_SYSTEM; | ||
381 | break; | ||
382 | #endif | ||
383 | |||
384 | #if HAVE_IPV6 | ||
385 | case AF_INET6: | ||
386 | if (!inet_ntop (AF_INET6, | ||
387 | &(((const struct sockaddr_in6 *) sa)->sin6_addr), | ||
388 | node, nodelen)) | ||
389 | return EAI_SYSTEM; | ||
390 | break; | ||
391 | #endif | ||
392 | |||
393 | default: | ||
394 | return EAI_FAMILY; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | if (service && servicelen > 0 && flags & NI_NUMERICSERV) | ||
399 | switch (sa->sa_family) | ||
400 | { | ||
401 | #if HAVE_IPV4 | ||
402 | case AF_INET: | ||
403 | #endif | ||
404 | #if HAVE_IPV6 | ||
405 | case AF_INET6: | ||
406 | #endif | ||
407 | { | ||
408 | unsigned short int port | ||
409 | = ntohs (((const struct sockaddr_in *) sa)->sin_port); | ||
410 | if (servicelen <= snprintf (service, servicelen, "%u", port)) | ||
411 | return EAI_OVERFLOW; | ||
412 | } | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | return 0; | ||
417 | } | ||