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