diff --git a/configure b/configure index 304ed53728..425db13d05 100755 --- a/configure +++ b/configure @@ -7169,6 +7169,80 @@ fi 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 +/* 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 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_includespec@,$python_includespec,;t t s,@python_libspec@,$python_libspec,;t t +s,@HAVE_IPV6@,$HAVE_IPV6,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@HPUXMATHLIB@,$HPUXMATHLIB,;t t s,@HAVE_POSIX_SIGNALS@,$HAVE_POSIX_SIGNALS,;t t diff --git a/configure.in b/configure.in index f5f0bb747d..64cb6844d5 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ 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 Developers, please strive to achieve this order: dnl @@ -687,6 +687,11 @@ AC_CHECK_HEADERS(netinet/tcp.h, [], [], #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 AC_CHECK_HEADERS(readline/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() AC_CHECK_FUNCS(atexit, [], [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 diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index c4d4af1251..8e28e3e17a 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,5 +1,5 @@ @@ -190,7 +190,11 @@ hostssl database user < must be zero for the record to match. (Of course IP addresses can be spoofed but this consideration is beyond the scope of - PostgreSQL.) + PostgreSQL.) If you machine supports + IPv6, the default pg_hba.conf will have an IPv6 + entry for localhost. You can add your own IPv6 + entries to the file. IPv6 entries are used only for IPv6 + connections. diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 0a9b3ff75e..c0d2d202c2 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -1,5 +1,5 @@ # -*-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, @@ -277,6 +277,7 @@ ifeq ($(enable_rpath), yes) LDFLAGS += $(rpath) endif +HAVE_IPV6 = @HAVE_IPV6@ ########################################################################## # diff --git a/src/backend/Makefile b/src/backend/Makefile index 2365b21efd..9df4b921d1 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -4,7 +4,7 @@ # # 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 $(MAKE) -C catalog install-data +ifdef HAVE_IPV6 $(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)/utils/misc/postgresql.conf.sample $(DESTDIR)$(datadir)/postgresql.conf.sample @@ -182,6 +189,9 @@ clean: rm -f postgres$(X) $(POSTGRES_IMP) \ $(top_srcdir)/src/include/parser/parse.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) rm -f postgres.dll postgres.def libpostgres.a endif diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index 31eecf739b..a4aff2c332 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -4,7 +4,7 @@ # Makefile for libpq subsystem (backend half of libpq interface) # # 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 -OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o md5.o pqcomm.o \ - pqformat.o pqsignal.o +OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \ + pqformat.o pqsignal.o all: SUBSYS.o diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index a582ce723e..247b80ba25 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * 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"; +#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, - "No pg_hba.conf entry for host %s, user %s, database %s", - hostinfo, port->user, port->database); + "No pg_hba.conf entry for host %s, user %s, database %s", + hostinfo, port->user, port->database); break; } diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 185093fb63..b9f177c720 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * 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) { - struct in_addr file_ip_addr, - mask; + SockAddr file_ip_addr, mask; if (strcmp(token, "hostssl") == 0) { @@ -623,7 +622,8 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) if (!line) goto hba_syntax; token = lfirst(line); - if (!inet_aton(token, &file_ip_addr)) + + if(SockAddr_pton(&file_ip_addr, token) < 0) goto hba_syntax; /* Read the mask field. */ @@ -631,7 +631,11 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) if (!line) goto hba_syntax; 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; /* 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; /* Must meet network restrictions */ - if (port->raddr.sa.sa_family != AF_INET || - ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) + if (!isAF_INETx(port->raddr.sa.sa_family) || + !rangeSockAddr(&port->raddr, &file_ip_addr, &mask)) return; } else diff --git a/src/backend/libpq/ip.c b/src/backend/libpq/ip.c new file mode 100644 index 0000000000..21f5c2fe1d --- /dev/null +++ b/src/backend/libpq/ip.c @@ -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 , Linux Based Systems Design + * http://www.lbsd.net. + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#include + +#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 diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 5338c79104..1116debbc1 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -44,5 +44,6 @@ # TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD -local all all trust -host all all 127.0.0.1 255.255.255.255 trust +local all all trust +host all all 127.0.0.1 255.255.255.255 trust +host all all ::1 ffff:ffff:ffff:ffff:ffff:ffff trust diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index c3a95562b1..904afb3b8f 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -29,7 +29,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * 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); +#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 @@ -182,149 +194,132 @@ int StreamServerPort(int family, char *hostName, unsigned short portNumber, char *unixSocketName, int *fdP) { - SockAddr saddr; int fd, err; int maxconn; - size_t len = 0; 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); - 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; } - 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, 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; } } - MemSet((char *) &saddr, 0, sizeof(saddr)); - saddr.sa.sa_family = family; - -#ifdef HAVE_UNIX_SOCKETS - if (family == AF_UNIX) - { - 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); +#ifdef HAVE_IPV6 + Assert(addrs->ai_next == NULL && addrs->ai_family == family); + err = bind(fd, addrs->ai_addr, addrs->ai_addrlen); +#else + err = bind(fd, (struct sockaddr *) &saddr.sa, len); +#endif 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) - elog(LOG, "StreamServerPort: bind() failed: %m\n" - "\tIs another postmaster already running on port %d?\n" - "\tIf not, remove socket node (%s) and retry.", - (int) portNumber, sock_path); + elog(LOG, "\tIf not, remove socket node (%s) and retry.", + sock_path); else - elog(LOG, "StreamServerPort: bind() failed: %m\n" - "\tIs another postmaster already running on port %d?\n" - "\tIf not, wait a few seconds and retry.", - (int) portNumber); + elog(LOG, "\tIf not, wait a few seconds and retry."); + FREEADDRINFO2(hint.ai_family, addrs); return STATUS_ERROR; } #ifdef HAVE_UNIX_SOCKETS if (family == AF_UNIX) { - /* 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') + if (Setup_AF_UNIX() != STATUS_OK) { - 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, "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); + FREEADDRINFO2(hint.ai_family, addrs); return STATUS_ERROR; } } -#endif /* HAVE_UNIX_SOCKETS */ +#endif /* * 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); 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; } *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; } + +/* + * 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 * server port. @@ -365,7 +450,7 @@ StreamConnection(int server_fd, Port *port) /* accept connection (and fill in the client (remote) address) */ addrlen = sizeof(port->raddr); if ((port->sock = accept(server_fd, - (struct sockaddr *) & port->raddr, + (struct sockaddr *) &port->raddr, &addrlen)) < 0) { elog(LOG, "StreamConnection: accept() failed: %m"); @@ -373,7 +458,6 @@ StreamConnection(int server_fd, Port *port) } #ifdef SCO_ACCEPT_BUG - /* * 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. @@ -392,7 +476,7 @@ StreamConnection(int server_fd, Port *port) } /* 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; @@ -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 * 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 * 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. * * NOTE: this routine does not do any character set conversion, diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index c1a25645a4..e237d00ca7 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * 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 * @@ -669,15 +669,30 @@ PostmasterMain(int argc, char *argv[]) */ 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, UnixSocketDir, &ServerSock_INET); if (status != STATUS_OK) { - postmaster_error("cannot create INET stream port"); - ExitPostmaster(1); + elog(LOG, "IPv6 support disabled --- perhaps the kernel does not support IPv6"); +#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 @@ -2091,13 +2106,19 @@ DoBackend(Port *port) /* * 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; 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); - host_addr = inet_ntoa(port->raddr.in.sin_addr); + host_addr = SockAddr_ntop(&port->raddr, ip_hostinfo, + sizeof(ip_hostinfo), 1); remote_host = NULL; diff --git a/src/include/libpq/ip.h b/src/include/libpq/ip.h new file mode 100644 index 0000000000..c9d1b0e466 --- /dev/null +++ b/src/include/libpq/ip.h @@ -0,0 +1,29 @@ +#ifndef IP_H +#define IP_H +#include +#include +#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 */ diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h index ced71326ce..0e8ecad246 100644 --- a/src/include/libpq/libpq.h +++ b/src/include/libpq/libpq.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * 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 "libpq/libpq-be.h" +#include "libpq/ip.h" /* ---------------- * PQArgBlock diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h index 1375852a0c..88f2d08bbc 100644 --- a/src/include/libpq/pqcomm.h +++ b/src/include/libpq/pqcomm.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * 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_in in; +#ifdef HAVE_IPV6 + struct sockaddr_in6 in6; +#endif struct sockaddr_un un; } SockAddr; diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index e666899961..447a6c7a89 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -8,7 +8,7 @@ * 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. * - * $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 @@ -368,6 +368,9 @@ /* Set to 1 if you have */ #undef HAVE_SYS_SHM_H +/* Set to 1 if you have for IPv6 */ +#undef HAVE_IPV6 + /* Set to 1 if you have */ #undef HAVE_KERNEL_OS_H diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index 4a31afb3d8..2fbab37e16 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -4,7 +4,7 @@ # # 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)"' 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 \ - wchar.o encnames.o \ + pqexpbuffer.o dllist.o pqsignal.o fe-secure.o wchar.o encnames.o ip.o \ + md5.o \ $(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 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 # compile with appropriate options to build a shared lib, we can't # 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 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 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 659114f999..8ce8f19780 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * 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 #endif +#include "libpq/ip.h" + + #ifndef HAVE_STRDUP #include "strdup.h" #endif @@ -48,6 +51,14 @@ #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 static int inet_aton(const char *cp, struct in_addr * inp) @@ -784,19 +795,32 @@ connectFailureMessage(PGconn *conn, int errorno) static int connectDBStart(PGconn *conn) { - int portno, - family; - + int portnum; + int sockfd; + char portstr[64]; #ifdef USE_SSL StartupPacket np; /* Used to negotiate SSL connection */ char SSLok; #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) return 0; #ifdef NOT_USED - /* * 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)); + /* + * 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') { +#ifdef HAVE_IPV6 + node = conn->pghostaddr; + hint.ai_family = AF_UNSPEC; +#else /* Using pghostaddr avoids a hostname lookup */ - /* Note that this supports IPv4 only */ struct in_addr addr; if (!inet_aton(conn->pghostaddr, &addr)) @@ -833,120 +873,158 @@ connectDBStart(PGconn *conn) memmove((char *) &(conn->raddr.in.sin_addr), (char *) &addr, sizeof(addr)); +#endif } 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 */ - struct hostent *hp; - - hp = gethostbyname(conn->pghost); - if ((hp == NULL) || (hp->h_addrtype != AF_INET)) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unknown host name: %s\n"), - conn->pghost); + if (getaddrinfo2(conn->pghost, portstr, family, &conn->raddr) != 0) goto connect_errReturn; - } + family = AF_INET; - - 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); +#endif } #ifdef HAVE_UNIX_SOCKETS 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); + StrNCpy(portstr, conn->raddr.un.sun_path, sizeof(portstr)); #ifdef USE_SSL /* Don't bother requesting SSL over a Unix socket */ conn->allow_ssl_try = false; conn->require_ssl = false; #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 - /* Open a socket */ - if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0) + do + { +#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, libpq_gettext("could not create socket: %s\n"), SOCK_STRERROR(SOCK_ERRNO)); 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 { - /* We're connected already */ - conn->status = CONNECTION_MADE; +#ifdef HAVE_IPV6 + 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 @@ -1014,7 +1092,6 @@ retry2: } #endif - /* * This makes the connection non-blocking, for all those cases which * forced us not to do it above. @@ -1038,7 +1115,10 @@ connect_errReturn: conn->sock = -1; } conn->status = CONNECTION_BAD; - +#ifdef HAVE_IPV6 + if (addrs != NULL) + FREEADDRINFO2(hint.ai_family, addrs); +#endif return 0; }