]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
libpq: Prepare for protocol grease during 19beta
authorJacob Champion <jchampion@postgresql.org>
Fri, 6 Feb 2026 18:31:45 +0000 (10:31 -0800)
committerJacob Champion <jchampion@postgresql.org>
Fri, 6 Feb 2026 18:31:45 +0000 (10:31 -0800)
The main reason that libpq doesn't request protocol version 3.2 by
default is because other proxy/server implementations don't implement
the negotiation. This is a bit of a chicken-and-egg problem: We don't
bump the default version that libpq requests, but other implementations
may not be incentivized to implement version negotiation if their users
never run into issues.

One established practice to combat this is to flip Postel's Law on its
head, by sending parameters that the server cannot possibly support. If
the server fails the handshake instead of correctly negotiating, then
the problem is surfaced naturally. If the server instead claims to
support the bogus parameters, then we fail the connection to make the
lie obvious. This is called "grease" (or "greasing"), after the GREASE
mechanism in TLS that popularized the concept:

    https://www.rfc-editor.org/rfc/rfc8701.html

This patch reserves 3.9999 as an explicitly unsupported protocol version
number and `_pq_.test_protocol_negotiation` as an explicitly unsupported
protocol extension. A later commit will send these by default in order
to stress-test the ecosystem during the beta period; that commit will
then be reverted before 19 RC1, so that we can decide what to do with
whatever data has been gathered.

The _pq_.test_protocol_negotiation change here is intentionally docs-
only: after its implementation is reverted, the parameter should remain
reserved.

Extracted/adapted from a patch by Jelte Fennema-Nio.

Author: Jelte Fennema-Nio <postgres@jeltef.nl>
Co-authored-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/DDPR5BPWH1RJ.1LWAK6QAURVAY%40jeltef.nl

doc/src/sgml/protocol.sgml
src/include/libpq/pqcomm.h
src/interfaces/libpq/fe-protocol3.c

index 36fd327d4b9208aa6a7d02e89a69b1e2db8573ca..89ac680efd50b67e171d89dc6267fffd18536b6b 100644 (file)
      </thead>
 
      <tbody>
+      <row>
+      <entry>3.9999</entry>
+      <entry>-</entry>
+      <entry>Reserved for protocol greasing. libpq may use this version, which
+        is higher than any minor version the project ever expects to use, to
+        test that servers and middleware properly implement protocol version
+        negotiation. Servers <emphasis>must not</emphasis> add special-case
+        logic for this version; they should simply compare it to their latest
+        supported version (which will always be smaller) and downgrade via a
+        NegotiateProtocolVersion message.
+      </entry>
+      </row>
       <row>
       <entry>3.1</entry>
       <entry>-</entry>
         otherwise continue the connection.
       </entry>
       </row>
+
+      <row>
+      <entry><literal>_pq_.test_protocol_negotiation</literal></entry>
+      <entry>Reserved for protocol greasing. libpq may send this extension to
+        test that servers and middleware properly implement protocol extension
+        negotiation. Servers <emphasis>must not</emphasis> add special-case
+        logic for this parameter; they should simply send the list of all
+        unsupported options (including this one) via a NegotiateProtocolVersion
+        message.
+      </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
index 1bbe5b9ee452421ceddf3dd4993f41685223dc33..a29c9c94d796ee4a405f60af49121a0950a41513 100644 (file)
@@ -104,6 +104,16 @@ is_unixsock_path(const char *path)
  */
 #define PG_PROTOCOL_RESERVED_31                PG_PROTOCOL(3,1)
 
+/*
+ * PG_PROTOCOL_GREASE is an intentionally unsupported protocol version used
+ * for "greasing" (the practice of sending valid, but extraneous or otherwise
+ * unusual, messages to keep peer implementations honest). This helps ensure
+ * that servers properly implement protocol version negotiation. Version 3.9999
+ * was chosen since it is safely within the valid range, it is representable
+ * via PQfullProtocolVersion, and it is unlikely to ever be needed in practice.
+ */
+#define PG_PROTOCOL_GREASE             PG_PROTOCOL(3,9999)
+
 /*
  * A client can send a cancel-current-operation request to the postmaster.
  * This is uglier than sending it directly to the client's backend, but it
index 103428033ef8e1ee6bf082bc97e5639000bc85be..90bbb2eba1f13b40362c8648884854042d226e14 100644 (file)
@@ -1451,7 +1451,19 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
        if (pqGetInt(&num, 4, conn) != 0)
                goto eof;
 
-       /* Check the protocol version */
+       /*
+        * Check the protocol version.
+        *
+        * PG_PROTOCOL_GREASE is intentionally unsupported and reserved. It's
+        * higher than any real version, so check for that first, to get the most
+        * specific error message. Then check the upper and lower bounds.
+        */
+       if (their_version == PG_PROTOCOL_GREASE)
+       {
+               libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested \"grease\" protocol version 3.9999");
+               goto failure;
+       }
+
        if (their_version > conn->pversion)
        {
                libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested downgrade to a higher-numbered version");