aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c88
1 files changed, 87 insertions, 1 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 0256753bd3e..5e3275ffd76 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -325,6 +325,16 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Require-Auth", "", 14, /* sizeof("scram-sha-256") == 14 */
offsetof(struct pg_conn, require_auth)},
+ {"min_protocol_version", "PGMINPROTOCOLVERSION",
+ NULL, NULL,
+ "Min-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
+ offsetof(struct pg_conn, min_protocol_version)},
+
+ {"max_protocol_version", "PGMAXPROTOCOLVERSION",
+ NULL, NULL,
+ "Max-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
+ offsetof(struct pg_conn, max_protocol_version)},
+
{"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", "TLSv1.2", NULL,
"SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
offsetof(struct pg_conn, ssl_min_protocol_version)},
@@ -483,6 +493,7 @@ static void pgpassfileWarning(PGconn *conn);
static void default_threadlock(int acquire);
static bool sslVerifyProtocolVersion(const char *version);
static bool sslVerifyProtocolRange(const char *min, const char *max);
+static bool pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn, const char *context);
/* global variable because fe-auth.c needs to access it */
@@ -2081,6 +2092,48 @@ pqConnectOptions2(PGconn *conn)
}
}
+ if (conn->min_protocol_version)
+ {
+ if (!pqParseProtocolVersion(conn->min_protocol_version, &conn->min_pversion, conn, "min_protocol_version"))
+ {
+ conn->status = CONNECTION_BAD;
+ return false;
+ }
+ }
+ else
+ {
+ conn->min_pversion = PG_PROTOCOL_EARLIEST;
+ }
+
+ if (conn->max_protocol_version)
+ {
+ if (!pqParseProtocolVersion(conn->max_protocol_version, &conn->max_pversion, conn, "max_protocol_version"))
+ {
+ conn->status = CONNECTION_BAD;
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * To not break connecting to older servers/poolers that do not yet
+ * support NegotiateProtocolVersion, default to the 3.0 protocol at
+ * least for a while longer. Except when min_protocol_version is set
+ * to something larger, then we might as well default to the latest.
+ */
+ if (conn->min_pversion > PG_PROTOCOL(3, 0))
+ conn->max_pversion = PG_PROTOCOL_LATEST;
+ else
+ conn->max_pversion = PG_PROTOCOL(3, 0);
+ }
+
+ if (conn->min_pversion > conn->max_pversion)
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "min_protocol_version is greater than max_protocol_version");
+ return false;
+ }
+
/*
* Resolve special "auto" client_encoding from the locale
*/
@@ -3084,7 +3137,7 @@ keep_going: /* We will come back to here until there is
* must persist across individual connection attempts, but we must
* reset them when we start to consider a new server.
*/
- conn->pversion = PG_PROTOCOL(3, 0);
+ conn->pversion = conn->max_pversion;
conn->send_appname = true;
conn->failed_enc_methods = 0;
conn->current_enc_method = 0;
@@ -4102,6 +4155,7 @@ keep_going: /* We will come back to here until there is
/* OK, we read the message; mark data consumed */
pqParseDone(conn, conn->inCursor);
+
goto keep_going;
}
@@ -8158,6 +8212,38 @@ error:
}
/*
+ * Parse and try to interpret "value" as a ProtocolVersion value, and if
+ * successful, store it in *result.
+ */
+static bool
+pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn,
+ const char *context)
+{
+ if (strcmp(value, "latest") == 0)
+ {
+ *result = PG_PROTOCOL_LATEST;
+ return true;
+ }
+ if (strcmp(value, "3.0") == 0)
+ {
+ *result = PG_PROTOCOL(3, 0);
+ return true;
+ }
+
+ /* 3.1 never existed, we went straight from 3.0 to 3.2 */
+
+ if (strcmp(value, "3.2") == 0)
+ {
+ *result = PG_PROTOCOL(3, 2);
+ return true;
+ }
+
+ libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
+ context, value);
+ return false;
+}
+
+/*
* To keep the API consistent, the locking stubs are always provided, even
* if they are not required.
*