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