From: Aurelien DARRAGON Date: Mon, 22 Apr 2024 15:38:44 +0000 (+0200) Subject: MINOR: tools: add cbor encode helpers X-Git-Tag: v3.0-dev9~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=810303e3e64b3ff9af1de8b8876f0f3cded88837;p=thirdparty%2Fhaproxy.git MINOR: tools: add cbor encode helpers 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. --- diff --git a/include/haproxy/tools-t.h b/include/haproxy/tools-t.h index 38762ac2b7..b6d674d8b2 100644 --- a/include/haproxy/tools-t.h +++ b/include/haproxy/tools-t.h @@ -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 + */ + 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 */ diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h index 4e1a6dfa3f..8538a8ebc2 100644 --- a/include/haproxy/tools.h +++ b/include/haproxy/tools.h @@ -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. diff --git a/src/tools.c b/src/tools.c index 114cbf6ddf..19d48d62e4 100644 --- a/src/tools.c +++ b/src/tools.c @@ -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 + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +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 + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +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 string chunk according to RFC8949 + * + * if is NULL, then only the (with length) will be + * emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +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 is NULL, then only the text prefix (with length) will be emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +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 is NULL, then only the byte string prefix (with length) will be + * emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +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.