0002-libpq-Retry-after-failed-ssl-gss-negotiation.patch
application/octet-stream
Filename: 0002-libpq-Retry-after-failed-ssl-gss-negotiation.patch
Type: application/octet-stream
Part: 0
From b60a18d2bb7e9a45d33155099459b41938f06323 Mon Sep 17 00:00:00 2001
From: Jakob Egger <jakob@eggerapps.at>
Date: Fri, 6 Dec 2019 12:34:23 +0100
Subject: [PATCH 2/2] libpq: Retry after failed ssl/gss negotiation
When attempting to negotiate both GSS and SSL,
detect errors from old servers that don't support
multiple negotiation attempts.
---
src/interfaces/libpq/fe-connect.c | 86 +++++++++++++++++++++----------
src/interfaces/libpq/libpq-int.h | 3 ++
2 files changed, 61 insertions(+), 28 deletions(-)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5c786360a9..e88771b7e6 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1965,11 +1965,6 @@ connectDBStart(PGconn *conn)
*/
resetPQExpBuffer(&conn->errorMessage);
-#ifdef ENABLE_GSS
- if (conn->gssencmode[0] == 'd') /* "disable" */
- conn->try_gss = false;
-#endif
-
/*
* Set up to try to connect to the first host. (Setting whichhost = -1 is
* a bit of a cheat, but PQconnectPoll will advance it to 0 before
@@ -2409,6 +2404,13 @@ keep_going: /* We will come back to here until there is
conn->allow_ssl_try = (conn->sslmode[0] != 'd'); /* "disable" */
conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
#endif
+
+#ifdef ENABLE_GSS
+ if (conn->gssencmode[0] == 'd') /* "disable" */
+ conn->try_gss = false;
+#endif
+
+ conn->did_negotiate = false;
reset_connection_state_machine = false;
need_new_connection = true;
@@ -2431,6 +2433,8 @@ keep_going: /* We will come back to here until there is
/* Reset conn->status to put the state machine in the right state */
conn->status = CONNECTION_NEEDED;
+ conn->did_negotiate = false;
+
need_new_connection = false;
}
@@ -2971,24 +2975,37 @@ keep_going: /* We will come back to here until there is
goto error_return;
}
/* Otherwise, proceed with normal startup */
+ conn->did_negotiate = true;
conn->allow_ssl_try = false;
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
}
else if (SSLok == 'E')
{
- /*
- * Server failure of some sort, such as failure to
- * fork a backend process. We need to process and
- * report the error message, which might be formatted
- * according to either protocol 2 or protocol 3.
- * Rather than duplicate the code for that, we flip
- * into AWAITING_RESPONSE state and let the code there
- * deal with it. Note we have *not* consumed the "E"
- * byte here.
- */
- conn->status = CONNECTION_AWAITING_RESPONSE;
- goto keep_going;
+ if (conn->did_negotiate) {
+ /*
+ * Postmaster used to allow only a single negotiation attempt.
+ * If we get an error on the second negotiation attempt,
+ * we reconnect and try again.
+ */
+ need_new_connection = true;
+ goto keep_going;
+ }
+ else
+ {
+ /*
+ * Server failure of some sort, such as failure to
+ * fork a backend process. We need to process and
+ * report the error message, which might be formatted
+ * according to either protocol 2 or protocol 3.
+ * Rather than duplicate the code for that, we flip
+ * into AWAITING_RESPONSE state and let the code there
+ * deal with it. Note we have *not* consumed the "E"
+ * byte here.
+ */
+ conn->status = CONNECTION_AWAITING_RESPONSE;
+ goto keep_going;
+ }
}
else
{
@@ -3061,17 +3078,29 @@ keep_going: /* We will come back to here until there is
if (gss_ok == 'E')
{
- /*
- * Server failure of some sort. Assume it's a
- * protocol version support failure, and let's see if
- * we can't recover (if it's not, we'll get a better
- * error message on retry). Server gets fussy if we
- * don't hang up the socket, though.
- */
- conn->try_gss = false;
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
+ if (conn->did_negotiate) {
+ /*
+ * Postmaster used to allow only a single negotiation attempt.
+ * If we get an error on the second negotiation attempt,
+ * we reconnect and try again.
+ */
+ need_new_connection = true;
+ goto keep_going;
+ }
+ else
+ {
+ /*
+ * Server failure of some sort. Assume it's a
+ * protocol version support failure, and let's see if
+ * we can't recover (if it's not, we'll get a better
+ * error message on retry). Server gets fussy if we
+ * don't hang up the socket, though.
+ */
+ conn->try_gss = false;
+ pqDropConnection(conn, true);
+ conn->status = CONNECTION_NEEDED;
+ goto keep_going;
+ }
}
/* mark byte consumed */
@@ -3087,6 +3116,7 @@ keep_going: /* We will come back to here until there is
goto error_return;
}
+ conn->did_negotiate = true;
conn->try_gss = false;
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 7f5be7db7a..e787ab82da 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -507,6 +507,9 @@ struct pg_conn
* connection */
#endif
+ /* Did we try negotiating SSL or GSS? Postmaster used to allow only a single attempt */
+ bool did_negotiate;
+
/* Buffer for current error message */
PQExpBufferData errorMessage; /* expansible string */
--
2.23.0