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