From: Arran Cudbard-Bell Date: Sat, 25 Jul 2020 01:14:05 +0000 (-0400) Subject: sbuff: Add escapes to fr_sbuff_out_bstrncpy_until X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c83a772d2655922d09f83fe8f1952f2a233ca84e;p=thirdparty%2Ffreeradius-server.git sbuff: Add escapes to fr_sbuff_out_bstrncpy_until --- diff --git a/src/lib/util/sbuff.c b/src/lib/util/sbuff.c index 0fd6c3b3c77..171c5bee630 100644 --- a/src/lib/util/sbuff.c +++ b/src/lib/util/sbuff.c @@ -299,6 +299,16 @@ do { \ fr_sbuff_advance(_in, _copied); \ } while(0) +/** Constrain end pointer to prevent advancing more than the amount the called specified + * + * @param[in] _sbuff to constrain. + * @param[in] _max maximum amount to advance. + * @param[in] _used how much we've advanced so far. + * @return a temporary end pointer. + */ +#define CONSTRAINED_END(_sbuff, _max, _used) \ + (((_max) - (_used)) > fr_sbuff_remaining(_sbuff) ? (_sbuff)->end : (_sbuff)->p + ((_max) - (_used))) + /** Copy as many bytes as possible from a sbuff to a sbuff * * Copy size is limited by available data in sbuff and space in output sbuff. @@ -397,26 +407,24 @@ size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len bool const allowed[static UINT8_MAX + 1]) { fr_sbuff_t our_in = FR_SBUFF_NO_ADVANCE(in); - size_t remaining; - size_t chunk_len; CHECK_SBUFF_INIT(in); - do { - + while (fr_sbuff_used_total(&our_in) < len) { char *p; + char *end; - remaining = (len - fr_sbuff_used_total(&our_in)); if (FR_SBUFF_CANT_EXTEND(&our_in)) break; - chunk_len = fr_sbuff_remaining(&our_in); - if (chunk_len > remaining) chunk_len = remaining; + p = our_in.p; + end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in)); - for (p = our_in.p; (p < (our_in.start + chunk_len)) && allowed[(uint8_t)*p]; p++); - chunk_len = p - our_in.p; + while((p < end) && allowed[(uint8_t)*p]) p++; - FILL_OR_GOTO_DONE(out, &our_in, chunk_len); - } while (remaining && chunk_len); + FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p); + + if (p != end) break; /* stopped early, break */ + }; done: return fr_sbuff_set(in, &our_in); @@ -433,34 +441,48 @@ done: * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. * @param[in] until Characters which stop the copy operation. + * @param[in] escape If not '\0', ignore characters in the until set when + * prefixed with this escape character. * @return * - 0 no bytes copied. * - >0 the number of bytes copied. */ size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1]) + bool const until[static UINT8_MAX + 1], char escape) { fr_sbuff_t our_in = FR_SBUFF_NO_ADVANCE(in); - size_t remaining; - size_t chunk_len; + bool do_escape = false; /* Track state across extensions */ CHECK_SBUFF_INIT(in); - do { - + while (fr_sbuff_used_total(&our_in) < len) { char *p; + char *end; - remaining = (len - fr_sbuff_used_total(&our_in)); if (FR_SBUFF_CANT_EXTEND(&our_in)) break; - chunk_len = fr_sbuff_remaining(&our_in); - if (chunk_len > remaining) chunk_len = remaining; + p = our_in.p; + end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in)); - for (p = our_in.p; (p < (our_in.start + chunk_len)) && !until[(uint8_t)*p]; p++); - chunk_len = p - our_in.p; + if (escape == '\0') { + while((p < end) && !until[(uint8_t)*p]) p++; + } else { + while (p < end) { + if (do_escape) { + do_escape = false; + } else if (*p == escape) { + do_escape = true; + } else if (until[(uint8_t)*p]) { + break; + } + p++; + } + } - FILL_OR_GOTO_DONE(out, &our_in, chunk_len); - } while (remaining && chunk_len); + FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p); + + if (p != end) break; /* stopped early, break */ + } done: return fr_sbuff_set(in, &our_in); @@ -883,16 +905,6 @@ size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t n return fr_sbuff_advance(sbuff, needle_len); } -/** Constrain end pointer to prevent advancing more than the amount the called specified - * - * @param[in] _sbuff to constrain. - * @param[in] _max maximum amount to advance. - * @param[in] _used how much we've advanced so far. - * @return a temporary end pointer. - */ -#define CONSTRAINED_END(_sbuff, _max, _used) \ - (((_max) - (_used)) > fr_sbuff_remaining(sbuff) ? sbuff->end : sbuff->p + ((_max) - (_used))) - /** Wind position to the first non-whitespace character * * @param[in] sbuff sbuff to search in. diff --git a/src/lib/util/sbuff.h b/src/lib/util/sbuff.h index a3ace65c950..880b51ddaeb 100644 --- a/src/lib/util/sbuff.h +++ b/src/lib/util/sbuff.h @@ -668,7 +668,7 @@ size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len bool const allowed[static UINT8_MAX + 1]); size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1]); + bool const until[static UINT8_MAX + 1], char escape); /** Find the longest prefix in an sbuff * @@ -724,8 +724,8 @@ static inline size_t fr_sbuff_out_abstrncpy_allowed(TALLOC_CTX *ctx, char **out, SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_allowed, in, len, allowed); static inline size_t fr_sbuff_out_abstrncpy_until(TALLOC_CTX *ctx, char **out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1]) -SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_until, in, len, until); + bool const until[static UINT8_MAX + 1], char escape) +SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_until, in, len, until, escape); /** @} */ /** @name Look for a token in a particular format, parse it, and write it to the output pointer diff --git a/src/lib/util/sbuff_tests.c b/src/lib/util/sbuff_tests.c index a2e10fed575..3c28c177dd8 100644 --- a/src/lib/util/sbuff_tests.c +++ b/src/lib/util/sbuff_tests.c @@ -291,34 +291,47 @@ static void test_bstrncpy_until(void) * set. */ TEST_CASE("Copy 5 bytes to out"); - slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 5, (bool[UINT8_MAX + 1]){ }); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 5, + (bool[UINT8_MAX + 1]){ }, '\0'); TEST_CHECK_SLEN(5, slen); TEST_CHECK_STRCMP("i am ", out); TEST_CHECK_STRCMP("a test string", sbuff.p); TEST_CASE("Copy 13 bytes to out"); - slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 13, (bool[UINT8_MAX + 1]){ }); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 13, + (bool[UINT8_MAX + 1]){ }, '\0'); TEST_CHECK_SLEN(13, slen); TEST_CHECK_STRCMP("a test string", out); TEST_CHECK_STRCMP("", sbuff.p); TEST_CHECK(sbuff.p == sbuff.end); TEST_CASE("Copy would overrun input"); - slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 1, (bool[UINT8_MAX + 1]){ }); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 1, + (bool[UINT8_MAX + 1]){ }, '\0'); TEST_CHECK_SLEN(0, slen); TEST_CHECK(sbuff.p == sbuff.end); + TEST_CASE("Check escapes"); + fr_sbuff_set_to_start(&sbuff); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, 'n'); + TEST_CHECK_SLEN(18, slen); + TEST_CHECK_STRCMP("i am a test string", out); + TEST_CHECK_STRCMP("", sbuff.p); + TEST_CASE("Copy would overrun output (and SIZE_MAX special value)"); fr_sbuff_init(&sbuff, in_long, sizeof(in_long)); - slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ }); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, + SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\0'); TEST_CHECK_SLEN(18, slen); TEST_CHECK_STRCMP("i am a longer test", out); TEST_CASE("Zero length output buffer"); fr_sbuff_set_to_start(&sbuff); out[0] = 'a'; - slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, (size_t)1), &sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ }); + slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, (size_t)1), &sbuff, + SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\0'); TEST_CHECK_SLEN(0, slen); TEST_CHECK(out[0] == '\0'); /* should be set to \0 */ TEST_CHECK(sbuff.p == sbuff.start); @@ -329,28 +342,28 @@ static void test_bstrncpy_until(void) TEST_CASE("Copy until first t"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }); + (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0'); TEST_CHECK_SLEN(14, slen); TEST_CHECK_STRCMP("i am a longer ", out); TEST_CASE("Copy until first t with length constraint (same len as token)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 15), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }); + (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0'); TEST_CHECK_SLEN(14, slen); TEST_CHECK_STRCMP("i am a longer ", out); TEST_CASE("Copy until first t with length constraint (one shorter than token)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }); + (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0'); TEST_CHECK_SLEN(13, slen); TEST_CHECK_STRCMP("i am a longer", out); TEST_CASE("Zero length token (should still be terminated)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['i'] = true }); + (bool[UINT8_MAX + 1]){ ['i'] = true }, '\0'); TEST_CHECK_SLEN(0, slen); TEST_CHECK_STRCMP("", out); } @@ -697,31 +710,36 @@ static void test_adv_past_allowed(void) static void test_adv_until(void) { fr_sbuff_t sbuff; - char const in[] = " abcdefgh ijklmnop"; + char const in[] = " abcdefgh ijklmnopp"; TEST_CASE("Check for token at beginning of string"); fr_sbuff_init(&sbuff, in, sizeof(in)); - TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true })); - TEST_CHECK_STRCMP(" abcdefgh ijklmnop", sbuff.p); + TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, '\0')); + TEST_CHECK_STRCMP(" abcdefgh ijklmnopp", sbuff.p); TEST_CASE("Check for token not at beginning of string"); fr_sbuff_init(&sbuff, in, sizeof(in)); - TEST_CHECK_LEN(1, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true })); - TEST_CHECK_STRCMP("abcdefgh ijklmnop", sbuff.p); + TEST_CHECK_LEN(1, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }, '\0')); + TEST_CHECK_STRCMP("abcdefgh ijklmnopp", sbuff.p); TEST_CASE("Check for token with zero length string"); fr_sbuff_init(&sbuff, in, 0 + 1); - TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true })); + TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }, '\0')); TEST_CHECK(sbuff.p == sbuff.start); TEST_CASE("Check for token that is not in the string"); fr_sbuff_init(&sbuff, in, sizeof(in)); - TEST_CHECK_LEN(18, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['|'] = true })); + TEST_CHECK_LEN(19, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['|'] = true }, '\0')); TEST_CHECK(sbuff.p == sbuff.end); + TEST_CASE("Check escapes"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + TEST_CHECK_LEN(18, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['p'] = true }, 'o')); + TEST_CHECK_STRCMP("p", sbuff.p); + TEST_CASE("Check for token that is not in the string with length constraint"); fr_sbuff_init(&sbuff, in, sizeof(in)); - TEST_CHECK_LEN(5, fr_sbuff_adv_until(&sbuff, 5, (bool[UINT8_MAX + 1]){ ['|'] = true })); + TEST_CHECK_LEN(5, fr_sbuff_adv_until(&sbuff, 5, (bool[UINT8_MAX + 1]){ ['|'] = true }, '\0')); TEST_CHECK(sbuff.p == (sbuff.start + 5)); }