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