]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tools: add cbor encode helpers
authorAurelien DARRAGON <adarragon@haproxy.com>
Mon, 22 Apr 2024 15:38:44 +0000 (17:38 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Fri, 26 Apr 2024 16:39:32 +0000 (18:39 +0200)
Add cbor helpers to encode strings (bytes/text) and integers according to
RFC8949, also add cbor_encode_ctx struct to pass encoding options such as
how to encode a single byte.

include/haproxy/tools-t.h
include/haproxy/tools.h
src/tools.c

index 38762ac2b7321c28580d9737f4ae0b62bb751c46..b6d674d8b23c1e7f352287728bfa676ac36d5fbb 100644 (file)
@@ -165,4 +165,20 @@ struct net_addr_type {
        int xprt_type;  // transport layer
 };
 
+/* To easily pass context to cbor encode functions
+ */
+struct cbor_encode_ctx {
+       /* function pointer that cbor encode functions will use to encode a
+        * single byte.
+        *
+        * The function needs to return the position of the last written byte
+        * on success and NULL on failure. The function cannot write past <stop>
+        */
+       char *(*e_byte_fct)(struct cbor_encode_ctx *ctx,
+                           char *start, char *stop, uint8_t byte);
+
+       /* to pass some context to the encode_byte fct */
+       void *e_byte_fct_ctx;
+};
+
 #endif /* _HAPROXY_TOOLS_T_H */
index 4e1a6dfa3f869059d30103adc30db18b8e8d3422..8538a8ebc263cf54c0609d9c57cb3c4be8609849 100644 (file)
@@ -432,6 +432,25 @@ char *escape_string(char *start, char *stop,
                    const char escape, const long *map,
                    const char *string, const char *string_stop);
 
+/* Below are RFC8949 compliant cbor encode helper functions, see source
+ * file for functions descriptions
+ */
+char *cbor_encode_uint64_prefix(struct cbor_encode_ctx *ctx,
+                                char *start, char *stop,
+                                uint64_t value, uint8_t prefix);
+char *cbor_encode_int64(struct cbor_encode_ctx *ctx,
+                        char *start, char *stop, int64_t value);
+char *cbor_encode_bytes_prefix(struct cbor_encode_ctx *ctx,
+                               char *start, char *stop,
+                               const char *bytes, size_t len,
+                               uint8_t prefix);
+char *cbor_encode_bytes(struct cbor_encode_ctx *ctx,
+                        char *start, char *stop,
+                        const char *bytes, size_t len);
+char *cbor_encode_text(struct cbor_encode_ctx *ctx,
+                       char *start, char *stop,
+                       const char *text, size_t len);
+
 /* Check a string for using it in a CSV output format. If the string contains
  * one of the following four char <">, <,>, CR or LF, the string is
  * encapsulated between <"> and the <"> are escaped by a <""> sequence.
index 114cbf6ddf1dfa07e0a00d11873c82c3ed350b95..19d48d62e4d6b38c5e853470fa0891f7f66b5cb1 100644 (file)
@@ -2064,6 +2064,164 @@ char *escape_string(char *start, char *stop,
        return NULL;
 }
 
+/* CBOR helper to encode an uint64 value with prefix (3bits MAJOR type)
+ * according to RFC8949
+ *
+ * CBOR encode ctx is provided in <ctx>
+ *
+ * Returns the position of the last written byte on success and NULL on
+ * error. The function cannot write past <stop>
+ */
+char *cbor_encode_uint64_prefix(struct cbor_encode_ctx *ctx,
+                                char *start, char *stop, uint64_t value,
+                                uint8_t prefix)
+{
+       int nb_bytes = 0;
+
+       /*
+        * For encoding logic, see:
+        * https://www.rfc-editor.org/rfc/rfc8949.html#name-specification-of-the-cbor-e
+        */
+       if (value < 24) {
+               /* argument is the value itself */
+               prefix |= value;
+       }
+       else {
+               if (value <= 0xFFU) {
+                       /* 1-byte */
+                       nb_bytes = 1;
+                       prefix |= 24; // 0x18
+               }
+               else if (value <= 0xFFFFU) {
+                       /* 2 bytes */
+                       nb_bytes = 2;
+                       prefix |= 25; // 0x19
+               }
+               else if (value <= 0xFFFFFFFFU) {
+                       /* 4 bytes */
+                       nb_bytes = 4;
+                       prefix |= 26; // 0x1A
+               }
+               else {
+                       /* 8 bytes */
+                       nb_bytes = 8;
+                       prefix |= 27; // 0x1B
+               }
+       }
+
+       start = ctx->e_byte_fct(ctx, start, stop, prefix);
+       if (start == NULL)
+               return NULL;
+
+       /* encode 1 byte at a time from higher bits to lower bits */
+       while (nb_bytes) {
+               uint8_t cur_byte = (value >> ((nb_bytes - 1) * 8)) & 0xFFU;
+
+               start = ctx->e_byte_fct(ctx, start, stop, cur_byte);
+               if (start == NULL)
+                       return NULL;
+
+               nb_bytes--;
+       }
+
+       return start;
+}
+
+/* CBOR helper to encode an int64 value according to RFC8949
+ *
+ * CBOR encode ctx is provided in <ctx>
+ *
+ * Returns the position of the last written byte on success and NULL on
+ * error. The function cannot write past <stop>
+ */
+char *cbor_encode_int64(struct cbor_encode_ctx *ctx,
+                        char *start, char *stop, int64_t value)
+{
+       uint64_t absolute_value = llabs(value);
+       int cbor_prefix;
+
+       /*
+        * For encoding logic, see:
+        * https://www.rfc-editor.org/rfc/rfc8949.html#name-specification-of-the-cbor-e
+        */
+       if (value >= 0)
+               cbor_prefix = 0x00; // unsigned int
+       else {
+               cbor_prefix = 0x20; // negative int
+               /* N-1 for negative int */
+               absolute_value -= 1;
+       }
+       return cbor_encode_uint64_prefix(ctx, start, stop,
+                                        absolute_value, cbor_prefix);
+}
+
+/* CBOR helper to encode a <prefix> string chunk according to RFC8949
+ *
+ * if <bytes> is NULL, then only the <prefix> (with length) will be
+ * emitted
+ *
+ * CBOR encode ctx is provided in <ctx>
+ *
+ * Returns the position of the last written byte on success and NULL on
+ * error. The function cannot write past <stop>
+ */
+char *cbor_encode_bytes_prefix(struct cbor_encode_ctx *ctx,
+                               char *start, char *stop,
+                               const char *bytes, size_t len,
+                               uint8_t prefix)
+{
+
+       size_t it = 0;
+
+       /* write prefix (with text length as argument) */
+       start = cbor_encode_uint64_prefix(ctx, start, stop,
+                                         len, prefix);
+       if (start == NULL)
+               return NULL;
+
+       /* write actual bytes if provided */
+       while (bytes && it < len) {
+               start = ctx->e_byte_fct(ctx, start, stop, bytes[it]);
+               if (start == NULL)
+                       return NULL;
+               it++;
+       }
+       return start;
+}
+
+/* CBOR helper to encode a text chunk according to RFC8949
+ *
+ * if <text> is NULL, then only the text prefix (with length) will be emitted
+ *
+ * CBOR encode ctx is provided in <ctx>
+ *
+ * Returns the position of the last written byte on success and NULL on
+ * error. The function cannot write past <stop>
+ */
+char *cbor_encode_text(struct cbor_encode_ctx *ctx,
+                       char *start, char *stop,
+                       const char *text, size_t len)
+{
+       return cbor_encode_bytes_prefix(ctx, start, stop, text, len, 0x60);
+}
+
+/* CBOR helper to encode a byte string chunk according to RFC8949
+ *
+ * if <bytes> is NULL, then only the byte string prefix (with length) will be
+ * emitted
+ *
+ * CBOR encode ctx is provided in <ctx>
+ *
+ * Returns the position of the last written byte on success and NULL on
+ * error. The function cannot write past <stop>
+ */
+char *cbor_encode_bytes(struct cbor_encode_ctx *ctx,
+                        char *start, char *stop,
+                        const char *bytes, size_t len)
+{
+       return cbor_encode_bytes_prefix(ctx, start, stop, bytes, len, 0x40);
+}
+
 /* Check a string for using it in a CSV output format. If the string contains
  * one of the following four char <">, <,>, CR or LF, the string is
  * encapsulated between <"> and the <"> are escaped by a <""> sequence.