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 | } | ||
