1 /*-------------------------------------------------------------------------
4 * IPv6-aware network access.
6 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/libpq/ip.c,v 1.36 2006/06/20 19:56:52 tgl Exp $
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)
67 /* not all versions of getaddrinfo() zero *result on failure */
70 #ifdef HAVE_UNIX_SOCKETS
71 if (hintp->ai_family == AF_UNIX)
72 return getaddrinfo_unix(servname, hintp, result);
75 /* NULL has special meaning to getaddrinfo(). */
76 return getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
77 servname, hintp, result);
82 * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
84 * Note: the ai_family field of the original hint structure must be passed
85 * so that we can tell whether the addrinfo struct was built by the system's
86 * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
87 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
88 * not safe to look at ai_family in the addrinfo itself.
91 pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai)
93 #ifdef HAVE_UNIX_SOCKETS
94 if (hint_ai_family == AF_UNIX)
96 /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
99 struct addrinfo *p = ai;
107 #endif /* HAVE_UNIX_SOCKETS */
109 /* struct was built by getaddrinfo() */
117 * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
119 * The API of this routine differs from the standard getnameinfo() definition
120 * in two ways: first, the addr parameter is declared as sockaddr_storage
121 * rather than struct sockaddr, and second, the node and service fields are
122 * guaranteed to be filled with something even on failure return.
125 pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen,
126 char *node, int nodelen,
127 char *service, int servicelen,
132 #ifdef HAVE_UNIX_SOCKETS
133 if (addr && addr->ss_family == AF_UNIX)
134 rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
140 rc = getnameinfo((const struct sockaddr *) addr, salen,
148 StrNCpy(node, "???", nodelen);
150 StrNCpy(service, "???", servicelen);
157 #if defined(HAVE_UNIX_SOCKETS)
160 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
162 * Bugs: only one addrinfo is set even though hintsp is NULL or
164 * AI_CANONNAME is not supported.
168 getaddrinfo_unix(const char *path, const struct addrinfo * hintsp,
169 struct addrinfo ** result)
171 struct addrinfo hints;
172 struct addrinfo *aip;
173 struct sockaddr_un *unp;
177 MemSet(&hints, 0, sizeof(hints));
179 if (strlen(path) >= sizeof(unp->sun_path))
184 hints.ai_family = AF_UNIX;
185 hints.ai_socktype = SOCK_STREAM;
188 memcpy(&hints, hintsp, sizeof(hints));
190 if (hints.ai_socktype == 0)
191 hints.ai_socktype = SOCK_STREAM;
193 if (hints.ai_family != AF_UNIX)
195 /* shouldn't have been called */
199 aip = calloc(1, sizeof(struct addrinfo));
203 unp = calloc(1, sizeof(struct sockaddr_un));
210 aip->ai_family = AF_UNIX;
211 aip->ai_socktype = hints.ai_socktype;
212 aip->ai_protocol = hints.ai_protocol;
214 aip->ai_canonname = NULL;
217 unp->sun_family = AF_UNIX;
218 aip->ai_addr = (struct sockaddr *) unp;
219 aip->ai_addrlen = sizeof(struct sockaddr_un);
221 strcpy(unp->sun_path, path);
223 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
224 unp->sun_len = sizeof(struct sockaddr_un);
231 * Convert an address to a hostname.
234 getnameinfo_unix(const struct sockaddr_un * sa, int salen,
235 char *node, int nodelen,
236 char *service, int servicelen,
241 /* Invalid arguments. */
242 if (sa == NULL || sa->sun_family != AF_UNIX ||
243 (node == NULL && service == NULL))
246 /* We don't support those. */
247 if ((node && !(flags & NI_NUMERICHOST))
248 || (service && !(flags & NI_NUMERICSERV)))
253 ret = snprintf(node, nodelen, "%s", "[local]");
254 if (ret == -1 || ret > nodelen)
260 ret = snprintf(service, servicelen, "%s", sa->sun_path);
261 if (ret == -1 || ret > servicelen)
267 #endif /* HAVE_UNIX_SOCKETS */
271 * pg_range_sockaddr - is addr within the subnet specified by netaddr/netmask ?
273 * Note: caller must already have verified that all three addresses are
274 * in the same address family; and AF_UNIX addresses are not supported.
277 pg_range_sockaddr(const struct sockaddr_storage * addr,
278 const struct sockaddr_storage * netaddr,
279 const struct sockaddr_storage * netmask)
281 if (addr->ss_family == AF_INET)
282 return range_sockaddr_AF_INET((struct sockaddr_in *) addr,
283 (struct sockaddr_in *) netaddr,
284 (struct sockaddr_in *) netmask);
286 else if (addr->ss_family == AF_INET6)
287 return range_sockaddr_AF_INET6((struct sockaddr_in6 *) addr,
288 (struct sockaddr_in6 *) netaddr,
289 (struct sockaddr_in6 *) netmask);
296 range_sockaddr_AF_INET(const struct sockaddr_in * addr,
297 const struct sockaddr_in * netaddr,
298 const struct sockaddr_in * netmask)
300 if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
301 netmask->sin_addr.s_addr) == 0)
311 range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr,
312 const struct sockaddr_in6 * netaddr,
313 const struct sockaddr_in6 * netmask)
317 for (i = 0; i < 16; i++)
319 if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
320 netmask->sin6_addr.s6_addr[i]) != 0)
326 #endif /* HAVE_IPV6 */
329 * pg_sockaddr_cidr_mask - make a network mask of the appropriate family
330 * and required number of significant bits
332 * The resulting mask is placed in *mask, which had better be big enough.
334 * Return value is 0 if okay, -1 if not.
337 pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
342 bits = strtol(numbits, &endptr, 10);
344 if (*numbits == '\0' || *endptr != '\0')
351 struct sockaddr_in mask4;
354 if (bits < 0 || bits > 32)
356 /* avoid "x << 32", which is not portable */
358 maskl = (0xffffffffUL << (32 - (int) bits))
362 mask4.sin_addr.s_addr = htonl(maskl);
363 memcpy(mask, &mask4, sizeof(mask4));
370 struct sockaddr_in6 mask6;
373 if (bits < 0 || bits > 128)
375 for (i = 0; i < 16; i++)
378 mask6.sin6_addr.s6_addr[i] = 0;
380 mask6.sin6_addr.s6_addr[i] = 0xff;
383 mask6.sin6_addr.s6_addr[i] =
384 (0xff << (8 - (int) bits)) & 0xff;
388 memcpy(mask, &mask6, sizeof(mask6));
396 mask->ss_family = family;
404 * pg_promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
405 * the standard convention for IPv4 addresses mapped into IPv6 world
407 * The passed addr is modified in place; be sure it is large enough to
408 * hold the result! Note that we only worry about setting the fields
409 * that pg_range_sockaddr will look at.
412 pg_promote_v4_to_v6_addr(struct sockaddr_storage * addr)
414 struct sockaddr_in addr4;
415 struct sockaddr_in6 addr6;
418 memcpy(&addr4, addr, sizeof(addr4));
419 ip4addr = ntohl(addr4.sin_addr.s_addr);
421 memset(&addr6, 0, sizeof(addr6));
423 addr6.sin6_family = AF_INET6;
425 addr6.sin6_addr.s6_addr[10] = 0xff;
426 addr6.sin6_addr.s6_addr[11] = 0xff;
427 addr6.sin6_addr.s6_addr[12] = (ip4addr >> 24) & 0xFF;
428 addr6.sin6_addr.s6_addr[13] = (ip4addr >> 16) & 0xFF;
429 addr6.sin6_addr.s6_addr[14] = (ip4addr >> 8) & 0xFF;
430 addr6.sin6_addr.s6_addr[15] = (ip4addr) & 0xFF;
432 memcpy(addr, &addr6, sizeof(addr6));
436 * pg_promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using
437 * the standard convention for IPv4 addresses mapped into IPv6 world
439 * This must be different from pg_promote_v4_to_v6_addr because we want to
440 * set the high-order bits to 1's not 0's.
442 * The passed addr is modified in place; be sure it is large enough to
443 * hold the result! Note that we only worry about setting the fields
444 * that pg_range_sockaddr will look at.
447 pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr)
449 struct sockaddr_in addr4;
450 struct sockaddr_in6 addr6;
454 memcpy(&addr4, addr, sizeof(addr4));
455 ip4addr = ntohl(addr4.sin_addr.s_addr);
457 memset(&addr6, 0, sizeof(addr6));
459 addr6.sin6_family = AF_INET6;
461 for (i = 0; i < 12; i++)
462 addr6.sin6_addr.s6_addr[i] = 0xff;
464 addr6.sin6_addr.s6_addr[12] = (ip4addr >> 24) & 0xFF;
465 addr6.sin6_addr.s6_addr[13] = (ip4addr >> 16) & 0xFF;
466 addr6.sin6_addr.s6_addr[14] = (ip4addr >> 8) & 0xFF;
467 addr6.sin6_addr.s6_addr[15] = (ip4addr) & 0xFF;
469 memcpy(addr, &addr6, sizeof(addr6));
472 #endif /* HAVE_IPV6 */