]> git.ipfire.org Git - thirdparty/squid.git/blame - lib/getaddrinfo.c
Merged from trunk.
[thirdparty/squid.git] / lib / getaddrinfo.c
CommitLineData
0e076fb1 1/*
2 * Shamelessly duplicated from the fetchmail public sources
3 * for use by the Squid Project under GNU Public License.
4 *
5 * Update/Maintenance History:
6 *
7 * 15-Aug-2007 : Copied from fetchmail 6.3.8
8 * - added protection around libray headers
9 *
10 * 16-Aug-2007 : Altered configure checks
11 * Un-hacked slightly to use system gethostbyname()
12 *
13 * 06-Oct-2007 : Various fixes to allow the build on MinGW
14 *
15 * Squid CVS $Id: getaddrinfo.c,v 1.1 2007/12/14 05:03:26 amosjeffries Exp $
16 *
17 * Original License and code follows.
18 */
19#include "config.h"
20
21/*
22 * This file is part of libESMTP, a library for submission of RFC 2822
23 * formatted electronic mail messages using the SMTP protocol described
24 * in RFC 2821.
25 *
26 * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2.1 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library; if not, write to the Free Software
40 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41 */
42
43/* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface.
44 */
45
46#ifndef HAVE_GETADDRINFO
47
48/* Need to turn off Posix features in glibc to build this */
49#undef _POSIX_C_SOURCE
50#undef _XOPEN_SOURCE
51
52#if HAVE_STDLIB_H
53#include <stdlib.h>
54#endif
55#if HAVE_STRING_H
56#include <string.h>
57#endif
58#if HAVE_CTYPE_H
59#include <ctype.h>
60#endif
61#if HAVE_ERRNO_H
62#include <errno.h>
63#endif
64
65#if HAVE_SYS_SOCKET_H
66#include <sys/socket.h>
67#endif
68#if HAVE_NETINET_IN_H
69#include <netinet/in.h>
70#endif
71#if HAVE_ARPA_INET_H
72#include <arpa/inet.h>
73#endif
74#if HAVE_NETDB_H
75#include <netdb.h>
76#endif
77#ifdef _SQUID_MSWIN_
78#undef IN_ADDR
79#include <ws2tcpip.h>
80#endif
81
82#include "getaddrinfo.h"
2da53de5 83#include "inet_pton.h"
0e076fb1 84
85static struct addrinfo *
86dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen)
87{
88 struct addrinfo *ret;
89
90 ret = malloc (sizeof (struct addrinfo));
91 if (ret == NULL)
92 return NULL;
93 memcpy (ret, info, sizeof (struct addrinfo));
94 ret->ai_addr = malloc (addrlen);
95 if (ret->ai_addr == NULL)
96 {
97 free (ret);
98 return NULL;
99 }
100 memcpy (ret->ai_addr, addr, addrlen);
101 ret->ai_addrlen = addrlen;
102 return ret;
103}
104
105int
106xgetaddrinfo (const char *nodename, const char *servname,
107 const struct addrinfo *hints, struct addrinfo **res)
108{
109 struct hostent *hp;
110 struct servent *servent;
111 const char *socktype;
112 int port;
113 struct addrinfo hint, result;
114 struct addrinfo *ai, *sai, *eai;
115 char **addrs;
116
117 if (servname == NULL && nodename == NULL)
118 return EAI_NONAME;
119
120 memset (&result, 0, sizeof result);
121
122 /* default for hints */
123 if (hints == NULL)
124 {
125 memset (&hint, 0, sizeof hint);
126 hint.ai_family = PF_UNSPEC;
127 hints = &hint;
128 }
129
130 if (servname == NULL)
131 port = 0;
132 else {
133 /* check for tcp or udp sockets only */
134 if (hints->ai_socktype == SOCK_STREAM)
135 socktype = "tcp";
136 else if (hints->ai_socktype == SOCK_DGRAM)
137 socktype = "udp";
138 else
139 return EAI_SERVICE;
140 result.ai_socktype = hints->ai_socktype;
141
142 /* Note: maintain port in host byte order to make debugging easier */
143 if (isdigit (*servname))
144 port = strtol (servname, NULL, 10);
145 else if ((servent = getservbyname (servname, socktype)) != NULL)
146 port = ntohs (servent->s_port);
147 else
148 return EAI_NONAME;
149 }
150
151 /* if nodename == NULL refer to the local host for a client or any
152 for a server */
153 if (nodename == NULL)
154 {
155 struct sockaddr_in sin;
156
157 /* check protocol family is PF_UNSPEC or PF_INET - could try harder
158 for IPv6 but that's more code than I'm prepared to write */
159 if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
160 result.ai_family = AF_INET;
161 else
162 return EAI_FAMILY;
163
164 sin.sin_family = result.ai_family;
165 sin.sin_port = htons (port);
166 if (hints->ai_flags & AI_PASSIVE)
167 sin.sin_addr.s_addr = htonl (INADDR_ANY);
168 else
169 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
170 /* Duplicate result and addr and return */
171 *res = dup_addrinfo (&result, &sin, sizeof sin);
172 return (*res == NULL) ? EAI_MEMORY : 0;
173 }
174
2da53de5 175 /* If AI_NUMERIC is specified, use xinet_pton to translate numbers and
0e076fb1 176 dots notation. */
177 if (hints->ai_flags & AI_NUMERICHOST)
178 {
179 struct sockaddr_in sin;
180
181 /* check protocol family is PF_UNSPEC or PF_INET */
182 if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
183 result.ai_family = AF_INET;
184 else
185 return EAI_FAMILY;
186
187 sin.sin_family = result.ai_family;
188 sin.sin_port = htons (port);
2da53de5
HN
189 if (xinet_pton(result.ai_family, nodename, &sin.sin_addr))
190 return EAI_NONAME;
0e076fb1 191 sin.sin_addr.s_addr = inet_addr (nodename);
192 /* Duplicate result and addr and return */
193 *res = dup_addrinfo (&result, &sin, sizeof sin);
194 return (*res == NULL) ? EAI_MEMORY : 0;
195 }
196
197 h_errno = 0;
198 errno = 0;
199 hp = gethostbyname(nodename);
200 if (hp == NULL)
201 {
202#ifdef EAI_SYSTEM
203 if (errno != 0) {
204 return EAI_SYSTEM;
205 }
206#endif
207 switch (h_errno)
208 {
209 case HOST_NOT_FOUND: return EAI_NODATA;
210 case NO_DATA: return EAI_NODATA;
211#if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
212 case NO_ADDRESS: return EAI_NODATA;
213#endif
214 case NO_RECOVERY: return EAI_FAIL;
215 case TRY_AGAIN: return EAI_AGAIN;
216 default: return EAI_FAIL;
217 }
218 return EAI_FAIL;
219 }
220
221 /* Check that the address family is acceptable.
222 */
223 switch (hp->h_addrtype)
224 {
225 case AF_INET:
226 if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
227 return EAI_FAMILY;
228 break;
229#if USE_IPV6
230 case AF_INET6:
231 if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
232 return EAI_FAMILY;
233 break;
234#endif
235 default:
236 return EAI_FAMILY;
237 }
238
239 /* For each element pointed to by hp, create an element in the
240 result linked list. */
241 sai = eai = NULL;
242 for (addrs = hp->h_addr_list; *addrs != NULL; addrs++)
243 {
244 struct sockaddr sa;
245 size_t addrlen;
246
247 if (hp->h_length < 1)
248 continue;
249 sa.sa_family = hp->h_addrtype;
250 switch (hp->h_addrtype)
251 {
252 case AF_INET:
253 ((struct sockaddr_in *) &sa)->sin_port = htons (port);
254 memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
255 *addrs, hp->h_length);
256 addrlen = sizeof (struct sockaddr_in);
257 break;
258#if USE_IPV6
259 case AF_INET6:
260#if SIN6_LEN
261 ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
262#endif
263 ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
264 memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
265 *addrs, hp->h_length);
266 addrlen = sizeof (struct sockaddr_in6);
267 break;
268#endif
269 default:
270 continue;
271 }
272
273 result.ai_family = hp->h_addrtype;
274 ai = dup_addrinfo (&result, &sa, addrlen);
275 if (ai == NULL)
276 {
277 xfreeaddrinfo (sai);
278 return EAI_MEMORY;
279 }
280 if (sai == NULL)
281 sai = ai;
282 else
283 eai->ai_next = ai;
284 eai = ai;
285 }
286
287 if (sai == NULL)
288 {
289 return EAI_NODATA;
290 }
291
292 if (hints->ai_flags & AI_CANONNAME)
293 {
294 sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
295 if (sai->ai_canonname == NULL)
296 {
297 xfreeaddrinfo (sai);
298 return EAI_MEMORY;
299 }
300 strcpy (sai->ai_canonname, hp->h_name);
301 }
302
303 *res = sai;
304 return 0;
305}
306
307void
308xfreeaddrinfo (struct addrinfo *ai)
309{
310 struct addrinfo *next;
311
312 while (ai != NULL)
313 {
314 next = ai->ai_next;
315 if (ai->ai_canonname != NULL)
316 free (ai->ai_canonname);
317 if (ai->ai_addr != NULL)
318 free (ai->ai_addr);
319 free (ai);
320 ai = next;
321 }
322}
323
324const char *
325xgai_strerror (int ecode)
326{
327 static const char *eai_descr[] =
328 {
329 "no error",
330 "address family for nodename not supported", /* EAI_ADDRFAMILY */
331 "temporary failure in name resolution", /* EAI_AGAIN */
332 "invalid value for ai_flags", /* EAI_BADFLAGS */
333 "non-recoverable failure in name resolution", /* EAI_FAIL */
334 "ai_family not supported", /* EAI_FAMILY */
335 "memory allocation failure", /* EAI_MEMORY */
336 "no address associated with nodename", /* EAI_NODATA */
337 "nodename nor servname provided, or not known", /* EAI_NONAME */
338 "servname not supported for ai_socktype", /* EAI_SERVICE */
339 "ai_socktype not supported", /* EAI_SOCKTYPE */
340 "system error returned in errno", /* EAI_SYSTEM */
341 "argument buffer overflow", /* EAI_OVERFLOW */
342 };
343
344 if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
345 return "unknown error";
346 return eai_descr[ecode];
347}
348
349#endif /* HAVE_GETADDRINFO */