]>
Commit | Line | Data |
---|---|---|
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 | |
85 | static struct addrinfo * | |
86 | dup_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 | ||
105 | int | |
106 | xgetaddrinfo (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 | ||
307 | void | |
308 | xfreeaddrinfo (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 | ||
324 | const char * | |
325 | xgai_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 */ |