aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Eisentraut <peter@eisentraut.org>2022-11-17 15:14:44 +0100
committerPeter Eisentraut <peter@eisentraut.org>2022-11-17 15:42:09 +0100
commitbbf9c282ce92272ed7bf6771daf0f9efa209e61b (patch)
treef05ce44758da3450325dae752d595f623b2f9883
parentdce92e59b1a9ff8401a660a1ac448ea37d498284 (diff)
downloadpostgresql-bbf9c282ce92272ed7bf6771daf0f9efa209e61b.tar.gz
postgresql-bbf9c282ce92272ed7bf6771daf0f9efa209e61b.zip
libpq: Handle NegotiateProtocolVersion message
Before, receiving a NegotiateProtocolVersion message would result in a confusing error message like expected authentication request from server, but received v This adds proper handling of this protocol message and produces an on-topic error message from it. Reviewed-by: Jacob Champion <jchampion@timescale.com> Reviewed-by: Nathan Bossart <nathandbossart@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/f9c7862f-b864-8ef7-a861-c4638c83e209%40enterprisedb.com
-rw-r--r--src/interfaces/libpq/fe-connect.c22
-rw-r--r--src/interfaces/libpq/fe-protocol3.c55
-rw-r--r--src/interfaces/libpq/libpq-int.h1
3 files changed, 73 insertions, 5 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 7d54aa35899..f88d672c6c8 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3194,10 +3194,11 @@ keep_going: /* We will come back to here until there is
/*
* Validate message type: we expect only an authentication
- * request or an error here. Anything else probably means
- * it's not Postgres on the other end at all.
+ * request, NegotiateProtocolVersion, or an error here.
+ * Anything else probably means it's not Postgres on the other
+ * end at all.
*/
- if (!(beresp == 'R' || beresp == 'E'))
+ if (!(beresp == 'R' || beresp == 'v' || beresp == 'E'))
{
libpq_append_conn_error(conn, "expected authentication request from server, but received %c",
beresp);
@@ -3214,14 +3215,15 @@ keep_going: /* We will come back to here until there is
/*
* Try to validate message length before using it.
* Authentication requests can't be very large, although GSS
- * auth requests may not be that small. Errors can be a
+ * auth requests may not be that small. Same for
+ * NegotiateProtocolVersion. Errors can be a
* little larger, but not huge. If we see a large apparent
* length in an error, it means we're really talking to a
* pre-3.0-protocol server; cope. (Before version 14, the
* server also used the old protocol for errors that happened
* before processing the startup packet.)
*/
- if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
+ if ((beresp == 'R' || beresp == 'v') && (msgLength < 8 || msgLength > 2000))
{
libpq_append_conn_error(conn, "expected authentication request from server, but received %c",
beresp);
@@ -3351,6 +3353,16 @@ keep_going: /* We will come back to here until there is
goto error_return;
}
+ else if (beresp == 'v')
+ {
+ if (pqGetNegotiateProtocolVersion3(conn))
+ {
+ goto error_return;
+ }
+ /* OK, we read the message; mark data consumed */
+ conn->inStart = conn->inCursor;
+ goto error_return;
+ }
/* It is an authentication request. */
conn->auth_req_received = true;
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 88dd360c905..364bad2b882 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1386,6 +1386,61 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
/*
+ * Attempt to read a NegotiateProtocolVersion message.
+ * Entry: 'v' message type and length have already been consumed.
+ * Exit: returns 0 if successfully consumed message.
+ * returns EOF if not enough data.
+ */
+int
+pqGetNegotiateProtocolVersion3(PGconn *conn)
+{
+ int tmp;
+ ProtocolVersion their_version;
+ int num;
+ PQExpBufferData buf;
+
+ if (pqGetInt(&tmp, 4, conn) != 0)
+ return EOF;
+ their_version = tmp;
+
+ if (pqGetInt(&num, 4, conn) != 0)
+ return EOF;
+
+ initPQExpBuffer(&buf);
+ for (int i = 0; i < num; i++)
+ {
+ if (pqGets(&conn->workBuffer, conn))
+ {
+ termPQExpBuffer(&buf);
+ return EOF;
+ }
+ if (buf.len > 0)
+ appendPQExpBufferChar(&buf, ' ');
+ appendPQExpBufferStr(&buf, conn->workBuffer.data);
+ }
+
+ if (their_version < conn->pversion)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("protocol version not supported by server: client uses %u.%u, server supports up to %u.%u\n"),
+ PG_PROTOCOL_MAJOR(conn->pversion), PG_PROTOCOL_MINOR(conn->pversion),
+ PG_PROTOCOL_MAJOR(their_version), PG_PROTOCOL_MINOR(their_version));
+ if (num > 0)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_ngettext("protocol extension not supported by server: %s\n",
+ "protocol extensions not supported by server: %s\n", num),
+ buf.data);
+
+ /* neither -- server shouldn't have sent it */
+ if (!(their_version < conn->pversion) && !(num > 0))
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s message"), "NegotiateProtocolVersion");
+
+ termPQExpBuffer(&buf);
+ return 0;
+}
+
+
+/*
* Attempt to read a ParameterStatus message.
* This is possible in several places, so we break it out as a subroutine.
* Entry: 'S' message type and length have already been consumed.
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index c24645b4696..512762f9991 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -685,6 +685,7 @@ extern void pqParseInput3(PGconn *conn);
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
PGVerbosity verbosity, PGContextVisibility show_context);
+extern int pqGetNegotiateProtocolVersion3(PGconn *conn);
extern int pqGetCopyData3(PGconn *conn, char **buffer, int async);
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);