Robert Haas wrote:
> So is this one Ready for Committer?
Here we go, I think this one is ready. In addition to previous patches,
it does:
* Use some techniques from postfix for getting interface addresses.
Couldn't use code outright, due to license incompatibilities.
* Tested on Solaris, FreeBSD, Linux and Windows. As far as I can tell
this should also work on Mac OS, HPUX and AIX, and probably others.
* Added src/tools/ifaddrs/test_ifaddrs tool for testing interface
address code.
Cheers,
Stef
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index ad4d084..e5152f4 100644
*** a/doc/src/sgml/client-auth.sgml
--- b/doc/src/sgml/client-auth.sgml
*************** hostnossl <replaceable>database</replac
*** 244,249 ****
--- 244,255 ----
support for IPv6 addresses.
</para>
+ <para>Instead of a <replaceable>CIDR-address</replaceable>, you can specify
+ <literal>samehost</literal> to match any of the server's own IP addresses,
+ or <literal>samenet</literal> to match any address in a subnet that the
+ server belongs to.
+ </para>
+
<para>
This field only applies to <literal>host</literal>,
<literal>hostssl</literal>, and <literal>hostnossl</> records.
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index e6f7db2..702971a 100644
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
*************** check_db(const char *dbname, const char
*** 512,517 ****
--- 512,608 ----
return false;
}
+ /*
+ * Check to see if a connecting IP matches the address and netmask.
+ */
+ static bool
+ check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
+ {
+ if (raddr->addr.ss_family == addr->sa_family)
+ {
+ /* Same address family */
+ if (!pg_range_sockaddr(&raddr->addr, (struct sockaddr_storage*)addr,
+ (struct sockaddr_storage*)mask))
+ return false;
+ }
+ #ifdef HAVE_IPV6
+ else if (addr->sa_family == AF_INET &&
+ raddr->addr.ss_family == AF_INET6)
+ {
+ /*
+ * Wrong address family. We allow only one case: if the file
+ * has IPv4 and the port is IPv6, promote the file address to
+ * IPv6 and try to match that way.
+ */
+ struct sockaddr_storage addrcopy,
+ maskcopy;
+
+ memcpy(&addrcopy, &addr, sizeof(addrcopy));
+ memcpy(&maskcopy, &mask, sizeof(maskcopy));
+ pg_promote_v4_to_v6_addr(&addrcopy);
+ pg_promote_v4_to_v6_mask(&maskcopy);
+
+ if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
+ return false;
+ }
+ #endif /* HAVE_IPV6 */
+ else
+ {
+ /* Wrong address family, no IPV6 */
+ return false;
+ }
+
+ return true;
+ }
+
+ typedef struct CheckNetwork {
+ NetMethod method;
+ SockAddr *raddr;
+ bool result;
+ } CheckNetwork;
+
+ static void
+ callback_check_network(struct sockaddr *addr, struct sockaddr *netmask, void *data)
+ {
+ CheckNetwork *cn = data;
+ struct sockaddr_storage mask;
+
+ /* Already found a match */
+ if (cn->result)
+ return;
+
+ /* Make a fully 1's netmask of appropriate length */
+ if (cn->method == nmSameHost)
+ {
+ pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
+ cn->result = check_ip(cn->raddr, addr, (struct sockaddr*)&mask);
+ }
+
+ /* Use the netmask of the interface itself */
+ else
+ {
+ cn->result = check_ip(cn->raddr, addr, netmask);
+ }
+ }
+
+ static bool
+ check_same_host_or_net(SockAddr *raddr, NetMethod method)
+ {
+ CheckNetwork cn;
+ cn.method = method;
+ cn.raddr = raddr;
+ cn.result = false;
+
+ if (pg_foreach_ifaddr(callback_check_network, &cn) < 0)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_WARNING),
+ errmsg("Error enumerating network interfaces")));
+ return false;
+ }
+
+ return cn.result;
+ }
/*
* Macros used to check and report on invalid configuration options.
*************** parse_hba_line(List *line, int line_num,
*** 658,756 ****
line_num, HbaFileName)));
return false;
}
- token = pstrdup(lfirst(line_item));
! /* Check if it has a CIDR suffix and if so isolate it */
! cidr_slash = strchr(token, '/');
! if (cidr_slash)
! *cidr_slash = '\0';
!
! /* Get the IP address either way */
! hints.ai_flags = AI_NUMERICHOST;
! hints.ai_family = PF_UNSPEC;
! hints.ai_socktype = 0;
! hints.ai_protocol = 0;
! hints.ai_addrlen = 0;
! hints.ai_canonname = NULL;
! hints.ai_addr = NULL;
! hints.ai_next = NULL;
! ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
! if (ret || !gai_result)
{
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid IP address \"%s\": %s",
! token, gai_strerror(ret)),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! if (cidr_slash)
! *cidr_slash = '/';
! if (gai_result)
! pg_freeaddrinfo_all(hints.ai_family, gai_result);
! return false;
}
! if (cidr_slash)
! *cidr_slash = '/';
!
! memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
! pg_freeaddrinfo_all(hints.ai_family, gai_result);
!
! /* Get the netmask */
! if (cidr_slash)
{
! if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
! parsedline->addr.ss_family) < 0)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid CIDR mask in address \"%s\"",
! token),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! return false;
! }
}
else
{
! /* Read the mask field. */
! line_item = lnext(line_item);
! if (!line_item)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("end-of-line before netmask specification"),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! return false;
! }
! token = lfirst(line_item);
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
if (ret || !gai_result)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid IP mask \"%s\": %s",
token, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
}
! memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
! if (parsedline->addr.ss_family != parsedline->mask.ss_family)
{
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("IP address and mask do not match in file \"%s\" line %d",
! HbaFileName, line_num)));
! return false;
}
}
} /* != ctLocal */
--- 749,868 ----
line_num, HbaFileName)));
return false;
}
! /* Is it equal to 'samehost' or 'samenet'? */
! token = lfirst(line_item);
! /* Any IP on this host is allowed to connect */
! if (strcmp(token, "samehost") == 0)
{
! parsedline->net_method = nmSameHost;
}
! /* Any IP on the host's subnets is allowed to connect */
! else if (strcmp(token, "samenet") == 0)
{
! parsedline->net_method = nmSameNet;
}
+
+ /* IP and netmask are specified */
else
{
! parsedline->net_method = nmCompare;
! token = pstrdup(token);
!
! /* Check if it has a CIDR suffix and if so isolate it */
! cidr_slash = strchr(token, '/');
! if (cidr_slash)
! *cidr_slash = '\0';
!
! /* Get the IP address either way */
! hints.ai_flags = AI_NUMERICHOST;
! hints.ai_family = PF_UNSPEC;
! hints.ai_socktype = 0;
! hints.ai_protocol = 0;
! hints.ai_addrlen = 0;
! hints.ai_canonname = NULL;
! hints.ai_addr = NULL;
! hints.ai_next = NULL;
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
if (ret || !gai_result)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid IP address \"%s\": %s",
token, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ if (cidr_slash)
+ *cidr_slash = '/';
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
}
! if (cidr_slash)
! *cidr_slash = '/';
!
! memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
! /* Get the netmask */
! if (cidr_slash)
{
! if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
! parsedline->addr.ss_family) < 0)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid CIDR mask in address \"%s\"",
! token),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! return false;
! }
! }
! else
! {
! /* Read the mask field. */
! line_item = lnext(line_item);
! if (!line_item)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("end-of-line before netmask specification"),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! return false;
! }
! token = lfirst(line_item);
!
! ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
! if (ret || !gai_result)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("invalid IP mask \"%s\": %s",
! token, gai_strerror(ret)),
! errcontext("line %d of configuration file \"%s\"",
! line_num, HbaFileName)));
! if (gai_result)
! pg_freeaddrinfo_all(hints.ai_family, gai_result);
! return false;
! }
!
! memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
! pg_freeaddrinfo_all(hints.ai_family, gai_result);
!
! if (parsedline->addr.ss_family != parsedline->mask.ss_family)
! {
! ereport(LOG,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("IP address and mask do not match in file \"%s\" line %d",
! HbaFileName, line_num)));
! return false;
! }
}
}
} /* != ctLocal */
*************** check_hba(hbaPort *port)
*** 1096,1131 ****
continue;
#endif
! /* Check IP address */
! if (port->raddr.addr.ss_family == hba->addr.ss_family)
{
! if (!pg_range_sockaddr(&port->raddr.addr, &hba->addr, &hba->mask))
continue;
! }
! #ifdef HAVE_IPV6
! else if (hba->addr.ss_family == AF_INET &&
! port->raddr.addr.ss_family == AF_INET6)
! {
! /*
! * Wrong address family. We allow only one case: if the file
! * has IPv4 and the port is IPv6, promote the file address to
! * IPv6 and try to match that way.
! */
! struct sockaddr_storage addrcopy,
! maskcopy;
!
! memcpy(&addrcopy, &hba->addr, sizeof(addrcopy));
! memcpy(&maskcopy, &hba->mask, sizeof(maskcopy));
! pg_promote_v4_to_v6_addr(&addrcopy);
! pg_promote_v4_to_v6_mask(&maskcopy);
!
! if (!pg_range_sockaddr(&port->raddr.addr, &addrcopy, &maskcopy))
continue;
! }
! #endif /* HAVE_IPV6 */
! else
! /* Wrong address family, no IPV6 */
continue;
} /* != ctLocal */
/* Check database and role */
--- 1208,1228 ----
continue;
#endif
! switch (hba->net_method)
{
! case nmCompare:
! if (!check_ip(&port->raddr, (struct sockaddr*)&hba->addr,
! (struct sockaddr*)&hba->mask))
continue;
! break;
! case nmSameHost:
! case nmSameNet:
! if (!check_same_host_or_net(&port->raddr, hba->net_method))
continue;
! break;
! default:
continue;
+ }
} /* != ctLocal */
/* Check database and role */
diff --git a/src/backend/libpq/ip.c b/src/backend/libpq/ip.c
index 0c35ddd..f0b7411 100644
*** a/src/backend/libpq/ip.c
--- b/src/backend/libpq/ip.c
*************** range_sockaddr_AF_INET6(const struct soc
*** 333,338 ****
--- 333,340 ----
* 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.
*************** pg_sockaddr_cidr_mask(struct sockaddr_st
*** 343,352 ****
long bits;
char *endptr;
! bits = strtol(numbits, &endptr, 10);
!
! if (*numbits == '\0' || *endptr != '\0')
! return -1;
switch (family)
{
--- 345,360 ----
long bits;
char *endptr;
! if (numbits == NULL)
! {
! bits = (family == AF_INET) ? 32 : 128;
! }
! else
! {
! bits = strtol(numbits, &endptr, 10);
! if (*numbits == '\0' || *endptr != '\0')
! return -1;
! }
switch (family)
{
*************** pg_promote_v4_to_v6_mask(struct sockaddr
*** 476,478 ****
--- 484,747 ----
}
#endif /* HAVE_IPV6 */
+
+
+ static void
+ run_ifaddr_callback(PgIfAddrCallback callback, void * cb_data,
+ struct sockaddr * addr, struct sockaddr * mask)
+ {
+ struct sockaddr_storage full;
+
+ 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(&full, NULL, addr->sa_family);
+ mask = (struct sockaddr*)&full;
+ }
+
+ (callback) (addr, mask, cb_data);
+ }
+
+ /*
+ * Enumerate network interface addresses on Win32. This uses
+ * the Winsock 2 functions (ie: ws2_32.dll)
+ */
+ #ifdef WIN32
+
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+ INTERFACE_INFO ii[64];
+ unsigned long length, i;
+ SOCKET sock;
+
+ sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+ if (sock == SOCKET_ERROR)
+ return -1;
+
+ if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0, &ii,
+ sizeof(ii), &length, 0, 0) == SOCKET_ERROR)
+ {
+ closesocket(sock);
+ return -1;
+ }
+
+ 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);
+ return 0;
+ }
+
+ /*
+ * Enumerate interfaces on BSDs, AIX and modern Linux.
+ * Uses the getifaddrs() interface, clean and simple.
+ */
+
+ #elif HAVE_GETIFADDRS /* && !WIN32 */
+
+ #include <ifaddrs.h>
+
+ 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 */
+
+ #include <sys/ioctl.h>
+ #include <net/if.h>
+
+ #ifdef HAVE_SYS_SOCKIO_H
+ #include <sys/sockio.h>
+ #endif
+
+ /*
+ * SIOCGIFCONF does not return IPv6 addresses on Solaris,
+ * and HP/UX. So we use SIOCGLIFCONF if it's available.
+ */
+
+ #ifdef SIOCGLIFCONF
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+ {
+ struct lifconf lifc;
+ struct lifreq *lifr, lmask;
+ struct sockaddr *addr, *mask;
+ char buffer[10240];
+ int sock, fd;
+ #ifdef HAVE_IPV6
+ int sock6;
+ #endif
+ int i, total;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return -1;
+
+ #ifdef HAVE_IPV6
+ sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock6 == -1)
+ {
+ close(sock);
+ return -1;
+ }
+ #endif
+
+ memset(&lifc, 0, sizeof (lifc));
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_len = sizeof (buffer);
+ lifc.lifc_buf = buffer;
+
+ if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0)
+ {
+ close(sock);
+ #ifdef HAVE_IPV6
+ close(sock6);
+ #endif
+ return -1;
+ }
+
+ 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);
+ }
+
+ close(sock);
+ #ifdef HAVE_IPV6
+ close(sock6);
+ #endif
+ return 0;
+ }
+
+ /*
+ * 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.
+ */
+
+ #elif defined(SIOCGIFCONF)
+
+ /* 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
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+ struct ifconf ifc;
+ struct ifreq *ifr, *end, addr, mask;
+ char buffer[10240];
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return -1;
+
+ ifc.ifc_buf = buffer;
+ ifc.ifc_len = sizeof(buffer);
+
+ if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
+ {
+ close(sock);
+ return -1;
+ }
+
+ 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));
+ }
+
+ close(sock);
+ return 0;
+ }
+
+ #else /* !defined(SIOCGIFCONF) */
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+ return -1;
+ }
+
+ #endif /* !defined(SIOCGIFCONF) */
+
+ #endif /* !HAVE_GETIFADDRS */
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index f1c0457..65966da 100644
*** a/src/backend/libpq/pg_hba.conf.sample
--- b/src/backend/libpq/pg_hba.conf.sample
***************
*** 33,38 ****
--- 33,41 ----
# (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies
# the number of significant bits in the mask. Alternatively, you can write
# an IP address and netmask in separate columns to specify the set of hosts.
+ # Instead of a CIDR-address, you can specify "samehost" to match any of the
+ # server's own IP addresses, or "samenet" to match any address in a subnet that
+ # the server belongs to.
#
# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
# "ident", "pam", "ldap" or "cert". Note that "password" sends passwords
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index b538ee4..5193f38 100644
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
*************** typedef enum UserAuth
*** 30,35 ****
--- 30,42 ----
uaCert
} UserAuth;
+ typedef enum NetMethod
+ {
+ nmCompare,
+ nmSameHost,
+ nmSameNet
+ } NetMethod;
+
typedef enum ConnType
{
ctLocal,
*************** typedef struct
*** 44,49 ****
--- 51,57 ----
ConnType conntype;
char *database;
char *role;
+ NetMethod net_method;
struct sockaddr_storage addr;
struct sockaddr_storage mask;
UserAuth auth_method;
diff --git a/src/include/libpq/ip.h b/src/include/libpq/ip.h
index 1934957..9bd562c 100644
*** a/src/include/libpq/ip.h
--- b/src/include/libpq/ip.h
*************** extern void pg_promote_v4_to_v6_mask(str
*** 47,50 ****
--- 47,56 ----
#define IS_AF_UNIX(fam) (0)
#endif
+ typedef void (*PgIfAddrCallback)(struct sockaddr * addr,
+ struct sockaddr * netmask,
+ void * cb_data);
+
+ extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data);
+
#endif /* IP_H */
diff --git a/src/tools/ifaddrs/Makefile b/src/tools/ifaddrs/Makefile
index ...4ec26c1 .
*** a/src/tools/ifaddrs/Makefile
--- b/src/tools/ifaddrs/Makefile
***************
*** 0 ****
--- 1,26 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile for src/tools/ifaddrs
+ #
+ # Copyright (c) 2003-2009, PostgreSQL Global Development Group
+ #
+ # $PostgreSQL$
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/tools/ifaddrs
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+
+ libpq_backdir = $(top_builddir)/src/backend/libpq/
+ override CPPFLAGS := -I$(libpq_backdir) $(CPPFLAGS)
+
+ OBJS= test_ifaddrs.o
+
+ all: test_ifaddrs
+
+ test_ifaddrs: test_ifaddrs.o $(libpq_backdir)/ip.o
+ $(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backdir)/ip.o $(LDFLAGS) $(LIBS) -o $...@$(X)
+
+ clean distclean maintainer-clean:
+ rm -f test_ifaddrs$(X) $(OBJS)
diff --git a/src/tools/ifaddrs/README b/src/tools/ifaddrs/README
index ...98e84ff .
*** a/src/tools/ifaddrs/README
--- b/src/tools/ifaddrs/README
***************
*** 0 ****
--- 1,10 ----
+ $PostgreSQL$
+
+ ifaddrs
+ =======
+
+ This program looks up all the IPv4 and IPv6 interfaces on the local machine. It is useful for testing that this functionality works on various platforms.
+
+ Usage: test_ifaddrs
+
+
diff --git a/src/tools/ifaddrs/test_ifaddrs.c b/src/tools/ifaddrs/test_ifaddrs.c
index ...4e77cbd .
*** a/src/tools/ifaddrs/test_ifaddrs.c
--- b/src/tools/ifaddrs/test_ifaddrs.c
***************
*** 0 ****
--- 1,72 ----
+ /*
+ * $PostgreSQL$
+ *
+ *
+ * test_ifaddrs.c
+ * test pg_foreach_ifaddr()
+ */
+
+ #include "postgres.h"
+
+ #include "libpq/ip.h"
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+
+ static void
+ print_addr(struct sockaddr *addr)
+ {
+ char buffer[256];
+ int ret, len;
+
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ len = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof (struct sockaddr_in6);
+ break;
+ default:
+ len = sizeof (struct sockaddr_storage);
+ break;
+ }
+
+ ret = getnameinfo(addr, len, buffer, 256, NULL, 0, NI_NUMERICHOST);
+ if (ret != 0)
+ printf ("[unknown:%d]", addr->sa_family);
+ else
+ printf ("%s", buffer);
+ }
+
+ static void
+ callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
+ {
+ printf ("addr: ");
+ print_addr (addr);
+ printf (" mask: ");
+ print_addr (mask);
+ printf ("\n");
+ }
+
+ int
+ main(int argc, char *argv[])
+ {
+ #ifdef WIN32
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
+ fprintf (stderr, "WSAStartup failed\n");
+ else
+ #endif
+ if (pg_foreach_ifaddr (callback, NULL) < 0)
+ fprintf (stderr, "pg_foreach_ifaddr failed: %s\n", strerror (errno));
+ return 0;
+ }
+
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index f5a01b3..2187420 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 147,152 ****
--- 147,153 ----
$libpq->AddIncludeDir('src\port');
$libpq->AddLibrary('wsock32.lib');
$libpq->AddLibrary('secur32.lib');
+ $libpq->AddLibrary('ws2_32.lib');
$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$libpq->UseDef('src\interfaces\libpq\libpqdll.def');
$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers