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