getaddrinfo.c 9.2 KB

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