* lib/getaddrinfo.c [HAVE_NETINET_IN_H]: Include <netinet/in.h>.
[gnulib.git] / lib / getaddrinfo.c
1 /* Get address information (partial implementation).
2    Copyright (C) 1997, 2001, 2002, 2004, 2005 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 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 General Public License for more details.
14
15    You should have received a copy of the GNU 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 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include "getaddrinfo.h"
24
25 #if HAVE_NETINET_IN_H
26 # include <netinet/in.h>
27 #endif
28
29 /* Get calloc. */
30 #include <stdlib.h>
31
32 /* Get memcpy. */
33 #include <string.h>
34
35 #include <stdbool.h>
36
37 #include "gettext.h"
38 #define _(String) gettext (String)
39 #define N_(String) String
40
41 #include "strdup.h"
42
43 static inline bool
44 validate_family (int family)
45 {
46   /* FIXME: Support more families. */
47 #if HAVE_IPV4
48      if (family == PF_INET)
49        return true;
50 #endif
51 #if HAVE_IPV6
52      if (family == PF_INET6)
53        return true;
54 #endif
55      if (family == PF_UNSPEC)
56        return true;
57      return false;
58 }
59
60 /* Translate name of a service location and/or a service name to set of
61    socket addresses. */
62 int
63 getaddrinfo (const char *restrict nodename,
64              const char *restrict servname,
65              const struct addrinfo *restrict hints,
66              struct addrinfo **restrict res)
67 {
68   struct addrinfo *tmp;
69   struct servent *se = NULL;
70   struct hostent *he;
71   void *storage;
72   size_t size;
73 #if HAVE_IPV6
74   struct v6_pair {
75     struct addrinfo addrinfo;
76     struct sockaddr_in6 sockaddr_in6;
77   };
78 #endif
79 #if HAVE_IPV4
80   struct v4_pair {
81     struct addrinfo addrinfo;
82     struct sockaddr_in sockaddr_in;
83   };
84 #endif
85
86   if (hints && (hints->ai_flags & ~AI_CANONNAME))
87     /* FIXME: Support more flags. */
88     return EAI_BADFLAGS;
89
90   if (hints && !validate_family (hints->ai_family))
91     return EAI_FAMILY;
92
93   if (hints &&
94       hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
95     /* FIXME: Support other socktype. */
96     return EAI_SOCKTYPE; /* FIXME: Better return code? */
97
98   if (!nodename)
99     /* FIXME: Support server bind mode. */
100     return EAI_NONAME;
101
102   if (servname)
103     {
104       const char *proto =
105         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
106
107       /* FIXME: Use getservbyname_r if available. */
108       se = getservbyname (servname, proto);
109
110       if (!se)
111         return EAI_SERVICE;
112     }
113
114   /* FIXME: Use gethostbyname_r if available. */
115   he = gethostbyname (nodename);
116   if (!he || he->h_addr_list[0] == NULL)
117     return EAI_NONAME;
118
119   switch (he->h_addrtype)
120     {
121 #if HAVE_IPV6
122     case PF_INET6:
123       size = sizeof (struct v6_pair);
124       break;
125 #endif
126
127 #if HAVE_IPV4
128     case PF_INET:
129       size = sizeof (struct v4_pair);
130       break;
131 #endif
132
133     default:
134       return EAI_NODATA;
135     }
136
137   storage = calloc (1, size);
138   if (!storage)
139     return EAI_MEMORY;
140
141   switch (he->h_addrtype)
142     {
143 #if HAVE_IPV6
144     case PF_INET6:
145       {
146         struct v6_pair *p = storage;
147         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
148         tmp = &p->addrinfo;
149
150         if (se)
151           sinp->sin6_port = se->s_port;
152
153         if (he->h_length != sizeof (sinp->sin6_addr))
154           {
155             free (storage);
156             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
157           }
158
159         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
160
161         tmp->ai_addr = (struct sockaddr *) sinp;
162         tmp->ai_addrlen = sizeof *sinp;
163       }
164       break;
165 #endif
166
167 #if HAVE_IPV4
168     case PF_INET:
169       {
170         struct v4_pair *p = storage;
171         struct sockaddr_in *sinp = &p->sockaddr_in;
172         tmp = &p->addrinfo;
173
174         if (se)
175           sinp->sin_port = se->s_port;
176
177         if (he->h_length != sizeof (sinp->sin_addr))
178           {
179             free (storage);
180             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
181           }
182
183         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
184
185         tmp->ai_addr = (struct sockaddr *) sinp;
186         tmp->ai_addrlen = sizeof *sinp;
187       }
188       break;
189 #endif
190
191     default:
192       free (storage);
193       return EAI_NODATA;
194     }
195
196   if (hints && hints->ai_flags & AI_CANONNAME)
197     {
198       const char *cn;
199       if (he->h_name)
200         cn = he->h_name;
201       else
202         cn = nodename;
203
204       tmp->ai_canonname = strdup (cn);
205       if (!tmp->ai_canonname)
206         {
207           free (storage);
208           return EAI_MEMORY;
209         }
210     }
211
212   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
213   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
214   tmp->ai_addr->sa_family = he->h_addrtype;
215
216   /* FIXME: If more than one address, create linked list of addrinfo's. */
217
218   *res = tmp;
219
220   return 0;
221 }
222
223 /* Free `addrinfo' structure AI including associated storage.  */
224 void
225 freeaddrinfo (struct addrinfo *ai)
226 {
227   while (ai)
228     {
229       struct addrinfo *cur;
230
231       cur = ai;
232       ai = ai->ai_next;
233
234       if (cur->ai_canonname) free (cur->ai_canonname);
235       free (cur);
236     }
237 }