]> git.ipfire.org Git - thirdparty/squid.git/blob - compat/getnameinfo.c
SourceFormat Enforcement
[thirdparty/squid.git] / compat / getnameinfo.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 * 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 *
17 * Original License and code follows.
18 */
19 #include "squid.h"
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
76 #if !HAVE_GETNAMEINFO
77
78 #include "compat/getaddrinfo.h"
79 #include "compat/inet_ntop.h"
80
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
115 #if _SQUID_WINDOWS_
116 #undef IN_ADDR
117 #include <ws2tcpip.h>
118 #endif
119
120 static const struct afd {
121 int a_af;
122 int a_addrlen;
123 int a_socklen;
124 int a_off;
125 int a_portoff;
126 } afdl [] = {
127 #if INET6
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)},
131 #endif
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},
136 };
137
138 #if INET6
139 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
140 size_t, int));
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)
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;
153 {
154 const struct afd *afd;
155 struct servent *sp;
156 struct hostent *hp;
157 unsigned short port;
158 int family, i;
159 const char *addr;
160 uint32_t v4a;
161 char numserv[512];
162
163 if (sa == NULL)
164 return EAI_FAIL;
165
166 #if HAVE_SA_LEN /*XXX*/
167 if (sa->sa_len != salen)
168 return EAI_FAIL;
169 #endif
170
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:
215 v4a = (uint32_t)
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;
223 #if INET6
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;
245 #endif
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 {
261 #if USE_GETIPNODEBY
262 int h_error = 0;
263 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
264 #else
265 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
266 #if 0 // getnameinfo.c:161:9: error: variable 'h_error' set but not used
267 #if HAVE_H_ERRNO
268 h_error = h_errno;
269 #else
270 h_error = EINVAL;
271 #endif
272 #endif /* 0 */
273 #endif
274
275 if (hp) {
276 #if 0
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 }
295 #endif
296 if (strlen(hp->h_name) + 1 > hostlen) {
297 #if USE_GETIPNODEBY
298 freehostent(hp);
299 #endif
300 return EAI_OVERFLOW;
301 }
302 strncpy(host, hp->h_name, hostlen);
303 #if USE_GETIPNODEBY
304 freehostent(hp);
305 #endif
306 } else {
307 if (flags & NI_NAMEREQD)
308 return EAI_NONAME;
309
310 numeric:
311 switch (afd->a_af) {
312 #if INET6
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 }
322 #endif
323 default:
324 if (inet_ntop(afd->a_af, addr, host,
325 hostlen) == NULL)
326 return EAI_SYSTEM;
327 break;
328 }
329 }
330 }
331 return(0);
332 }
333
334 #if INET6
335 static int
336 ip6_parsenumeric(sa, addr, host, hostlen, flags)
337 const struct sockaddr *sa;
338 const char *addr;
339 char *host;
340 size_t hostlen;
341 int flags;
342 {
343 int numaddrlen;
344 char numaddr[512];
345
346 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
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;
374 }
375
376 /* ARGSUSED */
377 static int
378 ip6_sa2str(sa6, buf, bufsiz, flags)
379 const struct sockaddr_in6 *sa6;
380 char *buf;
381 size_t bufsiz;
382 int flags;
383 {
384 unsigned int ifindex;
385 const struct in6_addr *a6;
386 int n;
387
388 ifindex = (unsigned int)sa6->sin6_scope_id;
389 a6 = &sa6->sin6_addr;
390
391 #if NI_NUMERICSCOPE
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 }
399 #endif
400
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;
415 }
416 #endif /* INET6 */
417 #endif