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