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()
             {