]> git.ipfire.org Git - thirdparty/squid.git/blob - lib/getaddrinfo.c
becb57b0e8d6ccd58e68745039d3576fa6c5e9a6
[thirdparty/squid.git] / lib / getaddrinfo.c
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$
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"
83 #include "inet_pton.h"
84
85 static struct addrinfo *
86 dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen) {
87 struct addrinfo *ret;
88
89 ret = malloc (sizeof (struct addrinfo));
90 if (ret == NULL)
91 return NULL;
92 memcpy (ret, info, sizeof (struct addrinfo));
93 ret->ai_addr = malloc (addrlen);
94 if (ret->ai_addr == NULL) {
95 free (ret);
96 return NULL;
97 }
98 memcpy (ret->ai_addr, addr, addrlen);
99 ret->ai_addrlen = addrlen;
100 return ret;
101 }
102
103 int
104 xgetaddrinfo (const char *nodename, const char *servname,
105 const struct addrinfo *hints, struct addrinfo **res)
106 {
107 struct hostent *hp;
108 struct servent *servent;
109 const char *socktype;
110 int port;
111 struct addrinfo hint, result;
112 struct addrinfo *ai, *sai, *eai;
113 char **addrs;
114
115 if (servname == NULL && nodename == NULL)
116 return EAI_NONAME;
117
118 memset (&result, 0, sizeof result);
119
120 /* default for hints */
121 if (hints == NULL) {
122 memset (&hint, 0, sizeof hint);
123 hint.ai_family = PF_UNSPEC;
124 hints = &hint;
125 }
126
127 if (servname == NULL)
128 port = 0;
129 else {
130 /* check for tcp or udp sockets only */
131 if (hints->ai_socktype == SOCK_STREAM)
132 socktype = "tcp";
133 else if (hints->ai_socktype == SOCK_DGRAM)
134 socktype = "udp";
135 else
136 return EAI_SERVICE;
137 result.ai_socktype = hints->ai_socktype;
138
139 /* Note: maintain port in host byte order to make debugging easier */
140 if (isdigit (*servname))
141 port = strtol (servname, NULL, 10);
142 else if ((servent = getservbyname (servname, socktype)) != NULL)
143 port = ntohs (servent->s_port);
144 else
145 return EAI_NONAME;
146 }
147
148 /* if nodename == NULL refer to the local host for a client or any
149 for a server */
150 if (nodename == NULL) {
151 struct sockaddr_in sin;
152
153 /* check protocol family is PF_UNSPEC or PF_INET - could try harder
154 for IPv6 but that's more code than I'm prepared to write */
155 if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
156 result.ai_family = AF_INET;
157 else
158 return EAI_FAMILY;
159
160 sin.sin_family = result.ai_family;
161 sin.sin_port = htons (port);
162 if (hints->ai_flags & AI_PASSIVE)
163 sin.sin_addr.s_addr = htonl (INADDR_ANY);
164 else
165 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
166 /* Duplicate result and addr and return */
167 *res = dup_addrinfo (&result, &sin, sizeof sin);
168 return (*res == NULL) ? EAI_MEMORY : 0;
169 }
170
171 /* If AI_NUMERIC is specified, use xinet_pton to translate numbers and
172 dots notation. */
173 if (hints->ai_flags & AI_NUMERICHOST) {
174 struct sockaddr_in sin;
175
176 /* check protocol family is PF_UNSPEC or PF_INET */
177 if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
178 result.ai_family = AF_INET;
179 else
180 return EAI_FAMILY;
181
182 sin.sin_family = result.ai_family;
183 sin.sin_port = htons (port);
184 if (xinet_pton(result.ai_family, nodename, &sin.sin_addr))
185 return EAI_NONAME;
186 sin.sin_addr.s_addr = inet_addr (nodename);
187 /* Duplicate result and addr and return */
188 *res = dup_addrinfo (&result, &sin, sizeof sin);
189 return (*res == NULL) ? EAI_MEMORY : 0;
190 }
191
192 h_errno = 0;
193 errno = 0;
194 hp = gethostbyname(nodename);
195 if (hp == NULL) {
196 #ifdef EAI_SYSTEM
197 if (errno != 0) {
198 return EAI_SYSTEM;
199 }
200 #endif
201 switch (h_errno) {
202 case HOST_NOT_FOUND:
203 return EAI_NODATA;
204 case NO_DATA:
205 return EAI_NODATA;
206 #if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
207 case NO_ADDRESS:
208 return EAI_NODATA;
209 #endif
210 case NO_RECOVERY:
211 return EAI_FAIL;
212 case TRY_AGAIN:
213 return EAI_AGAIN;
214 default:
215 return EAI_FAIL;
216 }
217 return EAI_FAIL;
218 }
219
220 /* Check that the address family is acceptable.
221 */
222 switch (hp->h_addrtype) {
223 case AF_INET:
224 if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
225 return EAI_FAMILY;
226 break;
227 #if USE_IPV6
228 case AF_INET6:
229 if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
230 return EAI_FAMILY;
231 break;
232 #endif
233 default:
234 return EAI_FAMILY;
235 }
236
237 /* For each element pointed to by hp, create an element in the
238 result linked list. */
239 sai = eai = NULL;
240 for (addrs = hp->h_addr_list; *addrs != NULL; addrs++) {
241 struct sockaddr sa;
242 size_t addrlen;
243
244 if (hp->h_length < 1)
245 continue;
246 sa.sa_family = hp->h_addrtype;
247 switch (hp->h_addrtype) {
248 case AF_INET:
249 ((struct sockaddr_in *) &sa)->sin_port = htons (port);
250 memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
251 *addrs, hp->h_length);
252 addrlen = sizeof (struct sockaddr_in);
253 break;
254 #if USE_IPV6
255 case AF_INET6:
256 #if SIN6_LEN
257 ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
258 #endif
259 ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
260 memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
261 *addrs, hp->h_length);
262 addrlen = sizeof (struct sockaddr_in6);
263 break;
264 #endif
265 default:
266 continue;
267 }
268
269 result.ai_family = hp->h_addrtype;
270 ai = dup_addrinfo (&result, &sa, addrlen);
271 if (ai == NULL) {
272 xfreeaddrinfo (sai);
273 return EAI_MEMORY;
274 }
275 if (sai == NULL)
276 sai = ai;
277 else
278 eai->ai_next = ai;
279 eai = ai;
280 }
281
282 if (sai == NULL) {
283 return EAI_NODATA;
284 }
285
286 if (hints->ai_flags & AI_CANONNAME) {
287 sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
288 if (sai->ai_canonname == NULL) {
289 xfreeaddrinfo (sai);
290 return EAI_MEMORY;
291 }
292 strcpy (sai->ai_canonname, hp->h_name);
293 }
294
295 *res = sai;
296 return 0;
297 }
298
299 void
300 xfreeaddrinfo (struct addrinfo *ai)
301 {
302 struct addrinfo *next;
303
304 while (ai != NULL) {
305 next = ai->ai_next;
306 if (ai->ai_canonname != NULL)
307 free (ai->ai_canonname);
308 if (ai->ai_addr != NULL)
309 free (ai->ai_addr);
310 free (ai);
311 ai = next;
312 }
313 }
314
315 const char *
316 xgai_strerror (int ecode)
317 {
318 static const char *eai_descr[] = {
319 "no error",
320 "address family for nodename not supported", /* EAI_ADDRFAMILY */
321 "temporary failure in name resolution", /* EAI_AGAIN */
322 "invalid value for ai_flags", /* EAI_BADFLAGS */
323 "non-recoverable failure in name resolution", /* EAI_FAIL */
324 "ai_family not supported", /* EAI_FAMILY */
325 "memory allocation failure", /* EAI_MEMORY */
326 "no address associated with nodename", /* EAI_NODATA */
327 "nodename nor servname provided, or not known", /* EAI_NONAME */
328 "servname not supported for ai_socktype", /* EAI_SERVICE */
329 "ai_socktype not supported", /* EAI_SOCKTYPE */
330 "system error returned in errno", /* EAI_SYSTEM */
331 "argument buffer overflow", /* EAI_OVERFLOW */
332 };
333
334 if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
335 return "unknown error";
336 return eai_descr[ecode];
337 }
338
339 #endif /* HAVE_GETADDRINFO */