aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/ip.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-10-01 01:58:58 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-10-01 01:58:58 +0000
commitf3aec2c7f51904e7920cd27115fdab831795d96b (patch)
tree6c81410fb6470f1ab8b7a4e5b74ec3ebbab775f6 /src/backend/libpq/ip.c
parentf7082f269eb2ee5347ddd411602a870866d783d2 (diff)
downloadpostgresql-f3aec2c7f51904e7920cd27115fdab831795d96b.tar.gz
postgresql-f3aec2c7f51904e7920cd27115fdab831795d96b.zip
Support "samehost" and "samenet" specifications in pg_hba.conf,
by enumerating the machine's IP interfaces to look for a match. Stef Walter
Diffstat (limited to 'src/backend/libpq/ip.c')
-rw-r--r--src/backend/libpq/ip.c416
1 files changed, 411 insertions, 5 deletions
diff --git a/src/backend/libpq/ip.c b/src/backend/libpq/ip.c
index 90ec119d79d..55f006f60c5 100644
--- a/src/backend/libpq/ip.c
+++ b/src/backend/libpq/ip.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/ip.c,v 1.47 2009/06/11 19:00:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/ip.c,v 1.48 2009/10/01 01:58:57 tgl Exp $
*
* This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@@ -333,6 +333,8 @@ range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr,
* pg_sockaddr_cidr_mask - make a network mask of the appropriate family
* and required number of significant bits
*
+ * numbits can be null, in which case the mask is fully set.
+ *
* The resulting mask is placed in *mask, which had better be big enough.
*
* Return value is 0 if okay, -1 if not.
@@ -343,10 +345,16 @@ pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
long bits;
char *endptr;
- bits = strtol(numbits, &endptr, 10);
-
- if (*numbits == '\0' || *endptr != '\0')
- return -1;
+ if (numbits == NULL)
+ {
+ bits = (family == AF_INET) ? 32 : 128;
+ }
+ else
+ {
+ bits = strtol(numbits, &endptr, 10);
+ if (*numbits == '\0' || *endptr != '\0')
+ return -1;
+ }
switch (family)
{
@@ -476,3 +484,401 @@ pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr)
}
#endif /* HAVE_IPV6 */
+
+
+/*
+ * Run the callback function for the addr/mask, after making sure the
+ * mask is sane for the addr.
+ */
+static void
+run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data,
+ struct sockaddr *addr, struct sockaddr *mask)
+{
+ struct sockaddr_storage fullmask;
+
+ if (!addr)
+ return;
+
+ /* Check that the mask is valid */
+ if (mask)
+ {
+ if (mask->sa_family != addr->sa_family)
+ {
+ mask = NULL;
+ }
+ else if (mask->sa_family == AF_INET)
+ {
+ if (((struct sockaddr_in*)mask)->sin_addr.s_addr == INADDR_ANY)
+ mask = NULL;
+ }
+#ifdef HAVE_IPV6
+ else if (mask->sa_family == AF_INET6)
+ {
+ if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)mask)->sin6_addr))
+ mask = NULL;
+ }
+#endif
+ }
+
+ /* If mask is invalid, generate our own fully-set mask */
+ if (!mask)
+ {
+ pg_sockaddr_cidr_mask(&fullmask, NULL, addr->sa_family);
+ mask = (struct sockaddr*) &fullmask;
+ }
+
+ (*callback) (addr, mask, cb_data);
+}
+
+#ifdef WIN32
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version is for Win32. Uses the Winsock 2 functions (ie: ws2_32.dll)
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ INTERFACE_INFO *ptr, *ii = NULL;
+ unsigned long length, i;
+ unsigned long n_ii = 0;
+ SOCKET sock;
+ int error;
+
+ sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+ if (sock == SOCKET_ERROR)
+ return -1;
+
+ while (n_ii < 1024)
+ {
+ n_ii += 64;
+ ptr = realloc(ii, sizeof (INTERFACE_INFO) * n_ii);
+ if (!ptr)
+ {
+ free(ii);
+ closesocket(sock);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ii = ptr;
+ if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0,
+ ii, n_ii * sizeof (INTERFACE_INFO),
+ &length, 0, 0) == SOCKET_ERROR)
+ {
+ error = WSAGetLastError();
+ if (error == WSAEFAULT || error == WSAENOBUFS)
+ continue; /* need to make the buffer bigger */
+ closesocket(sock);
+ free(ii);
+ return -1;
+ }
+
+ break;
+ }
+
+ for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i)
+ run_ifaddr_callback(callback, cb_data,
+ (struct sockaddr*)&ii[i].iiAddress,
+ (struct sockaddr*)&ii[i].iiNetmask);
+
+ closesocket(sock);
+ free(ii);
+ return 0;
+}
+
+#elif HAVE_GETIFADDRS /* && !WIN32 */
+
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version uses the getifaddrs() interface, which is available on
+ * BSDs, AIX, and modern Linux.
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ struct ifaddrs *ifa, *l;
+
+ if (getifaddrs(&ifa) < 0)
+ return -1;
+
+ for (l = ifa; l; l = l->ifa_next)
+ run_ifaddr_callback(callback, cb_data,
+ l->ifa_addr, l->ifa_netmask);
+
+ freeifaddrs(ifa);
+ return 0;
+}
+
+#else /* !HAVE_GETIFADDRS && !WIN32 */
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+/*
+ * SIOCGIFCONF does not return IPv6 addresses on Solaris
+ * and HP/UX. So we prefer SIOCGLIFCONF if it's available.
+ */
+
+#if defined(SIOCGLIFCONF)
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version uses ioctl(SIOCGLIFCONF).
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ struct lifconf lifc;
+ struct lifreq *lifr, lmask;
+ struct sockaddr *addr, *mask;
+ char *ptr, *buffer = NULL;
+ size_t n_buffer = 1024;
+ int sock, fd;
+#ifdef HAVE_IPV6
+ int sock6;
+#endif
+ int i, total;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return -1;
+
+ while (n_buffer < 1024 * 100)
+ {
+ n_buffer += 1024;
+ ptr = realloc(buffer, n_buffer);
+ if (!ptr)
+ {
+ free(buffer);
+ close(sock);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memset(&lifc, 0, sizeof (lifc));
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_buf = buffer = ptr;
+ lifc.lifc_len = n_buffer;
+
+ if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0)
+ {
+ if (errno == EINVAL)
+ continue;
+ free(buffer);
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Some Unixes try to return as much data as possible,
+ * with no indication of whether enough space allocated.
+ * Don't believe we have it all unless there's lots of slop.
+ */
+ if (lifc.lifc_len < n_buffer - 1024)
+ break;
+ }
+
+#ifdef HAVE_IPV6
+ /* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */
+ sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock6 == -1)
+ {
+ free(buffer);
+ close(sock);
+ return -1;
+ }
+#endif
+
+ total = lifc.lifc_len / sizeof(struct lifreq);
+ lifr = lifc.lifc_req;
+ for (i = 0; i < total; ++i)
+ {
+ addr = (struct sockaddr*)&lifr[i].lifr_addr;
+ memcpy(&lmask, &lifr[i], sizeof(struct lifreq));
+#ifdef HAVE_IPV6
+ fd = (addr->sa_family == AF_INET6) ? sock6 : sock;
+#else
+ fd = sock;
+#endif
+ if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0)
+ mask = NULL;
+ else
+ mask = (struct sockaddr*)&lmask.lifr_addr;
+ run_ifaddr_callback(callback, cb_data, addr, mask);
+ }
+
+ free(buffer);
+ close(sock);
+#ifdef HAVE_IPV6
+ close(sock6);
+#endif
+ return 0;
+}
+
+#elif defined(SIOCGIFCONF)
+
+/*
+ * Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information
+ * here, so this is the least preferred method. Note that there is no
+ * standard way to iterate the struct ifreq returned in the array.
+ * On some OSs the structures are padded large enough for any address,
+ * on others you have to calculate the size of the struct ifreq.
+ */
+
+/* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */
+#ifndef _SIZEOF_ADDR_IFREQ
+
+/* Calculate based on sockaddr.sa_len */
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+#define _SIZEOF_ADDR_IFREQ(ifr) \
+ ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
+ (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
+ (ifr).ifr_addr.sa_len) : sizeof(struct ifreq))
+
+/* Padded ifreq structure, simple */
+#else
+#define _SIZEOF_ADDR_IFREQ(ifr) \
+ sizeof (struct ifreq)
+#endif
+
+#endif /* !_SIZEOF_ADDR_IFREQ */
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version uses ioctl(SIOCGIFCONF).
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ struct ifconf ifc;
+ struct ifreq *ifr, *end, addr, mask;
+ char *ptr, *buffer = NULL;
+ size_t n_buffer = 1024;
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return -1;
+
+ while (n_buffer < 1024 * 100)
+ {
+ n_buffer += 1024;
+ ptr = realloc(buffer, n_buffer);
+ if (!ptr)
+ {
+ free(buffer);
+ close(sock);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memset(&ifc, 0, sizeof (ifc));
+ ifc.ifc_buf = buffer = ptr;
+ ifc.ifc_len = n_buffer;
+
+ if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
+ {
+ if (errno == EINVAL)
+ continue;
+ free(buffer);
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Some Unixes try to return as much data as possible,
+ * with no indication of whether enough space allocated.
+ * Don't believe we have it all unless there's lots of slop.
+ */
+ if (ifc.ifc_len < n_buffer - 1024)
+ break;
+ }
+
+ end = (struct ifreq*)(buffer + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < end;)
+ {
+ memcpy(&addr, ifr, sizeof(addr));
+ memcpy(&mask, ifr, sizeof(mask));
+ if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 &&
+ ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0)
+ run_ifaddr_callback(callback, cb_data,
+ &addr.ifr_addr, &mask.ifr_addr);
+ ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr));
+ }
+
+ free(buffer);
+ close(sock);
+ return 0;
+}
+
+#else /* !defined(SIOCGIFCONF) */
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version is our fallback if there's no known way to get the
+ * interface addresses. Just return the standard loopback addresses.
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ struct sockaddr_in addr;
+ struct sockaddr_storage mask;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 addr6;
+#endif
+
+ /* addr 127.0.0.1/8 */
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = ntohl(0x7f000001);
+ memset(&mask, 0, sizeof(mask));
+ pg_sockaddr_cidr_mask(&mask, "8", AF_INET);
+ run_ifaddr_callback(callback, cb_data,
+ (struct sockaddr*)&addr,
+ (struct sockaddr*)&mask);
+
+#ifdef HAVE_IPV6
+ /* addr ::1/128 */
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_addr.s6_addr[15] = 1;
+ memset(&mask, 0, sizeof(mask));
+ pg_sockaddr_cidr_mask(&mask, "128", AF_INET6);
+ run_ifaddr_callback(callback, cb_data,
+ (struct sockaddr*)&addr6,
+ (struct sockaddr*)&mask);
+#endif
+
+ return 0;
+}
+
+#endif /* !defined(SIOCGIFCONF) */
+
+#endif /* !HAVE_GETIFADDRS */