aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/libpq/pqcomm.h3
-rw-r--r--src/interfaces/libpq/fe-connect.c88
-rw-r--r--src/interfaces/libpq/fe-protocol3.c18
-rw-r--r--src/interfaces/libpq/libpq-int.h4
-rw-r--r--src/test/modules/libpq_pipeline/libpq_pipeline.c113
5 files changed, 221 insertions, 5 deletions
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 46b37e0e4eb..0aceb7147c7 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -91,11 +91,10 @@ is_unixsock_path(const char *path)
/*
* The earliest and latest frontend/backend protocol version supported.
- * (Only protocol version 3 is currently supported)
*/
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(3,0)
-#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0)
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,2)
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
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.
*
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 43e3519e4bd..7ba49ea4592 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1432,6 +1432,13 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
goto failure;
}
+ /* 3.1 never existed, we went straight from 3.0 to 3.2 */
+ if (their_version == PG_PROTOCOL(3, 1))
+ {
+ libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requests downgrade to non-existent 3.1 protocol version");
+ goto failure;
+ }
+
if (num < 0)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported negative number of unsupported parameters");
@@ -1444,6 +1451,17 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
goto failure;
}
+ if (their_version < conn->min_pversion)
+ {
+ libpq_append_conn_error(conn, "server only supports protocol version %d.%d, but min_protocol_version was set to %d.%d",
+ PG_PROTOCOL_MAJOR(their_version),
+ PG_PROTOCOL_MINOR(their_version),
+ PG_PROTOCOL_MAJOR(conn->min_pversion),
+ PG_PROTOCOL_MINOR(conn->min_pversion));
+
+ goto failure;
+ }
+
/* the version is acceptable */
conn->pversion = their_version;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d9f2e9ad743..232e0b00f75 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -417,6 +417,8 @@ struct pg_conn
char *gsslib; /* What GSS library to use ("gssapi" or
* "sspi") */
char *gssdelegation; /* Try to delegate GSS credentials? (0 or 1) */
+ char *min_protocol_version; /* minimum used protocol version */
+ char *max_protocol_version; /* maximum used protocol version */
char *ssl_min_protocol_version; /* minimum TLS protocol version */
char *ssl_max_protocol_version; /* maximum TLS protocol version */
char *target_session_attrs; /* desired session properties */
@@ -539,6 +541,8 @@ struct pg_conn
void *scram_client_key_binary; /* binary SCRAM client key */
size_t scram_server_key_len;
void *scram_server_key_binary; /* binary SCRAM server key */
+ ProtocolVersion min_pversion; /* protocol version to request */
+ ProtocolVersion max_pversion; /* protocol version to request */
/* Miscellaneous stuff */
int be_pid; /* PID of backend --- needed for cancels */
diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c
index ac9ac95135f..9a3c0236325 100644
--- a/src/test/modules/libpq_pipeline/libpq_pipeline.c
+++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c
@@ -206,15 +206,17 @@ copy_connection(PGconn *conn)
PQconninfoOption *opts = PQconninfo(conn);
const char **keywords;
const char **vals;
- int nopts = 1;
- int i = 0;
+ int nopts = 0;
+ int i;
for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
nopts++;
+ nopts++; /* for the NULL terminator */
keywords = pg_malloc(sizeof(char *) * nopts);
vals = pg_malloc(sizeof(char *) * nopts);
+ i = 0;
for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
{
if (opt->val)
@@ -1405,6 +1407,110 @@ test_prepared(PGconn *conn)
fprintf(stderr, "ok\n");
}
+/*
+ * Test max_protocol_version options.
+ */
+static void
+test_protocol_version(PGconn *conn)
+{
+ const char **keywords;
+ const char **vals;
+ int nopts;
+ PQconninfoOption *opts = PQconninfo(conn);
+ int protocol_version;
+ int max_protocol_version_index;
+ int i;
+
+ /*
+ * Prepare keywords/vals arrays, copied from the existing connection, with
+ * an extra slot for 'max_protocol_version'.
+ */
+ nopts = 0;
+ for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+ nopts++;
+ nopts++; /* max_protocol_version */
+ nopts++; /* NULL terminator */
+
+ keywords = pg_malloc0(sizeof(char *) * nopts);
+ vals = pg_malloc0(sizeof(char *) * nopts);
+
+ i = 0;
+ for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+ {
+ if (opt->val)
+ {
+ keywords[i] = opt->keyword;
+ vals[i] = opt->val;
+ i++;
+ }
+ }
+
+ max_protocol_version_index = i;
+ keywords[i] = "max_protocol_version"; /* value is filled in below */
+ i++;
+ keywords[i] = vals[i] = NULL;
+
+ /*
+ * Test max_protocol_version=3.0
+ */
+ vals[max_protocol_version_index] = "3.0";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30000)
+ pg_fatal("expected 30000, got %d", protocol_version);
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=3.1. It's not valid, we went straight from
+ * 3.0 to 3.2.
+ */
+ vals[max_protocol_version_index] = "3.1";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_BAD)
+ pg_fatal("Connecting with max_protocol_version 3.1 should have failed.");
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=3.2
+ */
+ vals[max_protocol_version_index] = "3.2";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30002)
+ pg_fatal("expected 30002, got %d", protocol_version);
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=latest. 'latest' currently means '3.2'.
+ */
+ vals[max_protocol_version_index] = "latest";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30002)
+ pg_fatal("expected 30002, got %d", protocol_version);
+
+ PQfinish(conn);
+}
+
/* Notice processor: print notices, and count how many we got */
static void
notice_processor(void *arg, const char *message)
@@ -2153,6 +2259,7 @@ print_test_list(void)
printf("pipeline_idle\n");
printf("pipelined_insert\n");
printf("prepared\n");
+ printf("protocol_version\n");
printf("simple_pipeline\n");
printf("singlerow\n");
printf("transaction\n");
@@ -2263,6 +2370,8 @@ main(int argc, char **argv)
test_pipelined_insert(conn, numrows);
else if (strcmp(testname, "prepared") == 0)
test_prepared(conn);
+ else if (strcmp(testname, "protocol_version") == 0)
+ test_protocol_version(conn);
else if (strcmp(testname, "simple_pipeline") == 0)
test_simple_pipeline(conn);
else if (strcmp(testname, "singlerow") == 0)