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