How XQUIC Supports QUIC Draft-29: Implementation Details in the Alibaba Library

XQUIC implements IETF QUIC draft-29 through dedicated protocol version constants, draft-specific TLS cryptographic salts, ALPN mapping to h3-29, and configurable engine parameters that enable both client and server operations.

The Alibaba XQUIC library maintains backward compatibility with IETF QUIC draft-29 alongside QUIC v1, allowing interoperability with legacy implementations. This support is embedded at multiple layers of the stack, from wire-format constants to connection-level version negotiation. Understanding these implementation details helps developers configure the library for specific protocol requirements.

Protocol Version Constants and Enumeration

XQUIC defines draft-29 as a distinct protocol version through the enumeration system in include/xquic/xquic.h. The library assigns XQC_IDRAFT_VER_29 the numeric value 2, separating it from QUIC v1 and other draft iterations.

The wire-format constants reside in src/transport/xqc_defs.h. Here, XQUIC specifies the 32-bit version number 0xFF00001D and the 4-byte field {0xFF,0x00,0x00,0x1D} used in packet headers. These values match the IETF specification for draft-29 version identification.

Cryptographic Parameters for Draft-29

Draft-29 requires specific initial salts and retry integrity keys for TLS 1.3 handshake integration. XQUIC stores these parameters in src/tls/xqc_tls_common.h, defining the initial salt, retry key, and retry nonce required for draft-29 operation.

Notably, XQUIC shares these cryptographic constants between draft-29 and drafts 30-32, as these versions used identical TLS parameter sets before the final QUIC v1 specification changed the salt values.

HTTP/3 ALPN Negotiation

For HTTP/3 support, XQUIC maps draft-29 to the application-layer protocol negotiation (ALPN) string h3-29 in src/http3/xqc_h3_defs.c. This mapping ensures that when a connection negotiates draft-29, the corresponding HTTP/3 layer correctly advertises the draft-specific protocol identifier during the TLS handshake.

Engine Configuration and Version Validation

The transport layer configures draft-29 support through the engine API in src/transport/xqc_engine.c. Server configurations populate the support_version_list array with XQC_IDRAFT_VER_29_VALUE, advertising draft-29 availability during version negotiation.

Version validation occurs through the xqc_check_proto_version_valid() macro defined in src/transport/xqc_defs.h. This macro guarantees that only explicitly defined versions—including draft-29—are accepted during connection establishment, preventing negotiation of unsupported protocol variants.

Connection Handling and Retry Logic

In src/transport/xqc_conn.c, XQUIC implements draft-specific connection behaviors. When processing retry packets, the library treats draft-29 as "too old" for retry packet logic, matching the IETF specification's guidance that legacy drafts should bypass certain retry mechanisms. This distinction appears in the connection state machine around line 4173.

Practical Implementation: Client and Server Examples

Configuring a Draft-29 Client

To force a client connection to use draft-29, set the proto_version field in the configuration structure:

#include <xquic/xquic.h>

int main(void) {
    xqc_engine_t *engine = xqc_engine_create(XQC_ENGINE_CLIENT, NULL);
    if (!engine) return -1;

    xqc_config_t cfg = XQC_CLIENT_DEFAULT_CONFIG;
    cfg.proto_version = XQC_IDRAFT_VER_29;  /* Select draft-29 */

    if (xqc_engine_set_config(engine, &cfg) != 0) return -1;

    xqc_connection_t *conn = NULL;
    xqc_engine_connect(engine, &conn, "example.com", 4433,
                       XQC_IDRAFT_VER_29, NULL);
    /* Handle streams and I/O */
    return 0;
}

The cfg.proto_version = XQC_IDRAFT_VER_29 assignment forces the client to use version 0xFF00001D, automatically selecting the matching TLS initial salt and h3-29 ALPN.

Advertising Draft-29 on a Server

Servers can support both v1 and draft-29 by populating the version list:

#include <xquic/xquic.h>

int main(void) {
    xqc_engine_t *engine = xqc_engine_create(XQC_ENGINE_SERVER, NULL);
    if (!engine) return -1;

    xqc_config_t cfg = XQC_SERVER_DEFAULT_CONFIG;
    cfg.support_version_count = 2;
    cfg.support_version_list[0] = XQC_VERSION_V1_VALUE;      /* QUIC v1 */
    cfg.support_version_list[1] = XQC_IDRAFT_VER_29_VALUE;   /* Draft-29 */

    xqc_engine_set_config(engine, &cfg);
    /* Bind to UDP socket and accept connections */
    return 0;
}

This configuration announces both versions during the version negotiation phase, allowing the client to select its preferred protocol.

Summary

  • XQUIC defines draft-29 as XQC_IDRAFT_VER_29 (value 2) in include/xquic/xquic.h with wire-format constant 0xFF00001D in src/transport/xqc_defs.h.
  • Cryptographic parameters including initial salt and retry keys are shared between draft-29 and drafts 30-32 in src/tls/xqc_tls_common.h.
  • The HTTP/3 layer maps draft-29 to ALPN string h3-29 via src/http3/xqc_h3_defs.c.
  • Clients select draft-29 through cfg.proto_version, while servers advertise it via support_version_list in the engine configuration.
  • Version validation uses the xqc_check_proto_version_valid() macro to enforce protocol compliance.

Frequently Asked Questions

What is the numeric version identifier for draft-29 in XQUIC?

XQUIC represents draft-29 as the 32-bit hexadecimal value 0xFF00001D (defined as XQC_IDRAFT_VER_29_VALUE in src/transport/xqc_defs.h) and the enum value XQC_IDRAFT_VER_29 equal to 2. These constants appear in packet headers during the wire protocol exchange.

Does XQUIC share cryptographic parameters between draft-29 and newer versions?

Yes, according to the implementation in src/tls/xqc_tls_common.h, XQUIC uses identical initial salts, retry keys, and retry nonces for draft-29 through draft-32. This cryptographic continuity distinguishes these drafts from QUIC v1, which employs different salt values.

How do I force a client connection to use draft-29 instead of QUIC v1?

Set cfg.proto_version = XQC_IDRAFT_VER_29 in the xqc_config_t structure before calling xqc_engine_set_config(). This overrides the default version selection and forces the connection to use draft-29's version number (0xFF00001D) and corresponding h3-29 ALPN.

Why does XQUIC treat draft-29 as "too old" for retry packet processing?

As implemented in src/transport/xqc_conn.c, XQUIC excludes draft-29 from retry packet logic because the IETF specification updated retry integrity tag calculation in later versions. Treating draft-29 as a legacy version ensures compliance with the specific retry packet requirements that existed during that draft era.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →