diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/libpq/pqcomm.h | 3 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 88 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-protocol3.c | 18 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 4 | ||||
-rw-r--r-- | src/test/modules/libpq_pipeline/libpq_pipeline.c | 113 |
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) |