699f87
From c0af30b47db023b87c4eb6b55190a8c081697241 Mon Sep 17 00:00:00 2001
699f87
From: Tom Lane <tgl@sss.pgh.pa.us>
699f87
Date: Fri, 7 Sep 2012 16:02:23 -0400
699f87
Subject: [PATCH 1/4] Centralize libpq's low-level code for dropping a
699f87
 connection.
699f87
699f87
Create an internal function pqDropConnection that does the physical socket
699f87
close and cleans up closely-associated state.  This removes a bunch of ad
699f87
hoc, not always consistent closure code.  The ulterior motive is to have a
699f87
single place to wait for a spawned child backend to exit, but this seems
699f87
like good cleanup even if that never happens.
699f87
699f87
I went back and forth on whether to include "conn->status = CONNECTION_BAD"
699f87
in pqDropConnection's actions, but for the moment decided not to.  Only a
699f87
minority of the call sites actually want that, and in any case it's
699f87
arguable that conn->status is slightly higher-level state, and thus not
699f87
part of this function's purview.
699f87
699f87
Upstream commit: 210eb9b743c0645df05e5c8be4490ba4f09fc871
699f87
---
699f87
 src/interfaces/libpq/fe-connect.c   | 95 +++++++++++------------------
699f87
 src/interfaces/libpq/fe-misc.c      |  5 +-
699f87
 src/interfaces/libpq/fe-protocol3.c |  4 +-
699f87
 src/interfaces/libpq/libpq-int.h    |  1 +
699f87
 4 files changed, 38 insertions(+), 67 deletions(-)
699f87
699f87
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
699f87
index 28b96a6..6985118 100644
699f87
--- a/src/interfaces/libpq/fe-connect.c
699f87
+++ b/src/interfaces/libpq/fe-connect.c
699f87
@@ -363,6 +363,28 @@ static void default_threadlock(int acquire);
699f87
 pgthreadlock_t pg_g_threadlock = default_threadlock;
699f87
 
699f87
 
699f87
+/*
699f87
+ *		pqDropConnection
699f87
+ *
699f87
+ * Close any physical connection to the server, and reset associated
699f87
+ * state inside the connection object.  We don't release state that
699f87
+ * would be needed to reconnect, though.
699f87
+ */
699f87
+void
699f87
+pqDropConnection(PGconn *conn)
699f87
+{
699f87
+	/* Drop any SSL state */
699f87
+	pqsecure_close(conn);
699f87
+	/* Close the socket itself */
699f87
+	if (conn->sock >= 0)
699f87
+		closesocket(conn->sock);
699f87
+	conn->sock = -1;
699f87
+	/* Discard any unread/unsent data */
699f87
+	conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
+	conn->outCount = 0;
699f87
+}
699f87
+
699f87
+
699f87
 /*
699f87
  *		Connecting to a Database
699f87
  *
699f87
@@ -1477,12 +1499,7 @@ connectDBStart(PGconn *conn)
699f87
 		return 1;
699f87
 
699f87
 connect_errReturn:
699f87
-	if (conn->sock >= 0)
699f87
-	{
699f87
-		pqsecure_close(conn);
699f87
-		closesocket(conn->sock);
699f87
-		conn->sock = -1;
699f87
-	}
699f87
+	pqDropConnection(conn);
699f87
 	conn->status = CONNECTION_BAD;
699f87
 	return 0;
699f87
 }
699f87
@@ -1720,8 +1737,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						if (!connectNoDelay(conn))
699f87
 						{
699f87
-							closesocket(conn->sock);
699f87
-							conn->sock = -1;
699f87
+							pqDropConnection(conn);
699f87
 							conn->addr_cur = addr_cur->ai_next;
699f87
 							continue;
699f87
 						}
699f87
@@ -1731,8 +1747,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to non-blocking mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->addr_cur = addr_cur->ai_next;
699f87
 						continue;
699f87
 					}
699f87
@@ -1743,8 +1758,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->addr_cur = addr_cur->ai_next;
699f87
 						continue;
699f87
 					}
699f87
@@ -1792,8 +1806,7 @@ keep_going:						/* We will come back to here until there is
699f87
 
699f87
 						if (err)
699f87
 						{
699f87
-							closesocket(conn->sock);
699f87
-							conn->sock = -1;
699f87
+							pqDropConnection(conn);
699f87
 							conn->addr_cur = addr_cur->ai_next;
699f87
 							continue;
699f87
 						}
699f87
@@ -1880,11 +1893,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * failure and keep going if there are more addresses.
699f87
 					 */
699f87
 					connectFailureMessage(conn, SOCK_ERRNO);
699f87
-					if (conn->sock >= 0)
699f87
-					{
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
-					}
699f87
+					pqDropConnection(conn);
699f87
 
699f87
 					/*
699f87
 					 * Try the next address, if any.
699f87
@@ -1929,6 +1938,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * error message.
699f87
 					 */
699f87
 					connectFailureMessage(conn, optval);
699f87
+					pqDropConnection(conn);
699f87
 
699f87
 					/*
699f87
 					 * If more addresses remain, keep trying, just as in the
699f87
@@ -1936,11 +1946,6 @@ keep_going:						/* We will come back to here until there is
699f87
 					 */
699f87
 					if (conn->addr_cur->ai_next != NULL)
699f87
 					{
699f87
-						if (conn->sock >= 0)
699f87
-						{
699f87
-							closesocket(conn->sock);
699f87
-							conn->sock = -1;
699f87
-						}
699f87
 						conn->addr_cur = conn->addr_cur->ai_next;
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
 						goto keep_going;
699f87
@@ -2215,12 +2220,8 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
-						/* Discard any unread/unsent data */
699f87
-						conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-						conn->outCount = 0;
699f87
 						goto keep_going;
699f87
 					}
699f87
 				}
699f87
@@ -2330,13 +2331,8 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						conn->pversion = PG_PROTOCOL(2, 0);
699f87
 						/* Must drop the old connection */
699f87
-						pqsecure_close(conn);
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
-						/* Discard any unread/unsent data */
699f87
-						conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-						conn->outCount = 0;
699f87
 						goto keep_going;
699f87
 					}
699f87
 
699f87
@@ -2401,12 +2397,8 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->wait_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
-						/* Discard any unread/unsent data */
699f87
-						conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-						conn->outCount = 0;
699f87
 						goto keep_going;
699f87
 					}
699f87
 
699f87
@@ -2421,13 +2413,8 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						pqsecure_close(conn);
699f87
-						closesocket(conn->sock);
699f87
-						conn->sock = -1;
699f87
+						pqDropConnection(conn);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
-						/* Discard any unread/unsent data */
699f87
-						conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-						conn->outCount = 0;
699f87
 						goto keep_going;
699f87
 					}
699f87
 #endif
699f87
@@ -2587,13 +2574,8 @@ keep_going:						/* We will come back to here until there is
699f87
 							PQclear(res);
699f87
 							conn->send_appname = false;
699f87
 							/* Must drop the old connection */
699f87
-							pqsecure_close(conn);
699f87
-							closesocket(conn->sock);
699f87
-							conn->sock = -1;
699f87
+							pqDropConnection(conn);
699f87
 							conn->status = CONNECTION_NEEDED;
699f87
-							/* Discard any unread/unsent data */
699f87
-							conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-							conn->outCount = 0;
699f87
 							goto keep_going;
699f87
 						}
699f87
 					}
699f87
@@ -2987,12 +2969,7 @@ closePGconn(PGconn *conn)
699f87
 	/*
699f87
 	 * Close the connection, reset all transient state, flush I/O buffers.
699f87
 	 */
699f87
-	if (conn->sock >= 0)
699f87
-	{
699f87
-		pqsecure_close(conn);
699f87
-		closesocket(conn->sock);
699f87
-	}
699f87
-	conn->sock = -1;
699f87
+	pqDropConnection(conn);
699f87
 	conn->status = CONNECTION_BAD;		/* Well, not really _bad_ - just
699f87
 										 * absent */
699f87
 	conn->asyncStatus = PGASYNC_IDLE;
699f87
@@ -3022,8 +2999,6 @@ closePGconn(PGconn *conn)
699f87
 	if (conn->lobjfuncs)
699f87
 		free(conn->lobjfuncs);
699f87
 	conn->lobjfuncs = NULL;
699f87
-	conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
-	conn->outCount = 0;
699f87
 #ifdef ENABLE_GSS
699f87
 	{
699f87
 		OM_uint32	min_s;
699f87
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
699f87
index 8b0d8ef..c1c5c75 100644
699f87
--- a/src/interfaces/libpq/fe-misc.c
699f87
+++ b/src/interfaces/libpq/fe-misc.c
699f87
@@ -815,11 +815,8 @@ definitelyEOF:
699f87
 
699f87
 	/* Come here if lower-level code already set a suitable errorMessage */
699f87
 definitelyFailed:
699f87
+	pqDropConnection(conn);
699f87
 	conn->status = CONNECTION_BAD;		/* No more connection to backend */
699f87
-	pqsecure_close(conn);
699f87
-	closesocket(conn->sock);
699f87
-	conn->sock = -1;
699f87
-
699f87
 	return -1;
699f87
 }
699f87
 
699f87
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
699f87
index f997257..b130b4c 100644
699f87
--- a/src/interfaces/libpq/fe-protocol3.c
699f87
+++ b/src/interfaces/libpq/fe-protocol3.c
699f87
@@ -457,9 +457,7 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
699f87
 	pqSaveErrorResult(conn);
699f87
 	conn->asyncStatus = PGASYNC_READY;	/* drop out of GetResult wait loop */
699f87
 
699f87
-	pqsecure_close(conn);
699f87
-	closesocket(conn->sock);
699f87
-	conn->sock = -1;
699f87
+	pqDropConnection(conn);
699f87
 	conn->status = CONNECTION_BAD;		/* No more connection to backend */
699f87
 }
699f87
 
699f87
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
699f87
index bc2be3c..70b956b 100644
699f87
--- a/src/interfaces/libpq/libpq-int.h
699f87
+++ b/src/interfaces/libpq/libpq-int.h
699f87
@@ -489,6 +489,7 @@ extern char *const pgresStatus[];
699f87
 
699f87
 /* === in fe-connect.c === */
699f87
 
699f87
+extern void pqDropConnection(PGconn *conn);
699f87
 extern int pqPacketSend(PGconn *conn, char pack_type,
699f87
 			 const void *buf, size_t buf_len);
699f87
 extern bool pqGetHomeDirectory(char *buf, int bufsize);
699f87
-- 
699f87
2.17.1
699f87
699f87
699f87
From 299e76cdeebf150f51cf29fa4269ae02cf6e7f24 Mon Sep 17 00:00:00 2001
699f87
From: Tom Lane <tgl@sss.pgh.pa.us>
699f87
Date: Thu, 12 Nov 2015 13:03:53 -0500
699f87
Subject: [PATCH 2/4] Fix unwanted flushing of libpq's input buffer when socket
699f87
 EOF is seen.
699f87
699f87
In commit 210eb9b743c0645d I centralized libpq's logic for closing down
699f87
the backend communication socket, and made the new pqDropConnection
699f87
routine always reset the I/O buffers to empty.  Many of the call sites
699f87
previously had not had such code, and while that amounted to an oversight
699f87
in some cases, there was one place where it was intentional and necessary
699f87
*not* to flush the input buffer: pqReadData should never cause that to
699f87
happen, since we probably still want to process whatever data we read.
699f87
699f87
This is the true cause of the problem Robert was attempting to fix in
699f87
c3e7c24a1d60dc6a, namely that libpq no longer reported the backend's final
699f87
ERROR message before reporting "server closed the connection unexpectedly".
699f87
But that only accidentally fixed it, by invoking parseInput before the
699f87
input buffer got flushed; and very likely there are timing scenarios
699f87
where we'd still lose the message before processing it.
699f87
699f87
To fix, pass a flag to pqDropConnection to tell it whether to flush the
699f87
input buffer or not.  On review I think flushing is actually correct for
699f87
every other call site.
699f87
699f87
Back-patch to 9.3 where the problem was introduced.  In HEAD, also improve
699f87
the comments added by c3e7c24a1d60dc6a.
699f87
699f87
Upstream commit: db6e8e1624a8f0357373450136c850f2b6e7fc8a
699f87
---
699f87
 src/interfaces/libpq/fe-connect.c   | 38 +++++++++++++++++------------
699f87
 src/interfaces/libpq/fe-misc.c      |  3 ++-
699f87
 src/interfaces/libpq/fe-protocol3.c |  4 +--
699f87
 src/interfaces/libpq/libpq-int.h    |  2 +-
699f87
 4 files changed, 27 insertions(+), 20 deletions(-)
699f87
699f87
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
699f87
index 6985118..c22901d 100644
699f87
--- a/src/interfaces/libpq/fe-connect.c
699f87
+++ b/src/interfaces/libpq/fe-connect.c
699f87
@@ -369,9 +369,13 @@ pgthreadlock_t pg_g_threadlock = default_threadlock;
699f87
  * Close any physical connection to the server, and reset associated
699f87
  * state inside the connection object.  We don't release state that
699f87
  * would be needed to reconnect, though.
699f87
+ *
699f87
+ * We can always flush the output buffer, since there's no longer any hope
699f87
+ * of sending that data.  However, unprocessed input data might still be
699f87
+ * valuable, so the caller must tell us whether to flush that or not.
699f87
  */
699f87
 void
699f87
-pqDropConnection(PGconn *conn)
699f87
+pqDropConnection(PGconn *conn, bool flushInput)
699f87
 {
699f87
 	/* Drop any SSL state */
699f87
 	pqsecure_close(conn);
699f87
@@ -379,8 +383,10 @@ pqDropConnection(PGconn *conn)
699f87
 	if (conn->sock >= 0)
699f87
 		closesocket(conn->sock);
699f87
 	conn->sock = -1;
699f87
-	/* Discard any unread/unsent data */
699f87
-	conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
+	/* Optionally discard any unread data */
699f87
+	if (flushInput)
699f87
+		conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
+	/* Always discard any unsent data */
699f87
 	conn->outCount = 0;
699f87
 }
699f87
 
699f87
@@ -1499,7 +1505,7 @@ connectDBStart(PGconn *conn)
699f87
 		return 1;
699f87
 
699f87
 connect_errReturn:
699f87
-	pqDropConnection(conn);
699f87
+	pqDropConnection(conn, true);
699f87
 	conn->status = CONNECTION_BAD;
699f87
 	return 0;
699f87
 }
699f87
@@ -1737,7 +1743,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						if (!connectNoDelay(conn))
699f87
 						{
699f87
-							pqDropConnection(conn);
699f87
+							pqDropConnection(conn, true);
699f87
 							conn->addr_cur = addr_cur->ai_next;
699f87
 							continue;
699f87
 						}
699f87
@@ -1747,7 +1753,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to non-blocking mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->addr_cur = addr_cur->ai_next;
699f87
 						continue;
699f87
 					}
699f87
@@ -1758,7 +1764,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->addr_cur = addr_cur->ai_next;
699f87
 						continue;
699f87
 					}
699f87
@@ -1806,7 +1812,7 @@ keep_going:						/* We will come back to here until there is
699f87
 
699f87
 						if (err)
699f87
 						{
699f87
-							pqDropConnection(conn);
699f87
+							pqDropConnection(conn, true);
699f87
 							conn->addr_cur = addr_cur->ai_next;
699f87
 							continue;
699f87
 						}
699f87
@@ -1893,7 +1899,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * failure and keep going if there are more addresses.
699f87
 					 */
699f87
 					connectFailureMessage(conn, SOCK_ERRNO);
699f87
-					pqDropConnection(conn);
699f87
+					pqDropConnection(conn, true);
699f87
 
699f87
 					/*
699f87
 					 * Try the next address, if any.
699f87
@@ -1938,7 +1944,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * error message.
699f87
 					 */
699f87
 					connectFailureMessage(conn, optval);
699f87
-					pqDropConnection(conn);
699f87
+					pqDropConnection(conn, true);
699f87
 
699f87
 					/*
699f87
 					 * If more addresses remain, keep trying, just as in the
699f87
@@ -2220,7 +2226,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
 						goto keep_going;
699f87
 					}
699f87
@@ -2331,7 +2337,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						conn->pversion = PG_PROTOCOL(2, 0);
699f87
 						/* Must drop the old connection */
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
 						goto keep_going;
699f87
 					}
699f87
@@ -2397,7 +2403,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->wait_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
 						goto keep_going;
699f87
 					}
699f87
@@ -2413,7 +2419,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
 						/* Must drop the old connection */
699f87
-						pqDropConnection(conn);
699f87
+						pqDropConnection(conn, true);
699f87
 						conn->status = CONNECTION_NEEDED;
699f87
 						goto keep_going;
699f87
 					}
699f87
@@ -2574,7 +2580,7 @@ keep_going:						/* We will come back to here until there is
699f87
 							PQclear(res);
699f87
 							conn->send_appname = false;
699f87
 							/* Must drop the old connection */
699f87
-							pqDropConnection(conn);
699f87
+							pqDropConnection(conn, true);
699f87
 							conn->status = CONNECTION_NEEDED;
699f87
 							goto keep_going;
699f87
 						}
699f87
@@ -2969,7 +2975,7 @@ closePGconn(PGconn *conn)
699f87
 	/*
699f87
 	 * Close the connection, reset all transient state, flush I/O buffers.
699f87
 	 */
699f87
-	pqDropConnection(conn);
699f87
+	pqDropConnection(conn, true);
699f87
 	conn->status = CONNECTION_BAD;		/* Well, not really _bad_ - just
699f87
 										 * absent */
699f87
 	conn->asyncStatus = PGASYNC_IDLE;
699f87
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
699f87
index c1c5c75..58c9ce0 100644
699f87
--- a/src/interfaces/libpq/fe-misc.c
699f87
+++ b/src/interfaces/libpq/fe-misc.c
699f87
@@ -815,7 +815,8 @@ definitelyEOF:
699f87
 
699f87
 	/* Come here if lower-level code already set a suitable errorMessage */
699f87
 definitelyFailed:
699f87
-	pqDropConnection(conn);
699f87
+	/* Do *not* drop any already-read data; caller still wants it */
699f87
+	pqDropConnection(conn, false);
699f87
 	conn->status = CONNECTION_BAD;		/* No more connection to backend */
699f87
 	return -1;
699f87
 }
699f87
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
699f87
index b130b4c..88ff74a 100644
699f87
--- a/src/interfaces/libpq/fe-protocol3.c
699f87
+++ b/src/interfaces/libpq/fe-protocol3.c
699f87
@@ -456,8 +456,8 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
699f87
 	/* build an error result holding the error message */
699f87
 	pqSaveErrorResult(conn);
699f87
 	conn->asyncStatus = PGASYNC_READY;	/* drop out of GetResult wait loop */
699f87
-
699f87
-	pqDropConnection(conn);
699f87
+	/* flush input data since we're giving up on processing it */
699f87
+	pqDropConnection(conn, true);
699f87
 	conn->status = CONNECTION_BAD;		/* No more connection to backend */
699f87
 }
699f87
 
699f87
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
699f87
index 70b956b..8843ccb 100644
699f87
--- a/src/interfaces/libpq/libpq-int.h
699f87
+++ b/src/interfaces/libpq/libpq-int.h
699f87
@@ -489,7 +489,7 @@ extern char *const pgresStatus[];
699f87
 
699f87
 /* === in fe-connect.c === */
699f87
 
699f87
-extern void pqDropConnection(PGconn *conn);
699f87
+extern void pqDropConnection(PGconn *conn, bool flushInput);
699f87
 extern int pqPacketSend(PGconn *conn, char pack_type,
699f87
 			 const void *buf, size_t buf_len);
699f87
 extern bool pqGetHomeDirectory(char *buf, int bufsize);
699f87
-- 
699f87
2.17.1
699f87
699f87
699f87
From 3b08b525a118be43a334045409b1bad9cfaeb438 Mon Sep 17 00:00:00 2001
699f87
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
699f87
Date: Wed, 7 Jun 2017 14:01:46 +0300
699f87
Subject: [PATCH 3/4] Clear auth context correctly when re-connecting after
699f87
 failed auth attempt.
699f87
699f87
If authentication over an SSL connection fails, with sslmode=prefer,
699f87
libpq will reconnect without SSL and retry. However, we did not clear
699f87
the variables related to GSS, SSPI, and SASL authentication state, when
699f87
reconnecting. Because of that, the second authentication attempt would
699f87
always fail with a "duplicate GSS/SASL authentication request" error.
699f87
pg_SSPI_startup did not check for duplicate authentication requests like
699f87
the corresponding GSS and SASL functions, so with SSPI, you would leak
699f87
some memory instead.
699f87
699f87
Another way this could manifest itself, on version 10, is if you list
699f87
multiple hostnames in the "host" parameter. If the first server requests
699f87
Kerberos or SCRAM authentication, but it fails, the attempts to connect to
699f87
the other servers will also fail with "duplicate authentication request"
699f87
errors.
699f87
699f87
To fix, move the clearing of authentication state from closePGconn to
699f87
pgDropConnection, so that it is cleared also when re-connecting.
699f87
699f87
Patch by Michael Paquier, with some kibitzing by me.
699f87
699f87
Backpatch down to 9.3. 9.2 has the same bug, but the code around closing
699f87
the connection is somewhat different, so that this patch doesn't apply.
699f87
To fix this in 9.2, I think we would need to back-port commit 210eb9b743
699f87
first, and then apply this patch. However, given that we only bumped into
699f87
this in our own testing, we haven't heard any reports from users about
699f87
this, and that 9.2 will be end-of-lifed in a couple of months anyway, it
699f87
doesn't seem worth the risk and trouble.
699f87
699f87
Discussion: https://www.postgresql.org/message-id/CAB7nPqRuOUm0MyJaUy9L3eXYJU3AKCZ-0-03=-aDTZJGV4GyWw@mail.gmail.com
699f87
699f87
Upstream commit: f2fa0c6514b6c5b7bccfe5050f6791dea1113c2e
699f87
---
699f87
 src/interfaces/libpq/fe-auth.c    |  7 ++-
699f87
 src/interfaces/libpq/fe-connect.c | 76 +++++++++++++++++--------------
699f87
 2 files changed, 47 insertions(+), 36 deletions(-)
699f87
699f87
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
699f87
index d10bd9f..ee961d9 100644
699f87
--- a/src/interfaces/libpq/fe-auth.c
699f87
+++ b/src/interfaces/libpq/fe-auth.c
699f87
@@ -618,7 +618,12 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate)
699f87
 	SECURITY_STATUS r;
699f87
 	TimeStamp	expire;
699f87
 
699f87
-	conn->sspictx = NULL;
699f87
+	if (conn->sspictx)
699f87
+	{
699f87
+		printfPQExpBuffer(&conn->errorMessage,
699f87
+					libpq_gettext("duplicate SSPI authentication request\n"));
699f87
+		return STATUS_ERROR;
699f87
+	}
699f87
 
699f87
 	/*
699f87
 	 * Retreive credentials handle
699f87
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
699f87
index c22901d..6dbcbc6 100644
699f87
--- a/src/interfaces/libpq/fe-connect.c
699f87
+++ b/src/interfaces/libpq/fe-connect.c
699f87
@@ -379,15 +379,56 @@ pqDropConnection(PGconn *conn, bool flushInput)
699f87
 {
699f87
 	/* Drop any SSL state */
699f87
 	pqsecure_close(conn);
699f87
+
699f87
 	/* Close the socket itself */
699f87
 	if (conn->sock >= 0)
699f87
 		closesocket(conn->sock);
699f87
 	conn->sock = -1;
699f87
+
699f87
 	/* Optionally discard any unread data */
699f87
 	if (flushInput)
699f87
 		conn->inStart = conn->inCursor = conn->inEnd = 0;
699f87
+
699f87
 	/* Always discard any unsent data */
699f87
 	conn->outCount = 0;
699f87
+
699f87
+	/* Free authentication state */
699f87
+#ifdef ENABLE_GSS
699f87
+	{
699f87
+		OM_uint32	min_s;
699f87
+
699f87
+		if (conn->gctx)
699f87
+			gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
699f87
+		if (conn->gtarg_nam)
699f87
+			gss_release_name(&min_s, &conn->gtarg_nam);
699f87
+		if (conn->ginbuf.length)
699f87
+			gss_release_buffer(&min_s, &conn->ginbuf);
699f87
+		if (conn->goutbuf.length)
699f87
+			gss_release_buffer(&min_s, &conn->goutbuf);
699f87
+	}
699f87
+#endif
699f87
+#ifdef ENABLE_SSPI
699f87
+	if (conn->ginbuf.length)
699f87
+		free(conn->ginbuf.value);
699f87
+	conn->ginbuf.length = 0;
699f87
+	conn->ginbuf.value = NULL;
699f87
+	if (conn->sspitarget)
699f87
+		free(conn->sspitarget);
699f87
+	conn->sspitarget = NULL;
699f87
+	if (conn->sspicred)
699f87
+	{
699f87
+		FreeCredentialsHandle(conn->sspicred);
699f87
+		free(conn->sspicred);
699f87
+		conn->sspicred = NULL;
699f87
+	}
699f87
+	if (conn->sspictx)
699f87
+	{
699f87
+		DeleteSecurityContext(conn->sspictx);
699f87
+		free(conn->sspictx);
699f87
+		conn->sspictx = NULL;
699f87
+	}
699f87
+	conn->usesspi = 0;
699f87
+#endif
699f87
 }
699f87
 
699f87
 
699f87
@@ -3005,41 +3046,6 @@ closePGconn(PGconn *conn)
699f87
 	if (conn->lobjfuncs)
699f87
 		free(conn->lobjfuncs);
699f87
 	conn->lobjfuncs = NULL;
699f87
-#ifdef ENABLE_GSS
699f87
-	{
699f87
-		OM_uint32	min_s;
699f87
-
699f87
-		if (conn->gctx)
699f87
-			gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
699f87
-		if (conn->gtarg_nam)
699f87
-			gss_release_name(&min_s, &conn->gtarg_nam);
699f87
-		if (conn->ginbuf.length)
699f87
-			gss_release_buffer(&min_s, &conn->ginbuf);
699f87
-		if (conn->goutbuf.length)
699f87
-			gss_release_buffer(&min_s, &conn->goutbuf);
699f87
-	}
699f87
-#endif
699f87
-#ifdef ENABLE_SSPI
699f87
-	if (conn->ginbuf.length)
699f87
-		free(conn->ginbuf.value);
699f87
-	conn->ginbuf.length = 0;
699f87
-	conn->ginbuf.value = NULL;
699f87
-	if (conn->sspitarget)
699f87
-		free(conn->sspitarget);
699f87
-	conn->sspitarget = NULL;
699f87
-	if (conn->sspicred)
699f87
-	{
699f87
-		FreeCredentialsHandle(conn->sspicred);
699f87
-		free(conn->sspicred);
699f87
-		conn->sspicred = NULL;
699f87
-	}
699f87
-	if (conn->sspictx)
699f87
-	{
699f87
-		DeleteSecurityContext(conn->sspictx);
699f87
-		free(conn->sspictx);
699f87
-		conn->sspictx = NULL;
699f87
-	}
699f87
-#endif
699f87
 }
699f87
 
699f87
 /*
699f87
-- 
699f87
2.17.1
699f87
699f87
699f87
From f25aa65f201df8925c39373aa10dfee19253d03e Mon Sep 17 00:00:00 2001
699f87
From: Tom Lane <tgl@sss.pgh.pa.us>
699f87
Date: Mon, 6 Aug 2018 10:53:35 -0400
699f87
Subject: [PATCH 4/4] Fix failure to reset libpq's state fully between
699f87
 connection attempts.
699f87
699f87
The logic in PQconnectPoll() did not take care to ensure that all of
699f87
a PGconn's internal state variables were reset before trying a new
699f87
connection attempt.  If we got far enough in the connection sequence
699f87
to have changed any of these variables, and then decided to try a new
699f87
server address or server name, the new connection might be completed
699f87
with some state that really only applied to the failed connection.
699f87
699f87
While this has assorted bad consequences, the only one that is clearly
699f87
a security issue is that password_needed didn't get reset, so that
699f87
if the first server asked for a password and the second didn't,
699f87
PQconnectionUsedPassword() would return an incorrect result.  This
699f87
could be leveraged by unprivileged users of dblink or postgres_fdw
699f87
to allow them to use server-side login credentials that they should
699f87
not be able to use.
699f87
699f87
Other notable problems include the possibility of forcing a v2-protocol
699f87
connection to a server capable of supporting v3, or overriding
699f87
"sslmode=prefer" to cause a non-encrypted connection to a server that
699f87
would have accepted an encrypted one.  Those are certainly bugs but
699f87
it's harder to paint them as security problems in themselves.  However,
699f87
forcing a v2-protocol connection could result in libpq having a wrong
699f87
idea of the server's standard_conforming_strings setting, which opens
699f87
the door to SQL-injection attacks.  The extent to which that's actually
699f87
a problem, given the prerequisite that the attacker needs control of
699f87
the client's connection parameters, is unclear.
699f87
699f87
These problems have existed for a long time, but became more easily
699f87
exploitable in v10, both because it introduced easy ways to force libpq
699f87
to abandon a connection attempt at a late stage and then try another one
699f87
(rather than just giving up), and because it provided an easy way to
699f87
specify multiple target hosts.
699f87
699f87
Fix by rearranging PQconnectPoll's state machine to provide centralized
699f87
places to reset state properly when moving to a new target host or when
699f87
dropping and retrying a connection to the same host.
699f87
699f87
Tom Lane, reviewed by Noah Misch.  Our thanks to Andrew Krasichkov
699f87
for finding and reporting the problem.
699f87
699f87
Security: CVE-2018-10915
699f87
699f87
Upstream commit: 243de06be96d6001d01f2ec7c4573aad8b657195
699f87
---
699f87
 src/interfaces/libpq/fe-connect.c | 301 +++++++++++++++++++-----------
699f87
 src/interfaces/libpq/libpq-int.h  |   2 +
699f87
 2 files changed, 196 insertions(+), 107 deletions(-)
699f87
699f87
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
699f87
index 6dbcbc6..ca06337 100644
699f87
--- a/src/interfaces/libpq/fe-connect.c
699f87
+++ b/src/interfaces/libpq/fe-connect.c
699f87
@@ -368,7 +368,8 @@ pgthreadlock_t pg_g_threadlock = default_threadlock;
699f87
  *
699f87
  * Close any physical connection to the server, and reset associated
699f87
  * state inside the connection object.  We don't release state that
699f87
- * would be needed to reconnect, though.
699f87
+ * would be needed to reconnect, though, nor local state that might still
699f87
+ * be useful later.
699f87
  *
699f87
  * We can always flush the output buffer, since there's no longer any hope
699f87
  * of sending that data.  However, unprocessed input data might still be
699f87
@@ -432,6 +433,64 @@ pqDropConnection(PGconn *conn, bool flushInput)
699f87
 }
699f87
 
699f87
 
699f87
+/*
699f87
+ *		pqDropServerData
699f87
+ *
699f87
+ * Clear all connection state data that was received from (or deduced about)
699f87
+ * the server.  This is essential to do between connection attempts to
699f87
+ * different servers, else we may incorrectly hold over some data from the
699f87
+ * old server.
699f87
+ *
699f87
+ * It would be better to merge this into pqDropConnection, perhaps, but
699f87
+ * right now we cannot because that function is called immediately on
699f87
+ * detection of connection loss (cf. pqReadData, for instance).  This data
699f87
+ * should be kept until we are actually starting a new connection.
699f87
+ */
699f87
+static void
699f87
+pqDropServerData(PGconn *conn)
699f87
+{
699f87
+	PGnotify   *notify;
699f87
+	pgParameterStatus *pstatus;
699f87
+
699f87
+	/* Forget pending notifies */
699f87
+	notify = conn->notifyHead;
699f87
+	while (notify != NULL)
699f87
+	{
699f87
+		PGnotify   *prev = notify;
699f87
+
699f87
+		notify = notify->next;
699f87
+		free(prev);
699f87
+	}
699f87
+	conn->notifyHead = conn->notifyTail = NULL;
699f87
+
699f87
+	/* Reset ParameterStatus data, as well as variables deduced from it */
699f87
+	pstatus = conn->pstatus;
699f87
+	while (pstatus != NULL)
699f87
+	{
699f87
+		pgParameterStatus *prev = pstatus;
699f87
+
699f87
+		pstatus = pstatus->next;
699f87
+		free(prev);
699f87
+	}
699f87
+	conn->pstatus = NULL;
699f87
+	conn->client_encoding = PG_SQL_ASCII;
699f87
+	conn->std_strings = false;
699f87
+	conn->sversion = 0;
699f87
+
699f87
+	/* Drop large-object lookup data */
699f87
+	if (conn->lobjfuncs)
699f87
+		free(conn->lobjfuncs);
699f87
+	conn->lobjfuncs = NULL;
699f87
+
699f87
+	/* Reset assorted other per-connection state */
699f87
+	conn->last_sqlstate[0] = '\0';
699f87
+	conn->auth_req_received = false;
699f87
+	conn->password_needed = false;
699f87
+	conn->be_pid = 0;
699f87
+	conn->be_key = 0;
699f87
+}
699f87
+
699f87
+
699f87
 /*
699f87
  *		Connecting to a Database
699f87
  *
699f87
@@ -1517,22 +1576,14 @@ connectDBStart(PGconn *conn)
699f87
 		goto connect_errReturn;
699f87
 	}
699f87
 
699f87
-#ifdef USE_SSL
699f87
-	/* setup values based on SSL mode */
699f87
-	if (conn->sslmode[0] == 'd')	/* "disable" */
699f87
-		conn->allow_ssl_try = false;
699f87
-	else if (conn->sslmode[0] == 'a')	/* "allow" */
699f87
-		conn->wait_ssl_try = true;
699f87
-#endif
699f87
-
699f87
 	/*
699f87
-	 * Set up to try to connect, with protocol 3.0 as the first attempt.
699f87
+	 * Set up to try to connect to the first address.
699f87
 	 */
699f87
 	conn->addrlist = addrs;
699f87
 	conn->addr_cur = addrs;
699f87
 	conn->addrlist_family = hint.ai_family;
699f87
-	conn->pversion = PG_PROTOCOL(3, 0);
699f87
-	conn->send_appname = true;
699f87
+	conn->try_next_addr = false;
699f87
+	conn->is_new_addr = true;
699f87
 	conn->status = CONNECTION_NEEDED;
699f87
 
699f87
 	/*
699f87
@@ -1546,6 +1597,12 @@ connectDBStart(PGconn *conn)
699f87
 		return 1;
699f87
 
699f87
 connect_errReturn:
699f87
+
699f87
+	/*
699f87
+	 * If we managed to open a socket, close it immediately rather than
699f87
+	 * waiting till PQfinish.  (The application cannot have gotten the socket
699f87
+	 * from PQsocket yet, so this doesn't risk breaking anything.)
699f87
+	 */
699f87
 	pqDropConnection(conn, true);
699f87
 	conn->status = CONNECTION_BAD;
699f87
 	return 0;
699f87
@@ -1607,6 +1664,7 @@ connectDBComplete(PGconn *conn)
699f87
 			case PGRES_POLLING_READING:
699f87
 				if (pqWaitTimed(1, 0, conn, finish_time))
699f87
 				{
699f87
+					/* hard failure, eg select() problem, aborts everything */
699f87
 					conn->status = CONNECTION_BAD;
699f87
 					return 0;
699f87
 				}
699f87
@@ -1615,6 +1673,7 @@ connectDBComplete(PGconn *conn)
699f87
 			case PGRES_POLLING_WRITING:
699f87
 				if (pqWaitTimed(0, 1, conn, finish_time))
699f87
 				{
699f87
+					/* hard failure, eg select() problem, aborts everything */
699f87
 					conn->status = CONNECTION_BAD;
699f87
 					return 0;
699f87
 				}
699f87
@@ -1663,6 +1722,7 @@ connectDBComplete(PGconn *conn)
699f87
 PostgresPollingStatusType
699f87
 PQconnectPoll(PGconn *conn)
699f87
 {
699f87
+	bool		need_new_connection = false;
699f87
 	PGresult   *res;
699f87
 	char		sebuf[256];
699f87
 	int			optval;
699f87
@@ -1723,6 +1783,69 @@ PQconnectPoll(PGconn *conn)
699f87
 
699f87
 keep_going:						/* We will come back to here until there is
699f87
 								 * nothing left to do. */
699f87
+
699f87
+	/* Time to advance to next address? */
699f87
+	if (conn->try_next_addr)
699f87
+	{
699f87
+		if (conn->addr_cur && conn->addr_cur->ai_next)
699f87
+		{
699f87
+			conn->addr_cur = conn->addr_cur->ai_next;
699f87
+			conn->is_new_addr = true;
699f87
+		}
699f87
+		else
699f87
+		{
699f87
+			/*
699f87
+			 * Oops, no more addresses.  An appropriate error message is
699f87
+			 * already set up, so just set the right status.
699f87
+			 */
699f87
+			goto error_return;
699f87
+		}
699f87
+		conn->try_next_addr = false;
699f87
+	}
699f87
+
699f87
+	/* Reset connection state machine? */
699f87
+	if (conn->is_new_addr)
699f87
+	{
699f87
+		/*
699f87
+		 * (Re) initialize our connection control variables for a set of
699f87
+		 * connection attempts to a single server address.  These variables
699f87
+		 * must persist across individual connection attempts, but we must
699f87
+		 * reset them when we start to consider a new address (since it might
699f87
+		 * not be the same server).
699f87
+		 */
699f87
+		conn->pversion = PG_PROTOCOL(3, 0);
699f87
+		conn->send_appname = true;
699f87
+#ifdef USE_SSL
699f87
+		/* initialize these values based on SSL mode */
699f87
+		conn->allow_ssl_try = (conn->sslmode[0] != 'd');		/* "disable" */
699f87
+		conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
699f87
+#endif
699f87
+
699f87
+		conn->is_new_addr = false;
699f87
+		need_new_connection = true;
699f87
+	}
699f87
+
699f87
+	/* Force a new connection (perhaps to the same server as before)? */
699f87
+	if (need_new_connection)
699f87
+	{
699f87
+		/* Drop any existing connection */
699f87
+		pqDropConnection(conn, true);
699f87
+
699f87
+		/* Reset all state obtained from old server */
699f87
+		pqDropServerData(conn);
699f87
+
699f87
+		/* Drop any PGresult we might have, too */
699f87
+		conn->asyncStatus = PGASYNC_IDLE;
699f87
+		conn->xactStatus = PQTRANS_IDLE;
699f87
+		pqClearAsyncResult(conn);
699f87
+
699f87
+		/* Reset conn->status to put the state machine in the right state */
699f87
+		conn->status = CONNECTION_NEEDED;
699f87
+
699f87
+		need_new_connection = false;
699f87
+	}
699f87
+
699f87
+	/* Now try to advance the state machine for this connection */
699f87
 	switch (conn->status)
699f87
 	{
699f87
 		case CONNECTION_NEEDED:
699f87
@@ -1730,12 +1853,24 @@ keep_going:						/* We will come back to here until there is
699f87
 				/*
699f87
 				 * Try to initiate a connection to one of the addresses
699f87
 				 * returned by pg_getaddrinfo_all().  conn->addr_cur is the
699f87
-				 * next one to try. We fail when we run out of addresses.
699f87
+				 * next one to try.
699f87
+				 *
699f87
+				 * The extra level of braces here is historical.  It's not
699f87
+				 * worth reindenting this whole switch case to remove 'em.
699f87
 				 */
699f87
-				while (conn->addr_cur != NULL)
699f87
 				{
699f87
 					struct addrinfo *addr_cur = conn->addr_cur;
699f87
 
699f87
+					if (addr_cur == NULL)
699f87
+					{
699f87
+						/*
699f87
+						 * Ooops, no more addresses.  An appropriate error
699f87
+						 * message is already set up, so just set the right
699f87
+						 * status.
699f87
+						 */
699f87
+						goto error_return;
699f87
+					}
699f87
+
699f87
 					/* Remember current address for possible error msg */
699f87
 					memcpy(&conn->raddr.addr, addr_cur->ai_addr,
699f87
 						   addr_cur->ai_addrlen);
699f87
@@ -1761,32 +1896,34 @@ keep_going:						/* We will come back to here until there is
699f87
 					if (conn->sock == -1)
699f87
 					{
699f87
 						/*
699f87
-						 * ignore socket() failure if we have more addresses
699f87
-						 * to try
699f87
+						 * Silently ignore socket() failure if we have more
699f87
+						 * addresses to try; this reduces useless chatter in
699f87
+						 * cases where the address list includes both IPv4 and
699f87
+						 * IPv6 but kernel only accepts one family.
699f87
 						 */
699f87
 						if (addr_cur->ai_next != NULL)
699f87
 						{
699f87
-							conn->addr_cur = addr_cur->ai_next;
699f87
-							continue;
699f87
+							conn->try_next_addr = true;
699f87
+							goto keep_going;
699f87
 						}
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 							  libpq_gettext("could not create socket: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						break;
699f87
+						goto error_return;
699f87
 					}
699f87
 
699f87
 					/*
699f87
 					 * Select socket options: no delay of outgoing data for
699f87
-					 * TCP sockets, nonblock mode, close-on-exec. Fail if any
699f87
-					 * of this fails.
699f87
+					 * TCP sockets, nonblock mode, close-on-exec.  Try the
699f87
+					 * next address if any of this fails.
699f87
 					 */
699f87
 					if (!IS_AF_UNIX(addr_cur->ai_family))
699f87
 					{
699f87
 						if (!connectNoDelay(conn))
699f87
 						{
699f87
-							pqDropConnection(conn, true);
699f87
-							conn->addr_cur = addr_cur->ai_next;
699f87
-							continue;
699f87
+							/* error message already created */
699f87
+							conn->try_next_addr = true;
699f87
+							goto keep_going;
699f87
 						}
699f87
 					}
699f87
 					if (!pg_set_noblock(conn->sock))
699f87
@@ -1794,9 +1931,8 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to non-blocking mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->addr_cur = addr_cur->ai_next;
699f87
-						continue;
699f87
+						conn->try_next_addr = true;
699f87
+						goto keep_going;
699f87
 					}
699f87
 
699f87
 #ifdef F_SETFD
699f87
@@ -1805,9 +1941,8 @@ keep_going:						/* We will come back to here until there is
699f87
 						appendPQExpBuffer(&conn->errorMessage,
699f87
 										  libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
699f87
 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->addr_cur = addr_cur->ai_next;
699f87
-						continue;
699f87
+						conn->try_next_addr = true;
699f87
+						goto keep_going;
699f87
 					}
699f87
 #endif   /* F_SETFD */
699f87
 
699f87
@@ -1853,9 +1988,8 @@ keep_going:						/* We will come back to here until there is
699f87
 
699f87
 						if (err)
699f87
 						{
699f87
-							pqDropConnection(conn, true);
699f87
-							conn->addr_cur = addr_cur->ai_next;
699f87
-							continue;
699f87
+							conn->try_next_addr = true;
699f87
+							goto keep_going;
699f87
 						}
699f87
 					}
699f87
 
699f87
@@ -1934,25 +2068,13 @@ keep_going:						/* We will come back to here until there is
699f87
 					}
699f87
 
699f87
 					/*
699f87
-					 * This connection failed --- set up error report, then
699f87
-					 * close socket (do it this way in case close() affects
699f87
-					 * the value of errno...).  We will ignore the connect()
699f87
-					 * failure and keep going if there are more addresses.
699f87
+					 * This connection failed.  Add the error report to
699f87
+					 * conn->errorMessage, then try the next address if any.
699f87
 					 */
699f87
 					connectFailureMessage(conn, SOCK_ERRNO);
699f87
-					pqDropConnection(conn, true);
699f87
-
699f87
-					/*
699f87
-					 * Try the next address, if any.
699f87
-					 */
699f87
-					conn->addr_cur = addr_cur->ai_next;
699f87
-				}				/* loop over addresses */
699f87
-
699f87
-				/*
699f87
-				 * Ooops, no more addresses.  An appropriate error message is
699f87
-				 * already set up, so just set the right status.
699f87
-				 */
699f87
-				goto error_return;
699f87
+					conn->try_next_addr = true;
699f87
+					goto keep_going;
699f87
+				}
699f87
 			}
699f87
 
699f87
 		case CONNECTION_STARTED:
699f87
@@ -1985,19 +2107,13 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * error message.
699f87
 					 */
699f87
 					connectFailureMessage(conn, optval);
699f87
-					pqDropConnection(conn, true);
699f87
 
699f87
 					/*
699f87
-					 * If more addresses remain, keep trying, just as in the
699f87
-					 * case where connect() returned failure immediately.
699f87
+					 * Try the next address if any, just as in the case where
699f87
+					 * connect() returned failure immediately.
699f87
 					 */
699f87
-					if (conn->addr_cur->ai_next != NULL)
699f87
-					{
699f87
-						conn->addr_cur = conn->addr_cur->ai_next;
699f87
-						conn->status = CONNECTION_NEEDED;
699f87
-						goto keep_going;
699f87
-					}
699f87
-					goto error_return;
699f87
+					conn->try_next_addr = true;
699f87
+					goto keep_going;
699f87
 				}
699f87
 
699f87
 				/* Fill in the client address */
699f87
@@ -2266,12 +2382,13 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
-						/* Must drop the old connection */
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->status = CONNECTION_NEEDED;
699f87
+						need_new_connection = true;
699f87
 						goto keep_going;
699f87
 					}
699f87
+					/* Else it's a hard failure */
699f87
+					goto error_return;
699f87
 				}
699f87
+				/* Else, return POLLING_READING or POLLING_WRITING status */
699f87
 				return pollres;
699f87
 #else							/* !USE_SSL */
699f87
 				/* can't get here */
699f87
@@ -2377,9 +2494,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
699f87
 					{
699f87
 						conn->pversion = PG_PROTOCOL(2, 0);
699f87
-						/* Must drop the old connection */
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->status = CONNECTION_NEEDED;
699f87
+						need_new_connection = true;
699f87
 						goto keep_going;
699f87
 					}
699f87
 
699f87
@@ -2430,6 +2545,9 @@ keep_going:						/* We will come back to here until there is
699f87
 					/* OK, we read the message; mark data consumed */
699f87
 					conn->inStart = conn->inCursor;
699f87
 
699f87
+					/* Check to see if we should mention pgpassfile */
699f87
+					dot_pg_pass_warning(conn);
699f87
+
699f87
 #ifdef USE_SSL
699f87
 
699f87
 					/*
699f87
@@ -2443,9 +2561,7 @@ keep_going:						/* We will come back to here until there is
699f87
 					{
699f87
 						/* only retry once */
699f87
 						conn->wait_ssl_try = false;
699f87
-						/* Must drop the old connection */
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->status = CONNECTION_NEEDED;
699f87
+						need_new_connection = true;
699f87
 						goto keep_going;
699f87
 					}
699f87
 
699f87
@@ -2454,14 +2570,13 @@ keep_going:						/* We will come back to here until there is
699f87
 					 * then do a non-SSL retry
699f87
 					 */
699f87
 					if (conn->sslmode[0] == 'p' /* "prefer" */
699f87
-						&& conn->allow_ssl_try
699f87
+						&& conn->ssl != NULL
699f87
+						&& conn->allow_ssl_try	/* redundant? */
699f87
 						&& !conn->wait_ssl_try) /* redundant? */
699f87
 					{
699f87
 						/* only retry once */
699f87
 						conn->allow_ssl_try = false;
699f87
-						/* Must drop the old connection */
699f87
-						pqDropConnection(conn, true);
699f87
-						conn->status = CONNECTION_NEEDED;
699f87
+						need_new_connection = true;
699f87
 						goto keep_going;
699f87
 					}
699f87
 #endif
699f87
@@ -2620,9 +2735,7 @@ keep_going:						/* We will come back to here until there is
699f87
 						{
699f87
 							PQclear(res);
699f87
 							conn->send_appname = false;
699f87
-							/* Must drop the old connection */
699f87
-							pqDropConnection(conn, true);
699f87
-							conn->status = CONNECTION_NEEDED;
699f87
+							need_new_connection = true;
699f87
 							goto keep_going;
699f87
 						}
699f87
 					}
699f87
@@ -2702,8 +2815,6 @@ keep_going:						/* We will come back to here until there is
699f87
 
699f87
 error_return:
699f87
 
699f87
-	dot_pg_pass_warning(conn);
699f87
-
699f87
 	/*
699f87
 	 * We used to close the socket at this point, but that makes it awkward
699f87
 	 * for those above us if they wish to remove this socket from their own
699f87
@@ -2830,13 +2941,7 @@ makeEmptyPGconn(void)
699f87
 	conn->std_strings = false;	/* unless server says differently */
699f87
 	conn->verbosity = PQERRORS_DEFAULT;
699f87
 	conn->sock = -1;
699f87
-	conn->auth_req_received = false;
699f87
-	conn->password_needed = false;
699f87
 	conn->dot_pgpass_used = false;
699f87
-#ifdef USE_SSL
699f87
-	conn->allow_ssl_try = true;
699f87
-	conn->wait_ssl_try = false;
699f87
-#endif
699f87
 
699f87
 	/*
699f87
 	 * We try to send at least 8K at a time, which is the usual size of pipe
699f87
@@ -2987,10 +3092,9 @@ freePGconn(PGconn *conn)
699f87
 static void
699f87
 closePGconn(PGconn *conn)
699f87
 {
699f87
-	PGnotify   *notify;
699f87
-	pgParameterStatus *pstatus;
699f87
-
699f87
 	/*
699f87
+	 * If possible, send Terminate message to close the connection politely.
699f87
+	 *
699f87
 	 * Note that the protocol doesn't allow us to send Terminate messages
699f87
 	 * during the startup phase.
699f87
 	 */
699f87
@@ -3020,32 +3124,15 @@ closePGconn(PGconn *conn)
699f87
 	conn->status = CONNECTION_BAD;		/* Well, not really _bad_ - just
699f87
 										 * absent */
699f87
 	conn->asyncStatus = PGASYNC_IDLE;
699f87
+	conn->xactStatus = PQTRANS_IDLE;
699f87
 	pqClearAsyncResult(conn);	/* deallocate result */
699f87
 	resetPQExpBuffer(&conn->errorMessage);
699f87
 	pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
699f87
 	conn->addrlist = NULL;
699f87
 	conn->addr_cur = NULL;
699f87
-	notify = conn->notifyHead;
699f87
-	while (notify != NULL)
699f87
-	{
699f87
-		PGnotify   *prev = notify;
699f87
 
699f87
-		notify = notify->next;
699f87
-		free(prev);
699f87
-	}
699f87
-	conn->notifyHead = conn->notifyTail = NULL;
699f87
-	pstatus = conn->pstatus;
699f87
-	while (pstatus != NULL)
699f87
-	{
699f87
-		pgParameterStatus *prev = pstatus;
699f87
-
699f87
-		pstatus = pstatus->next;
699f87
-		free(prev);
699f87
-	}
699f87
-	conn->pstatus = NULL;
699f87
-	if (conn->lobjfuncs)
699f87
-		free(conn->lobjfuncs);
699f87
-	conn->lobjfuncs = NULL;
699f87
+	/* Reset all state obtained from server, too */
699f87
+	pqDropServerData(conn);
699f87
 }
699f87
 
699f87
 /*
699f87
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
699f87
index 8843ccb..eaa80b6 100644
699f87
--- a/src/interfaces/libpq/libpq-int.h
699f87
+++ b/src/interfaces/libpq/libpq-int.h
699f87
@@ -375,6 +375,8 @@ struct pg_conn
699f87
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
699f87
 
699f87
 	/* Transient state needed while establishing connection */
699f87
+	bool		try_next_addr;	/* time to advance to next address? */
699f87
+	bool		is_new_addr;	/* need to (re)initialize for new address? */
699f87
 	struct addrinfo *addrlist;	/* list of possible backend addresses */
699f87
 	struct addrinfo *addr_cur;	/* the one currently being tried */
699f87
 	int			addrlist_family;	/* needed to know how to free addrlist */
699f87
-- 
699f87
2.17.1
699f87