Enable IPv6 connections to the server, and add pg_hba.conf IPv6 entries

if the OS supports it.  Code will still compile on non-IPv6-aware
machines (feature added by Bruce).

Nigel Kukard
This commit is contained in:
Bruce Momjian 2003-01-06 03:18:27 +00:00
parent d99e7b5a0d
commit c3e9699f21
18 changed files with 949 additions and 248 deletions

75
configure vendored
View File

@ -7169,6 +7169,80 @@ fi
done done
# This exports HAVE_IPV6 to both C files and Makefiles
echo "$as_me:$LINENO: checking for getaddrinfo" >&5
echo $ECHO_N "checking for getaddrinfo... $ECHO_C" >&6
if test "${ac_cv_func_getaddrinfo+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char getaddrinfo (); below. */
#include <assert.h>
/* Override any gcc2 internal prototype to avoid an error. */
#ifdef __cplusplus
extern "C"
#endif
/* We use char because int might match the return type of a gcc2
builtin and then its argument prototype would still apply. */
char getaddrinfo ();
char (*f) ();
#ifdef F77_DUMMY_MAIN
# ifdef __cplusplus
extern "C"
# endif
int F77_DUMMY_MAIN() { return 1; }
#endif
int
main ()
{
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined (__stub_getaddrinfo) || defined (__stub___getaddrinfo)
choke me
#else
f = getaddrinfo;
#endif
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -s conftest$ac_exeext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_cv_func_getaddrinfo=yes
else
echo "$as_me: failed program was:" >&5
cat conftest.$ac_ext >&5
ac_cv_func_getaddrinfo=no
fi
rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
fi
echo "$as_me:$LINENO: result: $ac_cv_func_getaddrinfo" >&5
echo "${ECHO_T}$ac_cv_func_getaddrinfo" >&6
if test $ac_cv_func_getaddrinfo = yes; then
HAVE_IPV6="yes"; cat >>confdefs.h <<\_ACEOF
#define HAVE_IPV6 1
_ACEOF
fi
if test "$with_readline" = yes; then if test "$with_readline" = yes; then
for ac_header in readline/readline.h for ac_header in readline/readline.h
@ -16427,6 +16501,7 @@ s,@python_moduledir@,$python_moduledir,;t t
s,@python_moduleexecdir@,$python_moduleexecdir,;t t s,@python_moduleexecdir@,$python_moduleexecdir,;t t
s,@python_includespec@,$python_includespec,;t t s,@python_includespec@,$python_includespec,;t t
s,@python_libspec@,$python_libspec,;t t s,@python_libspec@,$python_libspec,;t t
s,@HAVE_IPV6@,$HAVE_IPV6,;t t
s,@LIBOBJS@,$LIBOBJS,;t t s,@LIBOBJS@,$LIBOBJS,;t t
s,@HPUXMATHLIB@,$HPUXMATHLIB,;t t s,@HPUXMATHLIB@,$HPUXMATHLIB,;t t
s,@HAVE_POSIX_SIGNALS@,$HAVE_POSIX_SIGNALS,;t t s,@HAVE_POSIX_SIGNALS@,$HAVE_POSIX_SIGNALS,;t t

View File

@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
dnl $Header: /cvsroot/pgsql/configure.in,v 1.224 2002/12/30 17:19:49 tgl Exp $ dnl $Header: /cvsroot/pgsql/configure.in,v 1.225 2003/01/06 03:18:25 momjian Exp $
dnl dnl
dnl Developers, please strive to achieve this order: dnl Developers, please strive to achieve this order:
dnl dnl
@ -687,6 +687,11 @@ AC_CHECK_HEADERS(netinet/tcp.h, [], [],
#endif #endif
]) ])
# This exports HAVE_IPV6 to both C files and Makefiles
AC_CHECK_FUNC(getaddrinfo,
[HAVE_IPV6="yes"; AC_DEFINE(HAVE_IPV6, 1, [])], [])
AC_SUBST(HAVE_IPV6)
if test "$with_readline" = yes; then if test "$with_readline" = yes; then
AC_CHECK_HEADERS(readline/readline.h, [], AC_CHECK_HEADERS(readline/readline.h, [],
[AC_CHECK_HEADERS(readline.h, [], [AC_CHECK_HEADERS(readline.h, [],
@ -908,7 +913,7 @@ AC_CHECK_FUNCS([strtoull strtouq], [break])
# Check for one of atexit() or on_exit() # Check for one of atexit() or on_exit()
AC_CHECK_FUNCS(atexit, [], AC_CHECK_FUNCS(atexit, [],
[AC_CHECK_FUNCS(on_exit, [], [AC_CHECK_FUNCS(on_exit, [],
[AC_MSG_ERROR([neither atexit() nor on_exit() found])])]) [AC_MSG_ERROR([neither atexit() nor on_exit() found])])])
AC_FUNC_FSEEKO AC_FUNC_FSEEKO

View File

@ -1,5 +1,5 @@
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.42 2002/12/03 21:50:44 momjian Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.43 2003/01/06 03:18:26 momjian Exp $
--> -->
<chapter id="client-authentication"> <chapter id="client-authentication">
@ -190,7 +190,11 @@ hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <
</blockquote> </blockquote>
must be zero for the record to match. (Of course IP addresses must be zero for the record to match. (Of course IP addresses
can be spoofed but this consideration is beyond the scope of can be spoofed but this consideration is beyond the scope of
<productname>PostgreSQL</productname>.) <productname>PostgreSQL</productname>.) If you machine supports
IPv6, the default <filename>pg_hba.conf</> will have an IPv6
entry for <literal>localhost</>. You can add your own IPv6
entries to the file. IPv6 entries are used only for IPv6
connections.
</para> </para>
<para> <para>

View File

@ -1,5 +1,5 @@
# -*-makefile-*- # -*-makefile-*-
# $Header: /cvsroot/pgsql/src/Makefile.global.in,v 1.158 2003/01/05 13:45:47 petere Exp $ # $Header: /cvsroot/pgsql/src/Makefile.global.in,v 1.159 2003/01/06 03:18:26 momjian Exp $
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# All PostgreSQL makefiles include this file and use the variables it sets, # All PostgreSQL makefiles include this file and use the variables it sets,
@ -277,6 +277,7 @@ ifeq ($(enable_rpath), yes)
LDFLAGS += $(rpath) LDFLAGS += $(rpath)
endif endif
HAVE_IPV6 = @HAVE_IPV6@
########################################################################## ##########################################################################
# #

View File

@ -4,7 +4,7 @@
# #
# Copyright (c) 1994, Regents of the University of California # Copyright (c) 1994, Regents of the University of California
# #
# $Header: /cvsroot/pgsql/src/backend/Makefile,v 1.89 2002/12/14 00:24:23 petere Exp $ # $Header: /cvsroot/pgsql/src/backend/Makefile,v 1.90 2003/01/06 03:18:26 momjian Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -132,7 +132,14 @@ ifeq ($(MAKE_DLL), true)
endif endif
endif endif
$(MAKE) -C catalog install-data $(MAKE) -C catalog install-data
ifdef HAVE_IPV6
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample $(DESTDIR)$(datadir)/pg_hba.conf.sample $(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample $(DESTDIR)$(datadir)/pg_hba.conf.sample
else
grep -v '^host.*::1.*ffff:ffff:ffff:ffff:ffff:ffff' \
$(srcdir)/libpq/pg_hba.conf.sample \
> $(srcdir)/libpq/pg_hba.conf.sample.no_ipv6
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample.no_ipv6 $(DESTDIR)$(datadir)/pg_hba.conf.sample
endif
$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample $(DESTDIR)$(datadir)/pg_ident.conf.sample $(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample $(DESTDIR)$(datadir)/pg_ident.conf.sample
$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample $(DESTDIR)$(datadir)/postgresql.conf.sample $(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample $(DESTDIR)$(datadir)/postgresql.conf.sample
@ -182,6 +189,9 @@ clean:
rm -f postgres$(X) $(POSTGRES_IMP) \ rm -f postgres$(X) $(POSTGRES_IMP) \
$(top_srcdir)/src/include/parser/parse.h \ $(top_srcdir)/src/include/parser/parse.h \
$(top_builddir)/src/include/utils/fmgroids.h $(top_builddir)/src/include/utils/fmgroids.h
ifndef HAVE_IPV6
rm -f $(srcdir)/libpq/pg_hba.conf.sample.no_ipv6
endif
ifeq ($(PORTNAME), win) ifeq ($(PORTNAME), win)
rm -f postgres.dll postgres.def libpostgres.a rm -f postgres.dll postgres.def libpostgres.a
endif endif

View File

@ -4,7 +4,7 @@
# Makefile for libpq subsystem (backend half of libpq interface) # Makefile for libpq subsystem (backend half of libpq interface)
# #
# IDENTIFICATION # IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.35 2002/12/06 04:37:02 momjian Exp $ # $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.36 2003/01/06 03:18:26 momjian Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
# be-fsstubs is here for historical reasons, probably belongs elsewhere # be-fsstubs is here for historical reasons, probably belongs elsewhere
OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o md5.o pqcomm.o \ OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
pqformat.o pqsignal.o pqformat.o pqsignal.o
all: SUBSYS.o all: SUBSYS.o

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.94 2002/12/06 04:37:02 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.95 2003/01/06 03:18:26 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -410,12 +410,18 @@ ClientAuthentication(Port *port)
*/ */
{ {
const char *hostinfo = "localhost"; const char *hostinfo = "localhost";
#ifdef HAVE_IPV6
char ip_hostinfo[INET6_ADDRSTRLEN];
#else
char ip_hostinfo[INET_ADDRSTRLEN];
#endif
if (isAF_INETx(port->raddr.sa.sa_family) )
hostinfo = SockAddr_ntop(&port->raddr, ip_hostinfo,
sizeof(ip_hostinfo), 1);
if (port->raddr.sa.sa_family == AF_INET)
hostinfo = inet_ntoa(port->raddr.in.sin_addr);
elog(FATAL, elog(FATAL,
"No pg_hba.conf entry for host %s, user %s, database %s", "No pg_hba.conf entry for host %s, user %s, database %s",
hostinfo, port->user, port->database); hostinfo, port->user, port->database);
break; break;
} }

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.92 2002/12/14 18:49:37 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.93 2003/01/06 03:18:26 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -586,8 +586,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
} }
else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
{ {
struct in_addr file_ip_addr, SockAddr file_ip_addr, mask;
mask;
if (strcmp(token, "hostssl") == 0) if (strcmp(token, "hostssl") == 0)
{ {
@ -623,7 +622,8 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
if (!line) if (!line)
goto hba_syntax; goto hba_syntax;
token = lfirst(line); token = lfirst(line);
if (!inet_aton(token, &file_ip_addr))
if(SockAddr_pton(&file_ip_addr, token) < 0)
goto hba_syntax; goto hba_syntax;
/* Read the mask field. */ /* Read the mask field. */
@ -631,7 +631,11 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
if (!line) if (!line)
goto hba_syntax; goto hba_syntax;
token = lfirst(line); token = lfirst(line);
if (!inet_aton(token, &mask))
if(SockAddr_pton(&mask, token) < 0)
goto hba_syntax;
if(file_ip_addr.sa.sa_family != mask.sa.sa_family)
goto hba_syntax; goto hba_syntax;
/* Read the rest of the line. */ /* Read the rest of the line. */
@ -643,8 +647,8 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
goto hba_syntax; goto hba_syntax;
/* Must meet network restrictions */ /* Must meet network restrictions */
if (port->raddr.sa.sa_family != AF_INET || if (!isAF_INETx(port->raddr.sa.sa_family) ||
((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) !rangeSockAddr(&port->raddr, &file_ip_addr, &mask))
return; return;
} }
else else

371
src/backend/libpq/ip.c Normal file
View File

@ -0,0 +1,371 @@
/*-------------------------------------------------------------------------
*
* ip.c
* Handles IPv6
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.1 2003/01/06 03:18:26 momjian Exp $
*
* This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
* http://www.lbsd.net.
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#include <sys/file.h>
#include "libpq/libpq.h"
#include "miscadmin.h"
#ifdef FRONTEND
#define elog fprintf
#define LOG stderr
#endif
#if defined(HAVE_UNIX_SOCKETS) && defined(HAVE_IPV6)
static int getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
struct addrinfo **result);
#endif /* HAVE_UNIX_SOCKETS */
/*
* getaddrinfo2 - get address info for Unix, IPv4 and IPv6 sockets
*/
int
getaddrinfo2(const char *hostname, const char *servname,
#ifdef HAVE_IPV6
const struct addrinfo *hintp, struct addrinfo **result)
#else
int family, SockAddr *result)
#endif
{
#ifdef HAVE_UNIX_SOCKETS
#ifdef HAVE_IPV6
if (hintp != NULL && hintp->ai_family == AF_UNIX)
return getaddrinfo_unix(servname, hintp, result);
#else
if (family == AF_UNIX)
return 0;
#endif
else
{
#endif /* HAVE_UNIX_SOCKETS */
#ifdef HAVE_IPV6
/* NULL has special meaning to getaddrinfo */
return getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
servname, hintp, result);
#else
if (hostname[0] == '\0')
result->in.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
struct hostent *hp;
hp = gethostbyname(hostname);
if ((hp == NULL) || (hp->h_addrtype != AF_INET))
{
elog(LOG, "getaddrinfo2: gethostbyname(%s) failed\n", hostname);
return STATUS_ERROR;
}
memmove((char *) &(result->in.sin_addr), (char *) hp->h_addr,
hp->h_length);
}
result->in.sin_port = htons((unsigned short)atoi(servname));
return 0;
#endif /* HAVE_IPV6 */
#ifdef HAVE_UNIX_SOCKETS
}
#endif /* HAVE_UNIX_SOCKETS */
}
/*
* freeaddrinfo2 - free IPv6 addrinfo structures
*/
#ifdef HAVE_IPV6
void
freeaddrinfo2(int hint_ai_family, struct addrinfo *ai)
{
#ifdef HAVE_UNIX_SOCKETS
if (hint_ai_family == AF_UNIX)
{
struct addrinfo *p;
while (ai != NULL)
{
p = ai;
ai = ai->ai_next;
free(p->ai_addr);
free(p);
}
}
else
#endif /* HAVE_UNIX_SOCKETS */
freeaddrinfo(ai);
}
#endif
#if defined(HAVE_UNIX_SOCKETS) && defined(HAVE_IPV6)
/* -------
* getaddrinfo_unix - get unix socket info using IPv6
*
* Bug: only one addrinfo is set even though hintsp is NULL or
* ai_socktype is 0
* AI_CANNONNAME does not support.
* -------
*/
static int
getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
struct addrinfo **result)
{
struct addrinfo hints;
struct addrinfo *aip;
struct sockaddr_un *unp;
MemSet(&hints, 0, sizeof(hints));
if (hintsp == NULL)
{
hints.ai_family = AF_UNIX;
hints.ai_socktype = SOCK_STREAM;
}
else
memcpy(&hints, hintsp, sizeof(hints));
if (hints.ai_socktype == 0)
hints.ai_socktype = SOCK_STREAM;
if (!(hints.ai_family == AF_UNIX))
{
elog(LOG, "hints.ai_family is invalied getaddrinfo_unix()\n");
return EAI_ADDRFAMILY;
}
aip = calloc(1, sizeof(struct addrinfo));
if (aip == NULL)
return EAI_MEMORY;
aip->ai_family = AF_UNIX;
aip->ai_socktype = hints.ai_socktype;
aip->ai_protocol = hints.ai_protocol;
aip->ai_next = NULL;
aip->ai_canonname = NULL;
*result = aip;
unp = calloc(1, sizeof(struct sockaddr_un));
if (aip == NULL)
return EAI_MEMORY;
unp->sun_family = AF_UNIX;
aip->ai_addr = (struct sockaddr *) unp;
aip->ai_addrlen = sizeof(struct sockaddr_un);
if (strlen(path) >= sizeof(unp->sun_path))
return EAI_SERVICE;
strcpy(unp->sun_path, path);
#if SALEN
unp->sun_len = sizeof(struct sockaddr_un);
#endif /* SALEN */
if (hints.ai_flags & AI_PASSIVE)
unlink(unp->sun_path);
return 0;
}
#endif /* HAVE_UNIX_SOCKETS && HAVE_IPV6 */
/* ----------
* SockAddr_ntop - set IP address string from SockAddr
*
* parameters... sa : SockAddr union
* dst : buffer for address string
* cnt : sizeof dst
* v4conv: non-zero: if address is IPv4 mapped IPv6 address then
* convert to IPv4 address.
* returns... pointer to dst
* if sa.sa_family is not AF_INET or AF_INET6 dst is set as empy string.
* ----------
*/
char *
SockAddr_ntop(const SockAddr *sa, char *dst, size_t cnt, int v4conv)
{
switch (sa->sa.sa_family)
{
case AF_INET:
#ifdef HAVE_IPV6
inet_ntop(AF_INET, &sa->in.sin_addr, dst, cnt);
#else
StrNCpy(dst, inet_ntoa(sa->in.sin_addr), cnt);
#endif
break;
#ifdef HAVE_IPV6
case AF_INET6:
inet_ntop(AF_INET6, &sa->in6.sin6_addr, dst, cnt);
if (v4conv && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr))
strcpy(dst, dst + 7);
break;
#endif
default:
dst[0] = '\0';
break;
}
return dst;
}
/*
* SockAddr_pton - IPv6 pton
*/
int
SockAddr_pton(SockAddr *sa, const char *src)
{
int family = AF_INET;
#ifdef HAVE_IPV6
const char *ch;
for (ch = src; *ch != '\0'; ch++)
{
if (*ch == ':')
{
family = AF_INET6;
break;
}
}
#endif
sa->sa.sa_family = family;
switch (family)
{
case AF_INET:
#ifdef HAVE_IPV6
return inet_pton(AF_INET, src, &sa->in.sin_addr);
#else
return inet_aton(src, &sa->in.sin_addr);
#endif
#ifdef HAVE_IPV6
case AF_INET6:
return inet_pton(AF_INET6, src, &sa->in6.sin6_addr);
break;
#endif
default:
return -1;
}
}
/*
* isAF_INETx - check to see if sa is AF_INET or AF_INET6
*/
int
isAF_INETx(const int family)
{
if (family == AF_INET
#ifdef HAVE_IPV6
|| family == AF_INET6
#endif
)
return 1;
else
return 0;
}
int
rangeSockAddr(const SockAddr *addr, const SockAddr *netaddr, const SockAddr *netmask)
{
if (addr->sa.sa_family == AF_INET)
return rangeSockAddrAF_INET(addr, netaddr, netmask);
#ifdef HAVE_IPV6
else if (addr->sa.sa_family == AF_INET6)
return rangeSockAddrAF_INET6(addr, netaddr, netmask);
#endif
else
return 0;
}
int
rangeSockAddrAF_INET(const SockAddr *addr, const SockAddr *netaddr,
const SockAddr *netmask)
{
if (addr->sa.sa_family != AF_INET ||
netaddr->sa.sa_family != AF_INET ||
netmask->sa.sa_family != AF_INET)
return 0;
if (((addr->in.sin_addr.s_addr ^ netaddr->in.sin_addr.s_addr) &
netmask->in.sin_addr.s_addr) == 0)
return 1;
else
return 0;
}
#ifdef HAVE_IPV6
int
rangeSockAddrAF_INET6(const SockAddr *addr, const SockAddr *netaddr,
const SockAddr *netmask)
{
int i;
if (IN6_IS_ADDR_V4MAPPED(&addr->in6.sin6_addr))
{
SockAddr addr4;
convSockAddr6to4(addr, &addr4);
if (rangeSockAddrAF_INET(&addr4, netaddr, netmask))
return 1;
}
if (netaddr->sa.sa_family != AF_INET6 ||
netmask->sa.sa_family != AF_INET6)
return 0;
for (i = 0; i < 16; i++)
{
if (((addr->in6.sin6_addr.s6_addr[i] ^ netaddr->in6.sin6_addr.s6_addr[i]) &
netmask->in6.sin6_addr.s6_addr[i]) != 0)
return 0;
}
return 1;
}
void
convSockAddr6to4(const SockAddr *src, SockAddr *dst)
{
char addr_str[INET6_ADDRSTRLEN];
dst->in.sin_family = AF_INET;
dst->in.sin_port = src->in6.sin6_port;
dst->in.sin_addr.s_addr = src->in6.sin6_addr.s6_addr32[3];
SockAddr_ntop(src, addr_str, INET6_ADDRSTRLEN, 0);
}
#endif

View File

@ -44,5 +44,6 @@
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD # TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
local all all trust local all all trust
host all all 127.0.0.1 255.255.255.255 trust host all all 127.0.0.1 255.255.255.255 trust
host all all ::1 ffff:ffff:ffff:ffff:ffff:ffff trust

View File

@ -29,7 +29,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pqcomm.c,v 1.143 2002/12/06 04:37:02 momjian Exp $ * $Id: pqcomm.c,v 1.144 2003/01/06 03:18:26 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -86,6 +86,18 @@ extern ssize_t secure_write(Port *, const void *, size_t);
static void pq_close(void); static void pq_close(void);
#ifdef HAVE_UNIX_SOCKETS
int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName);
int Setup_AF_UNIX(void);
#endif /* HAVE_UNIX_SOCKETS */
#ifdef HAVE_IPV6
#define FREEADDRINFO2(family, addrs) freeaddrinfo2((family), (addrs))
#else
/* do nothing */
#define FREEADDRINFO2(family, addrs) do {} while (0)
#endif
/* /*
* Configuration options * Configuration options
@ -182,149 +194,132 @@ int
StreamServerPort(int family, char *hostName, unsigned short portNumber, StreamServerPort(int family, char *hostName, unsigned short portNumber,
char *unixSocketName, int *fdP) char *unixSocketName, int *fdP)
{ {
SockAddr saddr;
int fd, int fd,
err; err;
int maxconn; int maxconn;
size_t len = 0;
int one = 1; int one = 1;
int ret;
char portNumberStr[64];
char *service;
/*
* IPv6 address lookups use a hint structure, while IPv4 creates an
* address structure directly.
*/
#ifdef HAVE_IPV6
struct addrinfo *addrs = NULL;
struct addrinfo hint;
Assert(family == AF_INET6 || family == AF_INET || family == AF_UNIX);
/* Initialize hint structure */
MemSet(&hint, 0, sizeof(hint));
hint.ai_family = family;
hint.ai_flags = AI_PASSIVE;
hint.ai_socktype = SOCK_STREAM;
#else
SockAddr saddr;
size_t len;
Assert(family == AF_INET || family == AF_UNIX); Assert(family == AF_INET || family == AF_UNIX);
if ((fd = socket(family, SOCK_STREAM, 0)) < 0) /* Initialize address structure */
MemSet((char *) &saddr, 0, sizeof(saddr));
saddr.sa.sa_family = family;
#endif /* HAVE_IPV6 */
#ifdef HAVE_UNIX_SOCKETS
if (family == AF_UNIX)
{ {
elog(LOG, "StreamServerPort: socket() failed: %m"); if (Lock_AF_UNIX(portNumber, unixSocketName) != STATUS_OK)
return STATUS_ERROR;
service = sock_path;
#ifndef HAVE_IPV6
UNIXSOCK_PATH(saddr.un, portNumber, unixSocketName);
len = UNIXSOCK_LEN(saddr.un);
#endif
}
else
#endif /* HAVE_UNIX_SOCKETS */
{
snprintf(portNumberStr, sizeof(portNumberStr), "%d", portNumber);
service = portNumberStr;
#ifndef HAVE_IPV6
len = sizeof(saddr.in);
#endif
}
/* Look up name using IPv6 or IPv4 routines */
#ifdef HAVE_IPV6
ret = getaddrinfo2(hostName, service, &hint, &addrs);
if (ret || addrs == NULL)
#else
ret = getaddrinfo2(hostName, service, family, &saddr);
if (ret)
#endif
{
elog(LOG, "server socket failure: getaddrinfo2()%s: %s",
#ifdef HAVE_IPV6
(family == AF_INET6) ? " using IPv6" : "", gai_strerror(ret));
if (addrs != NULL)
FREEADDRINFO2(hint.ai_family, addrs);
#else
"", hostName);
#endif
return STATUS_ERROR; return STATUS_ERROR;
} }
if (family == AF_INET) if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
{
elog(LOG, "server socket failure: socket(): %s",
strerror(errno));
FREEADDRINFO2(hint.ai_family, addrs);
return STATUS_ERROR;
}
if (isAF_INETx(family))
{ {
if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
sizeof(one))) == -1) sizeof(one))) == -1)
{ {
elog(LOG, "StreamServerPort: setsockopt(SO_REUSEADDR) failed: %m"); elog(LOG, "server socket failure: setsockopt(SO_REUSEADDR): %s",
strerror(errno));
FREEADDRINFO2(hint.ai_family, addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
} }
MemSet((char *) &saddr, 0, sizeof(saddr)); #ifdef HAVE_IPV6
saddr.sa.sa_family = family; Assert(addrs->ai_next == NULL && addrs->ai_family == family);
err = bind(fd, addrs->ai_addr, addrs->ai_addrlen);
#ifdef HAVE_UNIX_SOCKETS #else
if (family == AF_UNIX) err = bind(fd, (struct sockaddr *) &saddr.sa, len);
{ #endif
UNIXSOCK_PATH(saddr.un, portNumber, unixSocketName);
len = UNIXSOCK_LEN(saddr.un);
strcpy(sock_path, saddr.un.sun_path);
/*
* Grab an interlock file associated with the socket file.
*/
if (!CreateSocketLockFile(sock_path, true))
return STATUS_ERROR;
/*
* Once we have the interlock, we can safely delete any
* pre-existing socket file to avoid failure at bind() time.
*/
unlink(sock_path);
}
#endif /* HAVE_UNIX_SOCKETS */
if (family == AF_INET)
{
/* TCP/IP socket */
if (hostName[0] == '\0')
saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
struct hostent *hp;
hp = gethostbyname(hostName);
if ((hp == NULL) || (hp->h_addrtype != AF_INET))
{
elog(LOG, "StreamServerPort: gethostbyname(%s) failed",
hostName);
return STATUS_ERROR;
}
memmove((char *) &(saddr.in.sin_addr), (char *) hp->h_addr,
hp->h_length);
}
saddr.in.sin_port = htons(portNumber);
len = sizeof(struct sockaddr_in);
}
err = bind(fd, (struct sockaddr *) & saddr.sa, len);
if (err < 0) if (err < 0)
{ {
elog(LOG, "server socket failure: bind(): %s\n"
"\tIs another postmaster already running on port %d?",
strerror(errno), (int) portNumber);
if (family == AF_UNIX) if (family == AF_UNIX)
elog(LOG, "StreamServerPort: bind() failed: %m\n" elog(LOG, "\tIf not, remove socket node (%s) and retry.",
"\tIs another postmaster already running on port %d?\n" sock_path);
"\tIf not, remove socket node (%s) and retry.",
(int) portNumber, sock_path);
else else
elog(LOG, "StreamServerPort: bind() failed: %m\n" elog(LOG, "\tIf not, wait a few seconds and retry.");
"\tIs another postmaster already running on port %d?\n" FREEADDRINFO2(hint.ai_family, addrs);
"\tIf not, wait a few seconds and retry.",
(int) portNumber);
return STATUS_ERROR; return STATUS_ERROR;
} }
#ifdef HAVE_UNIX_SOCKETS #ifdef HAVE_UNIX_SOCKETS
if (family == AF_UNIX) if (family == AF_UNIX)
{ {
/* Arrange to unlink the socket file at exit */ if (Setup_AF_UNIX() != STATUS_OK)
on_proc_exit(StreamDoUnlink, 0);
/*
* Fix socket ownership/permission if requested. Note we must do
* this before we listen() to avoid a window where unwanted
* connections could get accepted.
*/
Assert(Unix_socket_group);
if (Unix_socket_group[0] != '\0')
{ {
char *endptr; FREEADDRINFO2(hint.ai_family, addrs);
unsigned long int val;
gid_t gid;
val = strtoul(Unix_socket_group, &endptr, 10);
if (*endptr == '\0')
{
/* numeric group id */
gid = val;
}
else
{
/* convert group name to id */
struct group *gr;
gr = getgrnam(Unix_socket_group);
if (!gr)
{
elog(LOG, "No such group as '%s'",
Unix_socket_group);
return STATUS_ERROR;
}
gid = gr->gr_gid;
}
if (chown(sock_path, -1, gid) == -1)
{
elog(LOG, "Could not set group of %s: %m",
sock_path);
return STATUS_ERROR;
}
}
if (chmod(sock_path, Unix_socket_permissions) == -1)
{
elog(LOG, "Could not set permissions on %s: %m",
sock_path);
return STATUS_ERROR; return STATUS_ERROR;
} }
} }
#endif /* HAVE_UNIX_SOCKETS */ #endif
/* /*
* Select appropriate accept-queue length limit. PG_SOMAXCONN is only * Select appropriate accept-queue length limit. PG_SOMAXCONN is only
@ -338,15 +333,105 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
err = listen(fd, maxconn); err = listen(fd, maxconn);
if (err < 0) if (err < 0)
{ {
elog(LOG, "StreamServerPort: listen() failed: %m"); elog(LOG, "server socket failure: listen(): %s",
strerror(errno));
FREEADDRINFO2(hint.ai_family, addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
*fdP = fd; *fdP = fd;
FREEADDRINFO2(hint.ai_family, addrs);
return STATUS_OK;
}
/*
* Lock_AF_UNIX -- configure unix socket file path
*/
#ifdef HAVE_UNIX_SOCKETS
int
Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
{
SockAddr saddr; /* just used to get socket path */
UNIXSOCK_PATH(saddr.un, portNumber, unixSocketName);
strcpy(sock_path, saddr.un.sun_path);
/*
* Grab an interlock file associated with the socket file.
*/
if (!CreateSocketLockFile(sock_path, true))
return STATUS_ERROR;
/*
* Once we have the interlock, we can safely delete any pre-existing
* socket file to avoid failure at bind() time.
*/
unlink(sock_path);
return STATUS_OK; return STATUS_OK;
} }
/*
* Setup_AF_UNIX -- configure unix socket permissions
*/
int
Setup_AF_UNIX(void)
{
/* Arrange to unlink the socket file at exit */
on_proc_exit(StreamDoUnlink, 0);
/*
* Fix socket ownership/permission if requested. Note we must do this
* before we listen() to avoid a window where unwanted connections
* could get accepted.
*/
Assert(Unix_socket_group);
if (Unix_socket_group[0] != '\0')
{
char *endptr;
unsigned long int val;
gid_t gid;
val = strtoul(Unix_socket_group, &endptr, 10);
if (*endptr == '\0')
{ /* numeric group id */
gid = val;
}
else
{ /* convert group name to id */
struct group *gr;
gr = getgrnam(Unix_socket_group);
if (!gr)
{
elog(LOG, "server socket failure: no such group '%s'",
Unix_socket_group);
return STATUS_ERROR;
}
gid = gr->gr_gid;
}
if (chown(sock_path, -1, gid) == -1)
{
elog(LOG, "server socket failure: could not set group of %s: %s",
sock_path, strerror(errno));
return STATUS_ERROR;
}
}
if (chmod(sock_path, Unix_socket_permissions) == -1)
{
elog(LOG, "server socket failure: could not set permissions on %s: %s",
sock_path, strerror(errno));
return STATUS_ERROR;
}
return STATUS_OK;
}
#endif /* HAVE_UNIX_SOCKETS */
/* /*
* StreamConnection -- create a new connection with client using * StreamConnection -- create a new connection with client using
* server port. * server port.
@ -365,7 +450,7 @@ StreamConnection(int server_fd, Port *port)
/* accept connection (and fill in the client (remote) address) */ /* accept connection (and fill in the client (remote) address) */
addrlen = sizeof(port->raddr); addrlen = sizeof(port->raddr);
if ((port->sock = accept(server_fd, if ((port->sock = accept(server_fd,
(struct sockaddr *) & port->raddr, (struct sockaddr *) &port->raddr,
&addrlen)) < 0) &addrlen)) < 0)
{ {
elog(LOG, "StreamConnection: accept() failed: %m"); elog(LOG, "StreamConnection: accept() failed: %m");
@ -373,7 +458,6 @@ StreamConnection(int server_fd, Port *port)
} }
#ifdef SCO_ACCEPT_BUG #ifdef SCO_ACCEPT_BUG
/* /*
* UnixWare 7+ and OpenServer 5.0.4 are known to have this bug, but it * UnixWare 7+ and OpenServer 5.0.4 are known to have this bug, but it
* shouldn't hurt to catch it for all versions of those platforms. * shouldn't hurt to catch it for all versions of those platforms.
@ -392,7 +476,7 @@ StreamConnection(int server_fd, Port *port)
} }
/* select NODELAY and KEEPALIVE options if it's a TCP connection */ /* select NODELAY and KEEPALIVE options if it's a TCP connection */
if (port->laddr.sa.sa_family == AF_INET) if (isAF_INETx(port->laddr.sa.sa_family))
{ {
int on = 1; int on = 1;
@ -557,10 +641,10 @@ pq_getbytes(char *s, size_t len)
* *
* If maxlen is not zero, it is an upper limit on the length of the * If maxlen is not zero, it is an upper limit on the length of the
* string we are willing to accept. We abort the connection (by * string we are willing to accept. We abort the connection (by
* returning EOF) if client tries to send more than that. Note that * returning EOF) if client tries to send more than that. Note that
* since we test maxlen in the outer per-bufferload loop, the limit * since we test maxlen in the outer per-bufferload loop, the limit
* is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than * is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
* specified. This is fine for the intended purpose, which is just * specified. This is fine for the intended purpose, which is just
* to prevent DoS attacks from not-yet-authenticated clients. * to prevent DoS attacks from not-yet-authenticated clients.
* *
* NOTE: this routine does not do any character set conversion, * NOTE: this routine does not do any character set conversion,

View File

@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.301 2002/12/06 04:37:02 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.302 2003/01/06 03:18:27 momjian Exp $
* *
* NOTES * NOTES
* *
@ -669,15 +669,30 @@ PostmasterMain(int argc, char *argv[])
*/ */
if (NetServer) if (NetServer)
{ {
status = StreamServerPort(AF_INET, VirtualHost, #ifdef HAVE_IPV6
/* Try INET6 first. May fail if kernel doesn't support IP6 */
status = StreamServerPort(AF_INET6, VirtualHost,
(unsigned short) PostPortNumber, (unsigned short) PostPortNumber,
UnixSocketDir, UnixSocketDir,
&ServerSock_INET); &ServerSock_INET);
if (status != STATUS_OK) if (status != STATUS_OK)
{ {
postmaster_error("cannot create INET stream port"); elog(LOG, "IPv6 support disabled --- perhaps the kernel does not support IPv6");
ExitPostmaster(1); #endif
status = StreamServerPort(AF_INET, VirtualHost,
(unsigned short) PostPortNumber,
UnixSocketDir,
&ServerSock_INET);
if (status != STATUS_OK)
{
postmaster_error("cannot create INET stream port");
ExitPostmaster(1);
}
#ifdef HAVE_IPV6
else
elog(LOG, "IPv4 socket created");
} }
#endif
} }
#ifdef HAVE_UNIX_SOCKETS #ifdef HAVE_UNIX_SOCKETS
@ -2091,13 +2106,19 @@ DoBackend(Port *port)
/* /*
* Get the remote host name and port for logging and status display. * Get the remote host name and port for logging and status display.
*/ */
if (port->raddr.sa.sa_family == AF_INET) if (isAF_INETx(port->raddr.sa.sa_family))
{ {
unsigned short remote_port; unsigned short remote_port;
char *host_addr; char *host_addr;
#ifdef HAVE_IPV6
char ip_hostinfo[INET6_ADDRSTRLEN];
#else
char ip_hostinfo[INET_ADDRSTRLEN];
#endif
remote_port = ntohs(port->raddr.in.sin_port); remote_port = ntohs(port->raddr.in.sin_port);
host_addr = inet_ntoa(port->raddr.in.sin_addr); host_addr = SockAddr_ntop(&port->raddr, ip_hostinfo,
sizeof(ip_hostinfo), 1);
remote_host = NULL; remote_host = NULL;

29
src/include/libpq/ip.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef IP_H
#define IP_H
#include <sys/socket.h>
#include <netdb.h>
#include "libpq/pqcomm.h"
#ifdef HAVE_IPV6
void freeaddrinfo2(int hint_ai_family, struct addrinfo *ai);
int getaddrinfo2(const char *hostname, const char *servname,
const struct addrinfo *hintp, struct addrinfo **result);
#else
int getaddrinfo2(const char *hostname, const char *servname,
int family, SockAddr *result);
#endif
char *SockAddr_ntop(const SockAddr *sa, char *dst, size_t cnt, int v4conv);
int SockAddr_pton(SockAddr *sa, const char *src);
int isAF_INETx(const int family);
int rangeSockAddr(const SockAddr *addr, const SockAddr *netaddr,
const SockAddr *netmask);
int rangeSockAddrAF_INET(const SockAddr *addr, const SockAddr *netaddr,
const SockAddr *netmask);
#ifdef HAVE_IPV6
int rangeSockAddrAF_INET6(const SockAddr *addr, const SockAddr *netaddr,
const SockAddr *netmask);
void convSockAddr6to4(const SockAddr *src, SockAddr *dst);
#endif
#endif /* IP_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq.h,v 1.54 2002/12/06 04:37:05 momjian Exp $ * $Id: libpq.h,v 1.55 2003/01/06 03:18:27 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -19,6 +19,7 @@
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#include "libpq/libpq-be.h" #include "libpq/libpq-be.h"
#include "libpq/ip.h"
/* ---------------- /* ----------------
* PQArgBlock * PQArgBlock

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pqcomm.h,v 1.72 2002/12/06 04:37:05 momjian Exp $ * $Id: pqcomm.h,v 1.73 2003/01/06 03:18:27 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -47,6 +47,9 @@ typedef union SockAddr
{ {
struct sockaddr sa; struct sockaddr sa;
struct sockaddr_in in; struct sockaddr_in in;
#ifdef HAVE_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr_un un; struct sockaddr_un un;
} SockAddr; } SockAddr;

View File

@ -8,7 +8,7 @@
* or in pg_config.h afterwards. Of course, if you edit pg_config.h, then your * or in pg_config.h afterwards. Of course, if you edit pg_config.h, then your
* changes will be overwritten the next time you run configure. * changes will be overwritten the next time you run configure.
* *
* $Id: pg_config.h.in,v 1.34 2002/11/10 00:38:21 momjian Exp $ * $Id: pg_config.h.in,v 1.35 2003/01/06 03:18:27 momjian Exp $
*/ */
#ifndef PG_CONFIG_H #ifndef PG_CONFIG_H
@ -368,6 +368,9 @@
/* Set to 1 if you have <sys/shm.h> */ /* Set to 1 if you have <sys/shm.h> */
#undef HAVE_SYS_SHM_H #undef HAVE_SYS_SHM_H
/* Set to 1 if you have <netinet/ip6.h> for IPv6 */
#undef HAVE_IPV6
/* Set to 1 if you have <kernel/OS.h> */ /* Set to 1 if you have <kernel/OS.h> */
#undef HAVE_KERNEL_OS_H #undef HAVE_KERNEL_OS_H

View File

@ -4,7 +4,7 @@
# #
# Copyright (c) 1994, Regents of the University of California # Copyright (c) 1994, Regents of the University of California
# #
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.70 2002/12/13 22:17:57 momjian Exp $ # $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.71 2003/01/06 03:18:27 momjian Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -21,8 +21,8 @@ SO_MINOR_VERSION= 1
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
pqexpbuffer.o dllist.o md5.o pqsignal.o fe-secure.o \ pqexpbuffer.o dllist.o pqsignal.o fe-secure.o wchar.o encnames.o ip.o \
wchar.o encnames.o \ md5.o \
$(filter inet_aton.o snprintf.o strerror.o, $(LIBOBJS)) $(filter inet_aton.o snprintf.o strerror.o, $(LIBOBJS))
@ -45,6 +45,9 @@ dllist.c: $(backend_src)/lib/dllist.c
md5.c: $(backend_src)/libpq/md5.c md5.c: $(backend_src)/libpq/md5.c
rm -f $@ && $(LN_S) $< . rm -f $@ && $(LN_S) $< .
ip.c: $(backend_src)/libpq/ip.c
rm -f $@ && $(LN_S) $< .
# We use several backend modules verbatim, but since we need to # We use several backend modules verbatim, but since we need to
# compile with appropriate options to build a shared lib, we can't # compile with appropriate options to build a shared lib, we can't
# necessarily use the same object files as the backend uses. Instead, # necessarily use the same object files as the backend uses. Instead,
@ -70,5 +73,5 @@ uninstall: uninstall-lib
rm -f $(DESTDIR)$(includedir)/libpq-fe.h $(DESTDIR)$(includedir_internal)/libpq-int.h $(includedir_internal)/pqexpbuffer.h rm -f $(DESTDIR)$(includedir)/libpq-fe.h $(DESTDIR)$(includedir_internal)/libpq-int.h $(includedir_internal)/pqexpbuffer.h
clean distclean maintainer-clean: clean-lib clean distclean maintainer-clean: clean-lib
rm -f $(OBJS) dllist.c md5.c wchar.c encnames.c rm -f $(OBJS) dllist.c md5.c v6util.c wchar.c encnames.c
rm -f $(OBJS) inet_aton.c snprintf.c strerror.c rm -f $(OBJS) inet_aton.c snprintf.c strerror.c

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.216 2002/12/19 19:30:24 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.217 2003/01/06 03:18:27 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -39,6 +39,9 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#include "libpq/ip.h"
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#include "strdup.h" #include "strdup.h"
#endif #endif
@ -48,6 +51,14 @@
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#ifdef HAVE_IPV6
#define FREEADDRINFO2(family, addrs) freeaddrinfo2((family), (addrs))
#else
/* do nothing */
#define FREEADDRINFO2(family, addrs) do {} while (0)
#endif
#ifdef WIN32 #ifdef WIN32
static int static int
inet_aton(const char *cp, struct in_addr * inp) inet_aton(const char *cp, struct in_addr * inp)
@ -784,19 +795,32 @@ connectFailureMessage(PGconn *conn, int errorno)
static int static int
connectDBStart(PGconn *conn) connectDBStart(PGconn *conn)
{ {
int portno, int portnum;
family; int sockfd;
char portstr[64];
#ifdef USE_SSL #ifdef USE_SSL
StartupPacket np; /* Used to negotiate SSL connection */ StartupPacket np; /* Used to negotiate SSL connection */
char SSLok; char SSLok;
#endif #endif
#ifdef HAVE_IPV6
struct addrinfo *addrs = NULL;
struct addrinfo *addr_cur = NULL;
struct addrinfo hint;
const char *node = NULL;
const char *unix_node = "unix";
int ret;
/* Initialize hint structure */
MemSet(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
#else
int family = -1;
#endif
if (!conn) if (!conn)
return 0; return 0;
#ifdef NOT_USED #ifdef NOT_USED
/* /*
* parse dbName to get all additional info in it, if any * parse dbName to get all additional info in it, if any
*/ */
@ -815,10 +839,26 @@ connectDBStart(PGconn *conn)
MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr)); MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));
/*
* This code is confusing because IPv6 creates a hint structure
* that is passed to getaddrinfo2(), which returns a list of address
* structures that are looped through, while IPv4 creates an address
* structure directly.
*/
if (conn->pgport != NULL && conn->pgport[0] != '\0')
portnum = atoi(conn->pgport);
else
portnum = DEF_PGPORT;
snprintf(portstr, sizeof(portstr), "%d", portnum);
if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0') if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
{ {
#ifdef HAVE_IPV6
node = conn->pghostaddr;
hint.ai_family = AF_UNSPEC;
#else
/* Using pghostaddr avoids a hostname lookup */ /* Using pghostaddr avoids a hostname lookup */
/* Note that this supports IPv4 only */
struct in_addr addr; struct in_addr addr;
if (!inet_aton(conn->pghostaddr, &addr)) if (!inet_aton(conn->pghostaddr, &addr))
@ -833,120 +873,158 @@ connectDBStart(PGconn *conn)
memmove((char *) &(conn->raddr.in.sin_addr), memmove((char *) &(conn->raddr.in.sin_addr),
(char *) &addr, sizeof(addr)); (char *) &addr, sizeof(addr));
#endif
} }
else if (conn->pghost != NULL && conn->pghost[0] != '\0') else if (conn->pghost != NULL && conn->pghost[0] != '\0')
{ {
#ifdef HAVE_IPV6
node = conn->pghost;
hint.ai_family = AF_UNSPEC;
#else
/* Using pghost, so we have to look-up the hostname */ /* Using pghost, so we have to look-up the hostname */
struct hostent *hp; if (getaddrinfo2(conn->pghost, portstr, family, &conn->raddr) != 0)
hp = gethostbyname(conn->pghost);
if ((hp == NULL) || (hp->h_addrtype != AF_INET))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("unknown host name: %s\n"),
conn->pghost);
goto connect_errReturn; goto connect_errReturn;
}
family = AF_INET; family = AF_INET;
#endif
memmove((char *) &(conn->raddr.in.sin_addr),
(char *) hp->h_addr,
hp->h_length);
}
else
{
/* pghostaddr and pghost are NULL, so use Unix domain socket */
family = AF_UNIX;
}
/* Set family */
conn->raddr.sa.sa_family = family;
/* Set port number */
if (conn->pgport != NULL && conn->pgport[0] != '\0')
portno = atoi(conn->pgport);
else
portno = DEF_PGPORT;
if (family == AF_INET)
{
conn->raddr.in.sin_port = htons((unsigned short) (portno));
conn->raddr_len = sizeof(struct sockaddr_in);
} }
#ifdef HAVE_UNIX_SOCKETS #ifdef HAVE_UNIX_SOCKETS
else else
{ {
UNIXSOCK_PATH(conn->raddr.un, portno, conn->pgunixsocket); #ifdef HAVE_IPV6
node = unix_node;
hint.ai_family = AF_UNIX;
#else
/* pghostaddr and pghost are NULL, so use Unix domain socket */
family = AF_UNIX;
#endif
}
#endif /* HAVE_UNIX_SOCKETS */
#ifndef HAVE_IPV6
conn->raddr.sa.sa_family = family;
#endif
#ifdef HAVE_IPV6
if (hint.ai_family == AF_UNSPEC)
{/* do nothing*/}
#else
if (family == AF_INET)
{
conn->raddr.in.sin_port = htons((unsigned short) (portnum));
conn->raddr_len = sizeof(struct sockaddr_in);
}
#endif
#ifdef HAVE_UNIX_SOCKETS
else
{
UNIXSOCK_PATH(conn->raddr.un, portnum, conn->pgunixsocket);
conn->raddr_len = UNIXSOCK_LEN(conn->raddr.un); conn->raddr_len = UNIXSOCK_LEN(conn->raddr.un);
StrNCpy(portstr, conn->raddr.un.sun_path, sizeof(portstr));
#ifdef USE_SSL #ifdef USE_SSL
/* Don't bother requesting SSL over a Unix socket */ /* Don't bother requesting SSL over a Unix socket */
conn->allow_ssl_try = false; conn->allow_ssl_try = false;
conn->require_ssl = false; conn->require_ssl = false;
#endif #endif
} }
#endif /* HAVE_UNIX_SOCKETS */
#if HAVE_IPV6
ret = getaddrinfo2(node, portstr, &hint, &addrs);
if (ret || addrs == NULL)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("failed to getaddrinfo(): %s\n"),
gai_strerror(ret));
goto connect_errReturn;
}
addr_cur = addrs;
#endif #endif
/* Open a socket */ do
if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0) {
#ifdef HAVE_IPV6
sockfd = socket(addr_cur->ai_family, SOCK_STREAM,
addr_cur->ai_protocol);
#else
sockfd = socket(family, SOCK_STREAM, 0);
#endif
if (sockfd < 0)
continue;
conn->sock = sockfd;
#ifdef HAVE_IPV6
if (isAF_INETx(addr_cur->ai_family))
#else
if (isAF_INETx(family))
#endif
{
if (!connectNoDelay(conn))
goto connect_errReturn;
}
#if !defined(USE_SSL)
if (connectMakeNonblocking(conn) == 0)
goto connect_errReturn;
#endif
/* ----------
* Start / make connection. We are hopefully in non-blocking mode
* now, but it is possible that:
* 1. Older systems will still block on connect, despite the
* non-blocking flag. (Anyone know if this is true?)
* 2. We are using SSL.
* Thus, we have to make arrangements for all eventualities.
* ----------
*/
retry1:
#ifdef HAVE_IPV6
if (connect(sockfd, addr_cur->ai_addr, addr_cur->ai_addrlen) == 0)
#else
if (connect(sockfd, &conn->raddr.sa, conn->raddr_len) == 0)
#endif
{
/* We're connected already */
conn->status = CONNECTION_MADE;
break;
}
else
{
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry1;
if (SOCK_ERRNO == EINPROGRESS || SOCK_ERRNO == EWOULDBLOCK || SOCK_ERRNO == 0)
{
/*
* This is fine - we're in non-blocking mode, and the
* connection is in progress.
*/
conn->status = CONNECTION_STARTED;
break;
}
}
close(sockfd);
#ifdef HAVE_IPV6
} while ((addr_cur = addr_cur->ai_next) != NULL);
if (addr_cur == NULL)
#else
} while (0);
if (sockfd < 0)
#endif
{ {
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create socket: %s\n"), libpq_gettext("could not create socket: %s\n"),
SOCK_STRERROR(SOCK_ERRNO)); SOCK_STRERROR(SOCK_ERRNO));
goto connect_errReturn; goto connect_errReturn;
} }
/*
* Set the right options. Normally, we need nonblocking I/O, and we
* don't want delay of outgoing data for AF_INET sockets. If we are
* using SSL, then we need the blocking I/O (XXX Can this be fixed?).
*/
if (family == AF_INET)
{
if (!connectNoDelay(conn))
goto connect_errReturn;
}
#if !defined(USE_SSL)
if (connectMakeNonblocking(conn) == 0)
goto connect_errReturn;
#endif
/* ----------
* Start / make connection. We are hopefully in non-blocking mode
* now, but it is possible that:
* 1. Older systems will still block on connect, despite the
* non-blocking flag. (Anyone know if this is true?)
* 2. We are using SSL.
* Thus, we have to make arrangements for all eventualities.
* ----------
*/
retry1:
if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
{
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry1;
if (SOCK_ERRNO == EINPROGRESS || SOCK_ERRNO == EWOULDBLOCK || SOCK_ERRNO == 0)
{
/*
* This is fine - we're in non-blocking mode, and the
* connection is in progress.
*/
conn->status = CONNECTION_STARTED;
}
else
{
/* Something's gone wrong */
connectFailureMessage(conn, SOCK_ERRNO);
goto connect_errReturn;
}
}
else else
{ {
/* We're connected already */ #ifdef HAVE_IPV6
conn->status = CONNECTION_MADE; memmove(&conn->raddr, addr_cur->ai_addr, addr_cur->ai_addrlen);
conn->raddr_len = addr_cur->ai_addrlen;
FREEADDRINFO2(hint.ai_family, addrs);
addrs = NULL;
#endif
} }
#ifdef USE_SSL #ifdef USE_SSL
@ -1014,7 +1092,6 @@ retry2:
} }
#endif #endif
/* /*
* This makes the connection non-blocking, for all those cases which * This makes the connection non-blocking, for all those cases which
* forced us not to do it above. * forced us not to do it above.
@ -1038,7 +1115,10 @@ connect_errReturn:
conn->sock = -1; conn->sock = -1;
} }
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
#ifdef HAVE_IPV6
if (addrs != NULL)
FREEADDRINFO2(hint.ai_family, addrs);
#endif
return 0; return 0;
} }