examples: deterministically generate source conn ID on servers

When we receive a ClientHello split across multiple Initial packets we
end up creating multiple connections in the example servers due to the
fact that we only store the server-generated destination connection ID,
and not the original client-generated destination connection ID, in the
HashMap used to associate incoming packets to connections.

This changes how we generate the server-generated destination connection
ID by making it more deterministic and based on the client-generated
connection ID, and then do a double lookup for both the raw dcid and the
deterministically-generated one.

Unfortunately due to the fact that we use the normal Rust HashMap we
can't have multiple keys pointing to the same connection, so we can't
store the client-generated connection ID directly. In practice we could
have a double layer HashMap, but then we'd run into problems when
removing connections because we might not have the original destination
connection ID (e.g. when we don't do Retry), so we would leak memory.
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index a078956..bcf52ca 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -154,6 +154,10 @@
 
     let h3_config = quiche::h3::Config::new().unwrap();
 
+    let rng = SystemRandom::new();
+    let conn_id_seed =
+        ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
+
     let mut clients = ClientMap::new();
 
     loop {
@@ -213,14 +217,14 @@
 
             trace!("got packet {:?}", hdr);
 
-            if hdr.ty == quiche::Type::VersionNegotiation {
-                error!("Version negotiation invalid on the server");
-                continue;
-            }
+            let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
+            let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
 
             // Lookup a connection based on the packet's connection ID. If there
             // is no connection matching, create a new one.
-            let (_, client) = if !clients.contains_key(&hdr.dcid) {
+            let (_, client) = if !clients.contains_key(&hdr.dcid) &&
+                !clients.contains_key(conn_id)
+            {
                 if hdr.ty != quiche::Type::Initial {
                     error!("Packet is not Initial");
                     continue;
@@ -246,9 +250,8 @@
                     continue;
                 }
 
-                // Generate a random source connection ID for the connection.
                 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
-                SystemRandom::new().fill(&mut scid[..]).unwrap();
+                scid.copy_from_slice(&conn_id);
 
                 let mut odcid = None;
 
@@ -316,7 +319,11 @@
 
                 clients.get_mut(&scid[..]).unwrap()
             } else {
-                clients.get_mut(&hdr.dcid).unwrap()
+                match clients.get_mut(&hdr.dcid) {
+                    Some(v) => v,
+
+                    None => clients.get_mut(conn_id).unwrap(),
+                }
             };
 
             // Process potentially coalesced packets.
diff --git a/examples/server.rs b/examples/server.rs
index ad63e02..668684d 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -154,6 +154,10 @@
         .set_cc_algorithm_name(args.get_str("--cc-algorithm"))
         .unwrap();
 
+    let rng = SystemRandom::new();
+    let conn_id_seed =
+        ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
+
     let mut clients = ClientMap::new();
 
     let mut pkt_count = 0;
@@ -226,14 +230,14 @@
 
             trace!("got packet {:?}", hdr);
 
-            if hdr.ty == quiche::Type::VersionNegotiation {
-                error!("Version negotiation invalid on the server");
-                continue;
-            }
+            let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
+            let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
 
             // Lookup a connection based on the packet's connection ID. If there
             // is no connection matching, create a new one.
-            let (_, client) = if !clients.contains_key(&hdr.dcid) {
+            let (_, client) = if !clients.contains_key(&hdr.dcid) &&
+                !clients.contains_key(conn_id)
+            {
                 if hdr.ty != quiche::Type::Initial {
                     error!("Packet is not Initial");
                     continue;
@@ -259,9 +263,8 @@
                     continue;
                 }
 
-                // Generate a random source connection ID for the connection.
                 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
-                SystemRandom::new().fill(&mut scid[..]).unwrap();
+                scid.copy_from_slice(&conn_id);
 
                 let mut odcid = None;
 
@@ -329,7 +332,11 @@
 
                 clients.get_mut(&scid[..]).unwrap()
             } else {
-                clients.get_mut(&hdr.dcid).unwrap()
+                match clients.get_mut(&hdr.dcid) {
+                    Some(v) => v,
+
+                    None => clients.get_mut(conn_id).unwrap(),
+                }
             };
 
             // Process potentially coalesced packets.