]> git.ipfire.org Git - thirdparty/squid.git/blame - compat/getnameinfo.c
SourceFormat Enforcement
[thirdparty/squid.git] / compat / getnameinfo.c
CommitLineData
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 120static 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 139static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
26ac0430 140 size_t, int));
0e076fb1 141static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
142#endif
143
144int
145xgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
26ac0430
AJ
146const struct sockaddr *sa;
147socklen_t salen;
148char *host;
149size_t hostlen;
150char *serv;
151size_t servlen;
152int 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
179found:
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
310numeric:
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 335static int
336ip6_parsenumeric(sa, addr, host, hostlen, flags)
26ac0430
AJ
337const struct sockaddr *sa;
338const char *addr;
339char *host;
340size_t hostlen;
341int 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 */
377static int
378ip6_sa2str(sa6, buf, bufsiz, flags)
26ac0430
AJ
379const struct sockaddr_in6 *sa6;
380char *buf;
381size_t bufsiz;
382int 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