]>
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 | * 16-Aug-2007 : Copied from fetchmail 6.3.8 | |
8 | * - added protection around libray headers | |
9 | * - added use of alternative name xgetnameinfo | |
10 | * to split from any OS-provided. | |
11 | * | |
12 | * 06-Oct-2007 : Various fixes to allow the build on MinGW | |
13 | * - use srtncpy instead of strlcpy | |
14 | * - use xinet_ntop instead of inet_ntop | |
15 | * - use SQUIDHOSTNAMELEN instead of MAXHOSTNAMELEN | |
16 | * | |
0e076fb1 | 17 | * Original License and code follows. |
18 | */ | |
f7f3304a | 19 | #include "squid.h" |
0e076fb1 | 20 | |
21 | /* KAME: getnameinfo.c,v 1.72 2005/01/13 04:12:03 itojun Exp */ | |
22 | ||
23 | /* | |
24 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
25 | * All rights reserved. | |
26 | * | |
27 | * Redistribution and use in source and binary forms, with or without | |
28 | * modification, are permitted provided that the following conditions | |
29 | * are met: | |
30 | * 1. Redistributions of source code must retain the above copyright | |
31 | * notice, this list of conditions and the following disclaimer. | |
32 | * 2. Redistributions in binary form must reproduce the above copyright | |
33 | * notice, this list of conditions and the following disclaimer in the | |
34 | * documentation and/or other materials provided with the distribution. | |
35 | * 3. Neither the name of the project nor the names of its contributors | |
36 | * may be used to endorse or promote products derived from this software | |
37 | * without specific prior written permission. | |
38 | * | |
39 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
40 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
42 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
43 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
44 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
45 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
46 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
47 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
48 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
49 | * SUCH DAMAGE. | |
50 | */ | |
51 | ||
52 | /* | |
53 | * Issues to be discussed: | |
54 | * - RFC2553 says that we should raise error on short buffer. X/Open says | |
55 | * we need to truncate the result. We obey RFC2553 (and X/Open should be | |
56 | * modified). ipngwg rough consensus seems to follow RFC2553. RFC3493 says | |
57 | * nothing about it, but defines a new error code EAI_OVERFLOW which seems | |
58 | * to be intended the code for this case. | |
59 | * - What is "local" in NI_NOFQDN? (see comments in the code) | |
60 | * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. | |
61 | * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if | |
62 | * sin6_scope_id is filled - standardization status? | |
63 | * - what should we do if we should do getservbyport("sctp")? | |
64 | */ | |
65 | ||
66 | /* | |
67 | * Considerations about thread-safeness | |
68 | * The code in this file is thread-safe, and so the thread-safeness of | |
69 | * getnameinfo() depends on the property of backend functions. | |
70 | * - getservbyport() is not thread safe for most systems we are targeting. | |
71 | * - getipnodebyaddr() is thread safe. However, many resolver libraries | |
72 | * used in the function are not thread safe. | |
73 | * - gethostbyaddr() is usually not thread safe. | |
74 | */ | |
75 | ||
32d002cb | 76 | #if !HAVE_GETNAMEINFO |
0e076fb1 | 77 | |
27bc2077 | 78 | #include "compat/getaddrinfo.h" |
602d9612 | 79 | #include "compat/inet_ntop.h" |
27bc2077 | 80 | |
0e076fb1 | 81 | #if HAVE_SYS_SOCKET_H |
82 | #include <sys/socket.h> | |
83 | #endif | |
84 | #if HAVE_NET_IF_H | |
85 | #include <net/if.h> | |
86 | #endif | |
87 | #if HAVE_NETINET_IN_H | |
88 | #include <netinet/in.h> | |
89 | #endif | |
90 | #if HAVE_ARPA_INET_H | |
91 | #include <arpa/inet.h> | |
92 | #endif | |
93 | #if HAVE_ARPA_NAMESER_H | |
94 | #include <arpa/nameser.h> | |
95 | #endif | |
96 | #if HAVE_NETDB_H | |
97 | #include <netdb.h> | |
98 | #endif | |
99 | #if HAVE_RESOLV_H | |
100 | #include <resolv.h> | |
101 | #endif | |
102 | #if HAVE_STRING_H | |
103 | #include <string.h> | |
104 | #endif | |
105 | #if HAVE_STDDEF_H | |
106 | #include <stddef.h> | |
107 | #endif | |
108 | #if HAVE_ERRNO_H | |
109 | #include <errno.h> | |
110 | #endif | |
111 | #if HAVE_INTTYPES_H | |
112 | #include <inttypes.h> | |
113 | #endif | |
114 | ||
7aa9bb3e | 115 | #if _SQUID_WINDOWS_ |
0e076fb1 | 116 | #undef IN_ADDR |
117 | #include <ws2tcpip.h> | |
118 | #endif | |
119 | ||
0e076fb1 | 120 | static const struct afd { |
26ac0430 AJ |
121 | int a_af; |
122 | int a_addrlen; | |
123 | int a_socklen; | |
124 | int a_off; | |
125 | int a_portoff; | |
0e076fb1 | 126 | } afdl [] = { |
32d002cb | 127 | #if INET6 |
26ac0430 AJ |
128 | {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), |
129 | offsetof(struct sockaddr_in6, sin6_addr), | |
130 | offsetof(struct sockaddr_in6, sin6_port)}, | |
0e076fb1 | 131 | #endif |
26ac0430 AJ |
132 | {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), |
133 | offsetof(struct sockaddr_in, sin_addr), | |
134 | offsetof(struct sockaddr_in, sin_port)}, | |
135 | {0, 0, 0, 0, 0}, | |
0e076fb1 | 136 | }; |
137 | ||
32d002cb | 138 | #if INET6 |
0e076fb1 | 139 | static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, |
26ac0430 | 140 | size_t, int)); |
0e076fb1 | 141 | static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int)); |
142 | #endif | |
143 | ||
144 | int | |
145 | xgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags) | |
26ac0430 AJ |
146 | const struct sockaddr *sa; |
147 | socklen_t salen; | |
148 | char *host; | |
149 | size_t hostlen; | |
150 | char *serv; | |
151 | size_t servlen; | |
152 | int flags; | |
0e076fb1 | 153 | { |
26ac0430 AJ |
154 | const struct afd *afd; |
155 | struct servent *sp; | |
156 | struct hostent *hp; | |
f45dd259 | 157 | unsigned short port; |
26ac0430 AJ |
158 | int family, i; |
159 | const char *addr; | |
09aabd84 | 160 | uint32_t v4a; |
26ac0430 AJ |
161 | char numserv[512]; |
162 | ||
163 | if (sa == NULL) | |
164 | return EAI_FAIL; | |
0e076fb1 | 165 | |
32d002cb | 166 | #if HAVE_SA_LEN /*XXX*/ |
26ac0430 AJ |
167 | if (sa->sa_len != salen) |
168 | return EAI_FAIL; | |
0e076fb1 | 169 | #endif |
170 | ||
26ac0430 AJ |
171 | family = sa->sa_family; |
172 | for (i = 0; afdl[i].a_af; i++) | |
173 | if (afdl[i].a_af == family) { | |
174 | afd = &afdl[i]; | |
175 | goto found; | |
176 | } | |
177 | return EAI_FAMILY; | |
178 | ||
179 | found: | |
180 | if (salen != afd->a_socklen) | |
181 | return EAI_FAIL; | |
182 | ||
183 | /* network byte order */ | |
184 | memcpy(&port, (const char *)sa + afd->a_portoff, sizeof(port)); | |
185 | addr = (const char *)sa + afd->a_off; | |
186 | ||
187 | if (serv == NULL || servlen == 0) { | |
188 | /* | |
189 | * do nothing in this case. | |
190 | * in case you are wondering if "&&" is more correct than | |
191 | * "||" here: RFC3493 says that serv == NULL OR servlen == 0 | |
192 | * means that the caller does not want the result. | |
193 | */ | |
194 | } else { | |
195 | if (flags & NI_NUMERICSERV) | |
196 | sp = NULL; | |
197 | else { | |
198 | sp = getservbyport(port, | |
199 | (flags & NI_DGRAM) ? "udp" : "tcp"); | |
200 | } | |
201 | if (sp) { | |
202 | if (strlen(sp->s_name) + 1 > servlen) | |
203 | return EAI_OVERFLOW; | |
204 | strncpy(serv, sp->s_name, servlen); | |
205 | } else { | |
206 | snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); | |
207 | if (strlen(numserv) + 1 > servlen) | |
208 | return EAI_OVERFLOW; | |
209 | strncpy(serv, numserv, servlen); | |
210 | } | |
211 | } | |
212 | ||
213 | switch (sa->sa_family) { | |
214 | case AF_INET: | |
09aabd84 | 215 | v4a = (uint32_t) |
26ac0430 AJ |
216 | ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); |
217 | if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) | |
218 | flags |= NI_NUMERICHOST; | |
219 | v4a >>= IN_CLASSA_NSHIFT; | |
220 | if (v4a == 0) | |
221 | flags |= NI_NUMERICHOST; | |
222 | break; | |
32d002cb | 223 | #if INET6 |
26ac0430 AJ |
224 | case AF_INET6: { |
225 | const struct sockaddr_in6 *sin6; | |
226 | sin6 = (const struct sockaddr_in6 *)sa; | |
227 | switch (sin6->sin6_addr.s6_addr[0]) { | |
228 | case 0x00: | |
229 | if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | |
230 | ; | |
231 | else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) | |
232 | ; | |
233 | else | |
234 | flags |= NI_NUMERICHOST; | |
235 | break; | |
236 | default: | |
237 | if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) | |
238 | flags |= NI_NUMERICHOST; | |
239 | else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) | |
240 | flags |= NI_NUMERICHOST; | |
241 | break; | |
242 | } | |
243 | } | |
244 | break; | |
0e076fb1 | 245 | #endif |
26ac0430 AJ |
246 | } |
247 | if (host == NULL || hostlen == 0) { | |
248 | /* | |
249 | * do nothing in this case. | |
250 | * in case you are wondering if "&&" is more correct than | |
251 | * "||" here: RFC3493 says that host == NULL or hostlen == 0 | |
252 | * means that the caller does not want the result. | |
253 | */ | |
254 | } else if (flags & NI_NUMERICHOST) { | |
255 | /* NUMERICHOST and NAMEREQD conflicts with each other */ | |
256 | if (flags & NI_NAMEREQD) | |
257 | return EAI_NONAME; | |
258 | ||
259 | goto numeric; | |
260 | } else { | |
32d002cb | 261 | #if USE_GETIPNODEBY |
5b10eaba | 262 | int h_error = 0; |
26ac0430 | 263 | hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); |
0e076fb1 | 264 | #else |
26ac0430 | 265 | hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); |
5b10eaba | 266 | #if 0 // getnameinfo.c:161:9: error: variable 'h_error' set but not used |
32d002cb | 267 | #if HAVE_H_ERRNO |
26ac0430 | 268 | h_error = h_errno; |
0e076fb1 | 269 | #else |
26ac0430 | 270 | h_error = EINVAL; |
0e076fb1 | 271 | #endif |
5b10eaba | 272 | #endif /* 0 */ |
0e076fb1 | 273 | #endif |
274 | ||
26ac0430 | 275 | if (hp) { |
0e076fb1 | 276 | #if 0 |
26ac0430 AJ |
277 | if (flags & NI_NOFQDN) { |
278 | /* | |
279 | * According to RFC3493 section 6.2, NI_NOFQDN | |
280 | * means "node name portion of the FQDN shall | |
281 | * be returned for local hosts." The following | |
282 | * code tries to implement it by returning the | |
283 | * first label (the part before the first | |
284 | * period) of the FQDN. However, it is not | |
285 | * clear if this always makes sense, since the | |
286 | * given address may be outside of "local | |
287 | * hosts." Due to the unclear description, we | |
288 | * disable the code in this implementation. | |
289 | */ | |
290 | char *p; | |
291 | p = strchr(hp->h_name, '.'); | |
292 | if (p) | |
293 | *p = '\0'; | |
294 | } | |
0e076fb1 | 295 | #endif |
26ac0430 | 296 | if (strlen(hp->h_name) + 1 > hostlen) { |
32d002cb | 297 | #if USE_GETIPNODEBY |
26ac0430 | 298 | freehostent(hp); |
0e076fb1 | 299 | #endif |
26ac0430 AJ |
300 | return EAI_OVERFLOW; |
301 | } | |
302 | strncpy(host, hp->h_name, hostlen); | |
32d002cb | 303 | #if USE_GETIPNODEBY |
26ac0430 | 304 | freehostent(hp); |
0e076fb1 | 305 | #endif |
26ac0430 AJ |
306 | } else { |
307 | if (flags & NI_NAMEREQD) | |
308 | return EAI_NONAME; | |
0e076fb1 | 309 | |
26ac0430 AJ |
310 | numeric: |
311 | switch (afd->a_af) { | |
32d002cb | 312 | #if INET6 |
26ac0430 AJ |
313 | case AF_INET6: { |
314 | int error; | |
315 | ||
316 | if ((error = ip6_parsenumeric(sa, addr, host, | |
317 | hostlen, | |
318 | flags)) != 0) | |
319 | return(error); | |
320 | break; | |
321 | } | |
0e076fb1 | 322 | #endif |
26ac0430 | 323 | default: |
27bc2077 | 324 | if (inet_ntop(afd->a_af, addr, host, |
b1c8a478 | 325 | hostlen) == NULL) |
26ac0430 AJ |
326 | return EAI_SYSTEM; |
327 | break; | |
328 | } | |
329 | } | |
330 | } | |
331 | return(0); | |
0e076fb1 | 332 | } |
333 | ||
32d002cb | 334 | #if INET6 |
0e076fb1 | 335 | static int |
336 | ip6_parsenumeric(sa, addr, host, hostlen, flags) | |
26ac0430 AJ |
337 | const struct sockaddr *sa; |
338 | const char *addr; | |
339 | char *host; | |
340 | size_t hostlen; | |
341 | int flags; | |
0e076fb1 | 342 | { |
26ac0430 AJ |
343 | int numaddrlen; |
344 | char numaddr[512]; | |
345 | ||
27bc2077 | 346 | if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) |
26ac0430 AJ |
347 | return EAI_SYSTEM; |
348 | ||
349 | numaddrlen = strlen(numaddr); | |
350 | if (numaddrlen + 1 > hostlen) /* don't forget terminator */ | |
351 | return EAI_OVERFLOW; | |
352 | strncpy(host, numaddr, hostlen); | |
353 | ||
354 | if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { | |
355 | char zonebuf[SQUIDHOSTNAMELEN]; | |
356 | int zonelen; | |
357 | ||
358 | zonelen = ip6_sa2str( | |
359 | (const struct sockaddr_in6 *)(const void *)sa, | |
360 | zonebuf, sizeof(zonebuf), flags); | |
361 | if (zonelen < 0) | |
362 | return EAI_OVERFLOW; | |
363 | if (zonelen + 1 + numaddrlen + 1 > hostlen) | |
364 | return EAI_OVERFLOW; | |
365 | ||
366 | /* construct <numeric-addr><delim><zoneid> */ | |
367 | memcpy(host + numaddrlen + 1, zonebuf, | |
368 | (size_t)zonelen); | |
369 | host[numaddrlen] = SCOPE_DELIMITER; | |
370 | host[numaddrlen + 1 + zonelen] = '\0'; | |
371 | } | |
372 | ||
373 | return 0; | |
0e076fb1 | 374 | } |
375 | ||
376 | /* ARGSUSED */ | |
377 | static int | |
378 | ip6_sa2str(sa6, buf, bufsiz, flags) | |
26ac0430 AJ |
379 | const struct sockaddr_in6 *sa6; |
380 | char *buf; | |
381 | size_t bufsiz; | |
382 | int flags; | |
0e076fb1 | 383 | { |
26ac0430 AJ |
384 | unsigned int ifindex; |
385 | const struct in6_addr *a6; | |
386 | int n; | |
0e076fb1 | 387 | |
26ac0430 AJ |
388 | ifindex = (unsigned int)sa6->sin6_scope_id; |
389 | a6 = &sa6->sin6_addr; | |
0e076fb1 | 390 | |
32d002cb | 391 | #if NI_NUMERICSCOPE |
26ac0430 AJ |
392 | if ((flags & NI_NUMERICSCOPE) != 0) { |
393 | n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); | |
394 | if (n < 0 || n >= bufsiz) | |
395 | return -1; | |
396 | else | |
397 | return n; | |
398 | } | |
0e076fb1 | 399 | #endif |
400 | ||
26ac0430 AJ |
401 | /* if_indextoname() does not take buffer size. not a good api... */ |
402 | if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || | |
403 | IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) { | |
404 | char *p = if_indextoname(ifindex, buf); | |
405 | if (p) | |
406 | return (strlen(p)); | |
407 | } | |
408 | ||
409 | /* last resort */ | |
410 | n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); | |
411 | if (n < 0 || n >= bufsiz) | |
412 | return -1; | |
413 | else | |
414 | return n; | |
0e076fb1 | 415 | } |
416 | #endif /* INET6 */ | |
417 | #endif |