support draft-24 alongside draft-23
This adds initial support for draft-24, while still retaining draft-23
support. In practice they are basically the same protocol, so the only
change is supporting multiple versions on the server and doing some kind
of version negotiation.
diff --git a/examples/client.c b/examples/client.c
index 088b1c0..e184375 100644
--- a/examples/client.c
+++ b/examples/client.c
@@ -243,7 +243,7 @@
}
quiche_config_set_application_protos(config,
- (uint8_t *) "\x05hq-23\x08http/0.9", 15);
+ (uint8_t *) "\x05hq-24\x08http/0.9", 15);
quiche_config_set_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
diff --git a/examples/client.rs b/examples/client.rs
index c2e7edb..34e4cf8 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -114,7 +114,7 @@
config.verify_peer(true);
config
- .set_application_protos(b"\x05hq-23\x08http/0.9")
+ .set_application_protos(b"\x05hq-24\x08http/0.9")
.unwrap();
config.set_idle_timeout(5000);
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index 2f17224..f4db650 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -221,7 +221,7 @@
continue;
}
- if hdr.version != quiche::PROTOCOL_VERSION {
+ if !quiche::version_is_supported(hdr.version) {
warn!("Doing version negotiation");
let len =
diff --git a/examples/server.c b/examples/server.c
index 6a680fd..19183b3 100644
--- a/examples/server.c
+++ b/examples/server.c
@@ -443,7 +443,7 @@
quiche_config_load_priv_key_from_pem_file(config, "examples/cert.key");
quiche_config_set_application_protos(config,
- (uint8_t *) "\x05hq-23\x08http/0.9", 15);
+ (uint8_t *) "\x05hq-24\x08http/0.9", 15);
quiche_config_set_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
diff --git a/examples/server.rs b/examples/server.rs
index 2436fef..3228ce2 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -128,7 +128,7 @@
.unwrap();
config
- .set_application_protos(b"\x05hq-23\x08http/0.9")
+ .set_application_protos(b"\x05hq-24\x08http/0.9")
.unwrap();
config.set_idle_timeout(5000);
@@ -234,7 +234,7 @@
continue;
}
- if hdr.version != quiche::PROTOCOL_VERSION {
+ if !quiche::version_is_supported(hdr.version) {
warn!("Doing version negotiation");
let len =
diff --git a/include/quiche.h b/include/quiche.h
index bf2081d..7db88ee 100644
--- a/include/quiche.h
+++ b/include/quiche.h
@@ -38,7 +38,7 @@
//
// The current QUIC wire version.
-#define QUICHE_PROTOCOL_VERSION 0xff000017
+#define QUICHE_PROTOCOL_VERSION 0xff000018
// The maximum length of a connection ID.
#define QUICHE_MAX_CONN_ID_LEN 20
@@ -196,6 +196,9 @@
const uint8_t *token, size_t token_len,
uint8_t *out, size_t out_len);
+// Returns true if the given protocol version is supported.
+bool quiche_version_is_supported(uint32_t version);
+
quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len,
const uint8_t *odcid, size_t odcid_len,
quiche_config *config, void *ssl,
@@ -301,8 +304,8 @@
// HTTP/3 API
//
-/// The current HTTP/3 ALPN token.
-#define QUICHE_H3_APPLICATION_PROTOCOL "\x05h3-23"
+// List of ALPN tokens of supported HTTP/3 versions.
+#define QUICHE_H3_APPLICATION_PROTOCOL "\x05h3-24\x05h3-23"
// Stores configuration shared between multiple connections.
typedef struct Http3Config quiche_h3_config;
diff --git a/src/ffi.rs b/src/ffi.rs
index d3df092..9074b6e 100644
--- a/src/ffi.rs
+++ b/src/ffi.rs
@@ -339,6 +339,11 @@
}
#[no_mangle]
+pub extern fn quiche_version_is_supported(version: u32) -> bool {
+ version_is_supported(version)
+}
+
+#[no_mangle]
pub extern fn quiche_retry(
scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
new_scid: *const u8, new_scid_len: size_t, token: *const u8,
diff --git a/src/h3/mod.rs b/src/h3/mod.rs
index 798cc13..5424f46 100644
--- a/src/h3/mod.rs
+++ b/src/h3/mod.rs
@@ -251,8 +251,14 @@
use crate::octets;
-/// The current HTTP/3 ALPN token.
-pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-23";
+/// List of ALPN tokens of supported HTTP/3 versions.
+///
+/// This can be passed directly to the [`Config::set_application_protos()`]
+/// method when implementing HTTP/3 applications.
+///
+/// [`Config::set_application_protos()`]:
+/// ../struct.Config.html#method.set_application_protos
+pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-24\x05h3-23";
/// A specialized [`Result`] type for quiche HTTP/3 operations.
///
diff --git a/src/lib.rs b/src/lib.rs
index e549ba4..6f31093 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -242,7 +242,8 @@
use std::pin::Pin;
/// The current QUIC wire version.
-pub const PROTOCOL_VERSION: u32 = 0xff00_0017;
+pub const PROTOCOL_VERSION: u32 = 0xff00_0018;
+const PROTOCOL_VERSION_OLD: u32 = 0xff00_0017;
/// The maximum length of a connection ID.
pub const MAX_CONN_ID_LEN: usize = crate::packet::MAX_CID_LEN as usize;
@@ -899,6 +900,11 @@
packet::retry(scid, dcid, new_scid, token, out)
}
+/// Returns true if the given protocol version is supported.
+pub fn version_is_supported(version: u32) -> bool {
+ version == PROTOCOL_VERSION || version == PROTOCOL_VERSION_OLD
+}
+
impl Connection {
fn new(
scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, is_server: bool,
@@ -1159,8 +1165,9 @@
let mut new_version = 0;
for v in versions.iter() {
- if *v == PROTOCOL_VERSION {
+ if version_is_supported(*v) {
new_version = *v;
+ break;
}
}
@@ -1225,6 +1232,15 @@
return Err(Error::Done);
}
+ if self.is_server && !self.did_version_negotiation {
+ if !version_is_supported(hdr.version) {
+ return Err(Error::UnknownVersion);
+ }
+
+ self.version = hdr.version;
+ self.did_version_negotiation = true;
+ }
+
if hdr.ty != packet::Type::Short && hdr.version != self.version {
return Err(Error::UnknownVersion);
}
diff --git a/src/packet.rs b/src/packet.rs
index 6d81718..13af75f 100644
--- a/src/packet.rs
+++ b/src/packet.rs
@@ -211,13 +211,13 @@
};
let dcid_len = b.get_u8()?;
- if version == crate::PROTOCOL_VERSION && dcid_len > MAX_CID_LEN {
+ if crate::version_is_supported(version) && dcid_len > MAX_CID_LEN {
return Err(Error::InvalidPacket);
}
let dcid = b.get_bytes(dcid_len as usize)?.to_vec();
let scid_len = b.get_u8()?;
- if version == crate::PROTOCOL_VERSION && scid_len > MAX_CID_LEN {
+ if crate::version_is_supported(version) && scid_len > MAX_CID_LEN {
return Err(Error::InvalidPacket);
}
let scid = b.get_bytes(scid_len as usize)?.to_vec();
@@ -587,6 +587,7 @@
b.put_u8(dcid.len() as u8)?;
b.put_bytes(&dcid)?;
b.put_u32(crate::PROTOCOL_VERSION)?;
+ b.put_u32(crate::PROTOCOL_VERSION_OLD)?;
Ok(b.off())
}
diff --git a/src/tls.rs b/src/tls.rs
index 2563fd9..ab7118c 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -690,6 +690,12 @@
while let Ok(proto) = protos.get_bytes_with_u8_length() {
let found = conn.application_protos.iter().any(|expected| {
+ trace!(
+ "checking peer ALPN {:?} against {:?}",
+ std::str::from_utf8(proto.as_ref()),
+ std::str::from_utf8(expected.as_slice())
+ );
+
if expected.len() == proto.len() &&
expected.as_slice() == proto.as_ref()
{