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