Code

f09cf9ee641a4aef4c86bede901805d43229fac3
[nagiosplug.git] / gl / getaddrinfo.c
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>.
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 3, or (at your option)
9    any later version.
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.
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.  */
20 #include <config.h>
22 #include <netdb.h>
24 #if HAVE_NETINET_IN_H
25 # include <netinet/in.h>
26 #endif
28 /* Get inet_ntop.  */
29 #include <arpa/inet.h>
31 /* Get calloc. */
32 #include <stdlib.h>
34 /* Get memcpy, strdup. */
35 #include <string.h>
37 /* Get snprintf. */
38 #include <stdio.h>
40 #include <stdbool.h>
42 #include "gettext.h"
43 #define _(String) gettext (String)
44 #define N_(String) String
46 /* BeOS has AF_INET, but not PF_INET.  */
47 #ifndef PF_INET
48 # define PF_INET AF_INET
49 #endif
50 /* BeOS also lacks PF_UNSPEC.  */
51 #ifndef PF_UNSPEC
52 # define PF_UNSPEC 0
53 #endif
55 #if defined _WIN32 || defined __WIN32__
56 # define WIN32_NATIVE
57 #endif
59 #ifdef WIN32_NATIVE
60 typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
61                                         const struct addrinfo*,
62                                         struct addrinfo**);
63 typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
64 typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
65                                         socklen_t, char*, DWORD,
66                                         char*, DWORD, int);
68 static getaddrinfo_func getaddrinfo_ptr = NULL;
69 static freeaddrinfo_func freeaddrinfo_ptr = NULL;
70 static getnameinfo_func getnameinfo_ptr = NULL;
72 static int
73 use_win32_p (void)
74 {
75   static int done = 0;
76   HMODULE h;
78   if (done)
79     return getaddrinfo_ptr ? 1 : 0;
81   done = 1;
83   h = GetModuleHandle ("ws2_32.dll");
85   if (h)
86     {
87       getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
88       freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
89       getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
90     }
92   /* If either is missing, something is odd. */
93   if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
94     {
95       getaddrinfo_ptr = NULL;
96       freeaddrinfo_ptr = NULL;
97       getnameinfo_ptr = NULL;
98       return 0;
99     }
101   return 1;
103 #endif
105 static inline bool
106 validate_family (int family)
108   /* FIXME: Support more families. */
109 #if HAVE_IPV4
110      if (family == PF_INET)
111        return true;
112 #endif
113 #if HAVE_IPV6
114      if (family == PF_INET6)
115        return true;
116 #endif
117      if (family == PF_UNSPEC)
118        return true;
119      return false;
122 /* Translate name of a service location and/or a service name to set of
123    socket addresses. */
124 int
125 getaddrinfo (const char *restrict nodename,
126              const char *restrict servname,
127              const struct addrinfo *restrict hints,
128              struct addrinfo **restrict res)
130   struct addrinfo *tmp;
131   int port = 0;
132   struct hostent *he;
133   void *storage;
134   size_t size;
135 #if HAVE_IPV6
136   struct v6_pair {
137     struct addrinfo addrinfo;
138     struct sockaddr_in6 sockaddr_in6;
139   };
140 #endif
141 #if HAVE_IPV4
142   struct v4_pair {
143     struct addrinfo addrinfo;
144     struct sockaddr_in sockaddr_in;
145   };
146 #endif
148 #ifdef WIN32_NATIVE
149   if (use_win32_p ())
150     return getaddrinfo_ptr (nodename, servname, hints, res);
151 #endif
153   if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE)))
154     /* FIXME: Support more flags. */
155     return EAI_BADFLAGS;
157   if (hints && !validate_family (hints->ai_family))
158     return EAI_FAMILY;
160   if (hints &&
161       hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
162     /* FIXME: Support other socktype. */
163     return EAI_SOCKTYPE; /* FIXME: Better return code? */
165   if (!nodename)
166     {
167       if (!(hints->ai_flags & AI_PASSIVE))
168         return EAI_NONAME;
170 #ifdef HAVE_IPV6
171       nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0";
172 #else
173       nodename = "0.0.0.0";
174 #endif
175     }
177   if (servname)
178     {
179       struct servent *se = NULL;
180       const char *proto =
181         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
183       if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV))
184         /* FIXME: Use getservbyname_r if available. */
185         se = getservbyname (servname, proto);
187       if (!se)
188         {
189           char *c;
190           if (!(*servname >= '0' && *servname <= '9'))
191             return EAI_NONAME;
192           port = strtoul (servname, &c, 10);
193           if (*c || port > 0xffff)
194             return EAI_NONAME;
195           port = htons (port);
196         }
197       else
198         port = se->s_port;
199     }
201   /* FIXME: Use gethostbyname_r if available. */
202   he = gethostbyname (nodename);
203   if (!he || he->h_addr_list[0] == NULL)
204     return EAI_NONAME;
206   switch (he->h_addrtype)
207     {
208 #if HAVE_IPV6
209     case PF_INET6:
210       size = sizeof (struct v6_pair);
211       break;
212 #endif
214 #if HAVE_IPV4
215     case PF_INET:
216       size = sizeof (struct v4_pair);
217       break;
218 #endif
220     default:
221       return EAI_NODATA;
222     }
224   storage = calloc (1, size);
225   if (!storage)
226     return EAI_MEMORY;
228   switch (he->h_addrtype)
229     {
230 #if HAVE_IPV6
231     case PF_INET6:
232       {
233         struct v6_pair *p = storage;
234         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
235         tmp = &p->addrinfo;
237         if (port)
238           sinp->sin6_port = port;
240         if (he->h_length != sizeof (sinp->sin6_addr))
241           {
242             free (storage);
243             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
244           }
246         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
248         tmp->ai_addr = (struct sockaddr *) sinp;
249         tmp->ai_addrlen = sizeof *sinp;
250       }
251       break;
252 #endif
254 #if HAVE_IPV4
255     case PF_INET:
256       {
257         struct v4_pair *p = storage;
258         struct sockaddr_in *sinp = &p->sockaddr_in;
259         tmp = &p->addrinfo;
261         if (port)
262           sinp->sin_port = port;
264         if (he->h_length != sizeof (sinp->sin_addr))
265           {
266             free (storage);
267             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
268           }
270         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
272         tmp->ai_addr = (struct sockaddr *) sinp;
273         tmp->ai_addrlen = sizeof *sinp;
274       }
275       break;
276 #endif
278     default:
279       free (storage);
280       return EAI_NODATA;
281     }
283   if (hints && hints->ai_flags & AI_CANONNAME)
284     {
285       const char *cn;
286       if (he->h_name)
287         cn = he->h_name;
288       else
289         cn = nodename;
291       tmp->ai_canonname = strdup (cn);
292       if (!tmp->ai_canonname)
293         {
294           free (storage);
295           return EAI_MEMORY;
296         }
297     }
299   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
300   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
301   tmp->ai_addr->sa_family = he->h_addrtype;
302   tmp->ai_family = he->h_addrtype;
304 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
305   switch (he->h_addrtype)
306     {
307 #if HAVE_IPV4
308     case AF_INET:
309       tmp->ai_addr->sa_len = sizeof (struct sockaddr_in);
310       break;
311 #endif
312 #if HAVE_IPV6
313     case AF_INET6:
314       tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6);
315       break;
316 #endif
317     }
318 #endif
320   /* FIXME: If more than one address, create linked list of addrinfo's. */
322   *res = tmp;
324   return 0;
327 /* Free `addrinfo' structure AI including associated storage.  */
328 void
329 freeaddrinfo (struct addrinfo *ai)
331 #ifdef WIN32_NATIVE
332   if (use_win32_p ())
333     {
334       freeaddrinfo_ptr (ai);
335       return;
336     }
337 #endif
339   while (ai)
340     {
341       struct addrinfo *cur;
343       cur = ai;
344       ai = ai->ai_next;
346       free (cur->ai_canonname);
347       free (cur);
348     }
351 int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
352                 char *restrict node, socklen_t nodelen,
353                 char *restrict service, socklen_t servicelen,
354                 int flags)
356 #ifdef WIN32_NATIVE
357   if (use_win32_p ())
358     return getnameinfo_ptr (sa, salen, node, nodelen,
359                             service, servicelen, flags);
360 #endif
362   /* FIXME: Support other flags. */
363   if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
364       (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
365       (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
366     return EAI_BADFLAGS;
368   if (sa == NULL || salen < sizeof (sa->sa_family))
369     return EAI_FAMILY;
371   switch (sa->sa_family)
372     {
373 #if HAVE_IPV4
374     case AF_INET:
375       if (salen < sizeof (struct sockaddr_in))
376         return EAI_FAMILY;
377       break;
378 #endif
379 #if HAVE_IPV6
380     case AF_INET6:
381       if (salen < sizeof (struct sockaddr_in6))
382         return EAI_FAMILY;
383       break;
384 #endif
385     default:
386       return EAI_FAMILY;
387     }
389   if (node && nodelen > 0 && flags & NI_NUMERICHOST)
390     {
391       switch (sa->sa_family)
392         {
393 #if HAVE_IPV4
394         case AF_INET:
395           if (!inet_ntop (AF_INET,
396                           &(((const struct sockaddr_in *) sa)->sin_addr),
397                           node, nodelen))
398             return EAI_SYSTEM;
399           break;
400 #endif
402 #if HAVE_IPV6
403         case AF_INET6:
404           if (!inet_ntop (AF_INET6,
405                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
406                           node, nodelen))
407             return EAI_SYSTEM;
408           break;
409 #endif
411         default:
412           return EAI_FAMILY;
413         }
414     }
416   if (service && servicelen > 0 && flags & NI_NUMERICSERV)
417     switch (sa->sa_family)
418       {
419 #if HAVE_IPV4
420       case AF_INET:
421 #endif
422 #if HAVE_IPV6
423       case AF_INET6:
424 #endif
425         {
426           unsigned short int port
427             = ntohs (((const struct sockaddr_in *) sa)->sin_port);
428           if (servicelen <= snprintf (service, servicelen, "%u", port))
429             return EAI_OVERFLOW;
430         }
431         break;
432       }
434   return 0;