1 /*-------------------------------------------------------------------------
4 * IPv6-aware network access.
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 * This file and the IPV6 implementation were initially provided by
14 * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
15 * http://www.lbsd.net.
17 *-------------------------------------------------------------------------
20 /* This is intended to be used in both frontend and backend, so use c.h */
24 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #ifdef HAVE_NETINET_TCP_H
30 #include <netinet/tcp.h>
32 #include <arpa/inet.h>
38 static int range_sockaddr_AF_INET(const struct sockaddr_in
* addr
,
39 const struct sockaddr_in
* netaddr
,
40 const struct sockaddr_in
* netmask
);
43 static int range_sockaddr_AF_INET6(const struct sockaddr_in6
* addr
,
44 const struct sockaddr_in6
* netaddr
,
45 const struct sockaddr_in6
* netmask
);
48 #ifdef HAVE_UNIX_SOCKETS
49 static int getaddrinfo_unix(const char *path
,
50 const struct addrinfo
* hintsp
,
51 struct addrinfo
** result
);
53 static int getnameinfo_unix(const struct sockaddr_un
* sa
, int salen
,
54 char *node
, int nodelen
,
55 char *service
, int servicelen
,
61 * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
64 pg_getaddrinfo_all(const char *hostname
, const char *servname
,
65 const struct addrinfo
* hintp
, struct addrinfo
** result
)
69 /* not all versions of getaddrinfo() zero *result on failure */
72 #ifdef HAVE_UNIX_SOCKETS
73 if (hintp
->ai_family
== AF_UNIX
)
74 return getaddrinfo_unix(servname
, hintp
, result
);
77 /* NULL has special meaning to getaddrinfo(). */
78 rc
= getaddrinfo((!hostname
|| hostname
[0] == '\0') ? NULL
: hostname
,
79 servname
, hintp
, result
);
86 * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
88 * Note: the ai_family field of the original hint structure must be passed
89 * so that we can tell whether the addrinfo struct was built by the system's
90 * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
91 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
92 * not safe to look at ai_family in the addrinfo itself.
95 pg_freeaddrinfo_all(int hint_ai_family
, struct addrinfo
* ai
)
97 #ifdef HAVE_UNIX_SOCKETS
98 if (hint_ai_family
== AF_UNIX
)
100 /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
103 struct addrinfo
*p
= ai
;
111 #endif /* HAVE_UNIX_SOCKETS */
113 /* struct was built by getaddrinfo() */
121 * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
123 * The API of this routine differs from the standard getnameinfo() definition
124 * in two ways: first, the addr parameter is declared as sockaddr_storage
125 * rather than struct sockaddr, and second, the node and service fields are
126 * guaranteed to be filled with something even on failure return.
129 pg_getnameinfo_all(const struct sockaddr_storage
* addr
, int salen
,
130 char *node
, int nodelen
,
131 char *service
, int servicelen
,
136 #ifdef HAVE_UNIX_SOCKETS
137 if (addr
&& addr
->ss_family
== AF_UNIX
)
138 rc
= getnameinfo_unix((const struct sockaddr_un
*) addr
, salen
,
144 rc
= getnameinfo((const struct sockaddr
*) addr
, salen
,
152 strlcpy(node
, "???", nodelen
);
154 strlcpy(service
, "???", servicelen
);
161 #if defined(HAVE_UNIX_SOCKETS)
164 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
166 * Bugs: only one addrinfo is set even though hintsp is NULL or
168 * AI_CANONNAME is not supported.
172 getaddrinfo_unix(const char *path
, const struct addrinfo
* hintsp
,
173 struct addrinfo
** result
)
175 struct addrinfo hints
;
176 struct addrinfo
*aip
;
177 struct sockaddr_un
*unp
;
181 MemSet(&hints
, 0, sizeof(hints
));
183 if (strlen(path
) >= sizeof(unp
->sun_path
))
188 hints
.ai_family
= AF_UNIX
;
189 hints
.ai_socktype
= SOCK_STREAM
;
192 memcpy(&hints
, hintsp
, sizeof(hints
));
194 if (hints
.ai_socktype
== 0)
195 hints
.ai_socktype
= SOCK_STREAM
;
197 if (hints
.ai_family
!= AF_UNIX
)
199 /* shouldn't have been called */
203 aip
= calloc(1, sizeof(struct addrinfo
));
207 unp
= calloc(1, sizeof(struct sockaddr_un
));
214 aip
->ai_family
= AF_UNIX
;
215 aip
->ai_socktype
= hints
.ai_socktype
;
216 aip
->ai_protocol
= hints
.ai_protocol
;
218 aip
->ai_canonname
= NULL
;
221 unp
->sun_family
= AF_UNIX
;
222 aip
->ai_addr
= (struct sockaddr
*) unp
;
223 aip
->ai_addrlen
= sizeof(struct sockaddr_un
);
225 strcpy(unp
->sun_path
, path
);
227 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
228 unp
->sun_len
= sizeof(struct sockaddr_un
);
235 * Convert an address to a hostname.
238 getnameinfo_unix(const struct sockaddr_un
* sa
, int salen
,
239 char *node
, int nodelen
,
240 char *service
, int servicelen
,
245 /* Invalid arguments. */
246 if (sa
== NULL
|| sa
->sun_family
!= AF_UNIX
||
247 (node
== NULL
&& service
== NULL
))
250 /* We don't support those. */
251 if ((node
&& !(flags
& NI_NUMERICHOST
))
252 || (service
&& !(flags
& NI_NUMERICSERV
)))
257 ret
= snprintf(node
, nodelen
, "%s", "[local]");
258 if (ret
== -1 || ret
> nodelen
)
264 ret
= snprintf(service
, servicelen
, "%s", sa
->sun_path
);
265 if (ret
== -1 || ret
> servicelen
)
271 #endif /* HAVE_UNIX_SOCKETS */
275 * pg_range_sockaddr - is addr within the subnet specified by netaddr/netmask ?
277 * Note: caller must already have verified that all three addresses are
278 * in the same address family; and AF_UNIX addresses are not supported.
281 pg_range_sockaddr(const struct sockaddr_storage
* addr
,
282 const struct sockaddr_storage
* netaddr
,
283 const struct sockaddr_storage
* netmask
)
285 if (addr
->ss_family
== AF_INET
)
286 return range_sockaddr_AF_INET((struct sockaddr_in
*) addr
,
287 (struct sockaddr_in
*) netaddr
,
288 (struct sockaddr_in
*) netmask
);
290 else if (addr
->ss_family
== AF_INET6
)
291 return range_sockaddr_AF_INET6((struct sockaddr_in6
*) addr
,
292 (struct sockaddr_in6
*) netaddr
,
293 (struct sockaddr_in6
*) netmask
);
300 range_sockaddr_AF_INET(const struct sockaddr_in
* addr
,
301 const struct sockaddr_in
* netaddr
,
302 const struct sockaddr_in
* netmask
)
304 if (((addr
->sin_addr
.s_addr
^ netaddr
->sin_addr
.s_addr
) &
305 netmask
->sin_addr
.s_addr
) == 0)
315 range_sockaddr_AF_INET6(const struct sockaddr_in6
* addr
,
316 const struct sockaddr_in6
* netaddr
,
317 const struct sockaddr_in6
* netmask
)
321 for (i
= 0; i
< 16; i
++)
323 if (((addr
->sin6_addr
.s6_addr
[i
] ^ netaddr
->sin6_addr
.s6_addr
[i
]) &
324 netmask
->sin6_addr
.s6_addr
[i
]) != 0)
330 #endif /* HAVE_IPV6 */
333 * pg_sockaddr_cidr_mask - make a network mask of the appropriate family
334 * and required number of significant bits
336 * The resulting mask is placed in *mask, which had better be big enough.
338 * Return value is 0 if okay, -1 if not.
341 pg_sockaddr_cidr_mask(struct sockaddr_storage
* mask
, char *numbits
, int family
)
346 bits
= strtol(numbits
, &endptr
, 10);
348 if (*numbits
== '\0' || *endptr
!= '\0')
355 struct sockaddr_in mask4
;
358 if (bits
< 0 || bits
> 32)
360 memset(&mask4
, 0, sizeof(mask4
));
361 /* avoid "x << 32", which is not portable */
363 maskl
= (0xffffffffUL
<< (32 - (int) bits
))
367 mask4
.sin_addr
.s_addr
= htonl(maskl
);
368 memcpy(mask
, &mask4
, sizeof(mask4
));
375 struct sockaddr_in6 mask6
;
378 if (bits
< 0 || bits
> 128)
380 memset(&mask6
, 0, sizeof(mask6
));
381 for (i
= 0; i
< 16; i
++)
384 mask6
.sin6_addr
.s6_addr
[i
] = 0;
386 mask6
.sin6_addr
.s6_addr
[i
] = 0xff;
389 mask6
.sin6_addr
.s6_addr
[i
] =
390 (0xff << (8 - (int) bits
)) & 0xff;
394 memcpy(mask
, &mask6
, sizeof(mask6
));
402 mask
->ss_family
= family
;
410 * pg_promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
411 * the standard convention for IPv4 addresses mapped into IPv6 world
413 * The passed addr is modified in place; be sure it is large enough to
414 * hold the result! Note that we only worry about setting the fields
415 * that pg_range_sockaddr will look at.
418 pg_promote_v4_to_v6_addr(struct sockaddr_storage
* addr
)
420 struct sockaddr_in addr4
;
421 struct sockaddr_in6 addr6
;
424 memcpy(&addr4
, addr
, sizeof(addr4
));
425 ip4addr
= ntohl(addr4
.sin_addr
.s_addr
);
427 memset(&addr6
, 0, sizeof(addr6
));
429 addr6
.sin6_family
= AF_INET6
;
431 addr6
.sin6_addr
.s6_addr
[10] = 0xff;
432 addr6
.sin6_addr
.s6_addr
[11] = 0xff;
433 addr6
.sin6_addr
.s6_addr
[12] = (ip4addr
>> 24) & 0xFF;
434 addr6
.sin6_addr
.s6_addr
[13] = (ip4addr
>> 16) & 0xFF;
435 addr6
.sin6_addr
.s6_addr
[14] = (ip4addr
>> 8) & 0xFF;
436 addr6
.sin6_addr
.s6_addr
[15] = (ip4addr
) & 0xFF;
438 memcpy(addr
, &addr6
, sizeof(addr6
));
442 * pg_promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using
443 * the standard convention for IPv4 addresses mapped into IPv6 world
445 * This must be different from pg_promote_v4_to_v6_addr because we want to
446 * set the high-order bits to 1's not 0's.
448 * The passed addr is modified in place; be sure it is large enough to
449 * hold the result! Note that we only worry about setting the fields
450 * that pg_range_sockaddr will look at.
453 pg_promote_v4_to_v6_mask(struct sockaddr_storage
* addr
)
455 struct sockaddr_in addr4
;
456 struct sockaddr_in6 addr6
;
460 memcpy(&addr4
, addr
, sizeof(addr4
));
461 ip4addr
= ntohl(addr4
.sin_addr
.s_addr
);
463 memset(&addr6
, 0, sizeof(addr6
));
465 addr6
.sin6_family
= AF_INET6
;
467 for (i
= 0; i
< 12; i
++)
468 addr6
.sin6_addr
.s6_addr
[i
] = 0xff;
470 addr6
.sin6_addr
.s6_addr
[12] = (ip4addr
>> 24) & 0xFF;
471 addr6
.sin6_addr
.s6_addr
[13] = (ip4addr
>> 16) & 0xFF;
472 addr6
.sin6_addr
.s6_addr
[14] = (ip4addr
>> 8) & 0xFF;
473 addr6
.sin6_addr
.s6_addr
[15] = (ip4addr
) & 0xFF;
475 memcpy(addr
, &addr6
, sizeof(addr6
));
478 #endif /* HAVE_IPV6 */