From 343f8d9c6f20ed8762168bb5fbaa8a9138b32b3f Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Fri, 17 Aug 2018 21:04:22 +0900
Subject: [PATCH] NEW_TOKEN frame

---
 lib/includes/ngtcp2/ngtcp2.h | 10 +++++-
 lib/ngtcp2_pkt.c             | 67 ++++++++++++++++++++++++++++++++++++
 lib/ngtcp2_pkt.h             | 28 +++++++++++++++
 tests/main.c                 |  2 ++
 tests/ngtcp2_pkt_test.c      | 26 ++++++++++++++
 tests/ngtcp2_pkt_test.h      |  1 +
 6 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index d45fd043..45d0280b 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -258,7 +258,8 @@ typedef enum {
   NGTCP2_FRAME_PATH_CHALLENGE = 0x0e,
   NGTCP2_FRAME_PATH_RESPONSE = 0x0f,
   NGTCP2_FRAME_STREAM = 0x10,
-  NGTCP2_FRAME_CRYPTO = 0x18
+  NGTCP2_FRAME_CRYPTO = 0x18,
+  NGTCP2_FRAME_NEW_TOKEN = 0x19
 } ngtcp2_frame_type;
 
 typedef enum {
@@ -492,6 +493,12 @@ typedef struct {
   ngtcp2_vec data[1];
 } ngtcp2_crypto;
 
+typedef struct {
+  uint8_t type;
+  size_t tokenlen;
+  const uint8_t *token;
+} ngtcp2_new_token;
+
 typedef union {
   uint8_t type;
   ngtcp2_stream stream;
@@ -512,6 +519,7 @@ typedef union {
   ngtcp2_path_challenge path_challenge;
   ngtcp2_path_response path_response;
   ngtcp2_crypto crypto;
+  ngtcp2_new_token new_token;
 } ngtcp2_frame;
 
 typedef enum {
diff --git a/lib/ngtcp2_pkt.c b/lib/ngtcp2_pkt.c
index 6ed9459e..57b26615 100644
--- a/lib/ngtcp2_pkt.c
+++ b/lib/ngtcp2_pkt.c
@@ -368,6 +368,9 @@ ssize_t ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
                                                  payloadlen);
   case NGTCP2_FRAME_CRYPTO:
     return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen);
+  case NGTCP2_FRAME_NEW_TOKEN:
+    return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload,
+                                             payloadlen);
   default:
     if (has_mask(type, NGTCP2_FRAME_STREAM)) {
       return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen);
@@ -1138,6 +1141,45 @@ ssize_t ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
   return (ssize_t)len;
 }
 
+ssize_t ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+                                          const uint8_t *payload,
+                                          size_t payloadlen) {
+  size_t len = 1 + 1;
+  const uint8_t *p;
+  size_t n;
+  size_t datalen;
+
+  if (payloadlen < len) {
+    return NGTCP2_ERR_FRAME_ENCODING;
+  }
+
+  p = payload + 1;
+
+  n = ngtcp2_get_varint_len(p);
+  len += n - 1;
+
+  if (payloadlen < len) {
+    return NGTCP2_ERR_FRAME_ENCODING;
+  }
+
+  datalen = ngtcp2_get_varint(&n, p);
+  len += datalen;
+
+  if (payloadlen < len) {
+    return NGTCP2_ERR_FRAME_ENCODING;
+  }
+
+  dest->type = NGTCP2_FRAME_NEW_TOKEN;
+  dest->tokenlen = datalen;
+  p += n;
+  dest->token = p;
+  p += dest->tokenlen;
+
+  assert((size_t)(p - payload) == len);
+
+  return (ssize_t)len;
+}
+
 ssize_t ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, ngtcp2_frame *fr) {
   switch (fr->type) {
   case NGTCP2_FRAME_STREAM:
@@ -1185,6 +1227,8 @@ ssize_t ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, ngtcp2_frame *fr) {
                                                  &fr->path_response);
   case NGTCP2_FRAME_CRYPTO:
     return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto);
+  case NGTCP2_FRAME_NEW_TOKEN:
+    return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token);
   default:
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
@@ -1613,6 +1657,29 @@ ssize_t ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
   return (ssize_t)len;
 }
 
+ssize_t ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+                                          const ngtcp2_new_token *fr) {
+  size_t len = 1 + ngtcp2_put_varint_len(fr->tokenlen) + fr->tokenlen;
+  uint8_t *p;
+
+  if (outlen < len) {
+    return NGTCP2_ERR_NOBUF;
+  }
+
+  p = out;
+
+  *p++ = NGTCP2_FRAME_NEW_TOKEN;
+
+  p = ngtcp2_put_varint(p, fr->tokenlen);
+  if (fr->tokenlen) {
+    p = ngtcp2_cpymem(p, fr->token, fr->tokenlen);
+  }
+
+  assert((size_t)(p - out) == len);
+
+  return (ssize_t)len;
+}
+
 ssize_t ngtcp2_pkt_write_version_negotiation(uint8_t *dest, size_t destlen,
                                              uint8_t unused_random,
                                              const ngtcp2_cid *dcid,
diff --git a/lib/ngtcp2_pkt.h b/lib/ngtcp2_pkt.h
index e3a31f3e..6c2878c5 100644
--- a/lib/ngtcp2_pkt.h
+++ b/lib/ngtcp2_pkt.h
@@ -414,6 +414,21 @@ ssize_t ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
                                        const uint8_t *payload,
                                        size_t payloadlen);
 
+/*
+ * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from
+ * |payload| of length |payloadlen|.  The result is stored in the
+ * object pointed by |dest|.  NEW_TOKEN frame must start at
+ * payload[0].  This function finishes when it decodes one NEW_TOKEN
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ *     Payload is too short to include NEW_TOKEN frame.
+ */
+ssize_t ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+                                          const uint8_t *payload,
+                                          size_t payloadlen);
+
 /*
  * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the
  * buffer pointed by |out| of length |outlen|.
@@ -661,6 +676,19 @@ ssize_t ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
 ssize_t ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
                                        const ngtcp2_crypto *fr);
 
+/*
+ * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ *     Buffer does not have enough capacity to write a frame.
+ */
+ssize_t ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+                                          const ngtcp2_new_token *fr);
+
 /*
  * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for
  * |pkt_num|, which is expected to be least significant |n| bits.  The
diff --git a/tests/main.c b/tests/main.c
index cc9414d0..aef737a0 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -108,6 +108,8 @@ int main() {
                    test_ngtcp2_pkt_encode_path_response_frame) ||
       !CU_add_test(pSuite, "pkt_encode_crypto_frame",
                    test_ngtcp2_pkt_encode_crypto_frame) ||
+      !CU_add_test(pSuite, "pkt_encode_new_token_frame",
+                   test_ngtcp2_pkt_encode_new_token_frame) ||
       !CU_add_test(pSuite, "pkt_adjust_pkt_num",
                    test_ngtcp2_pkt_adjust_pkt_num) ||
       !CU_add_test(pSuite, "pkt_validate_ack", test_ngtcp2_pkt_validate_ack) ||
diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c
index a4255564..0d7fb219 100644
--- a/tests/ngtcp2_pkt_test.c
+++ b/tests/ngtcp2_pkt_test.c
@@ -913,6 +913,32 @@ void test_ngtcp2_pkt_encode_crypto_frame(void) {
                         fr.crypto.data[0].len));
 }
 
+void test_ngtcp2_pkt_encode_new_token_frame(void) {
+  const uint8_t token[] = "0123456789abcdef2";
+  uint8_t buf[256];
+  ngtcp2_frame fr, nfr;
+  ssize_t rv;
+  size_t framelen;
+
+  fr.type = NGTCP2_FRAME_NEW_TOKEN;
+  fr.new_token.tokenlen = strsize(token);
+  fr.new_token.token = token;
+
+  framelen = 1 + 1 + strsize(token);
+
+  rv = ngtcp2_pkt_encode_new_token_frame(buf, sizeof(buf), &fr.new_token);
+
+  CU_ASSERT((ssize_t)framelen == rv);
+
+  rv = ngtcp2_pkt_decode_new_token_frame(&nfr.new_token, buf, framelen);
+
+  CU_ASSERT((ssize_t)framelen == rv);
+  CU_ASSERT(fr.type == nfr.type);
+  CU_ASSERT(fr.new_token.tokenlen == nfr.new_token.tokenlen);
+  CU_ASSERT(0 == memcmp(fr.new_token.token, nfr.new_token.token,
+                        fr.new_token.tokenlen));
+}
+
 void test_ngtcp2_pkt_adjust_pkt_num(void) {
   CU_ASSERT(0xaa831f94llu ==
             ngtcp2_pkt_adjust_pkt_num(0xaa82f30ellu, 0x1f94, 16));
diff --git a/tests/ngtcp2_pkt_test.h b/tests/ngtcp2_pkt_test.h
index 5267a0f5..9ca8bbf0 100644
--- a/tests/ngtcp2_pkt_test.h
+++ b/tests/ngtcp2_pkt_test.h
@@ -51,6 +51,7 @@ void test_ngtcp2_pkt_encode_stop_sending_frame(void);
 void test_ngtcp2_pkt_encode_path_challenge_frame(void);
 void test_ngtcp2_pkt_encode_path_response_frame(void);
 void test_ngtcp2_pkt_encode_crypto_frame(void);
+void test_ngtcp2_pkt_encode_new_token_frame(void);
 void test_ngtcp2_pkt_adjust_pkt_num(void);
 void test_ngtcp2_pkt_validate_ack(void);
 void test_ngtcp2_pkt_write_stateless_reset(void);
-- 
GitLab