]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
sbuff: Add escapes to fr_sbuff_out_bstrncpy_until
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 25 Jul 2020 01:14:05 +0000 (21:14 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 25 Jul 2020 01:14:05 +0000 (21:14 -0400)
src/lib/util/sbuff.c
src/lib/util/sbuff.h
src/lib/util/sbuff_tests.c

index 0fd6c3b3c77acf9666ca72cc50f40e850b118b4a..171c5bee630346a8660f5e241cc9649c82242881 100644 (file)
@@ -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.
index a3ace65c950a7cdeecd250e22b90f3e559bd3e9e..880b51ddaebebd895407e63c144a16d52376b652 100644 (file)
@@ -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
index a2e10fed575f9586e458a17dc440d83d3ace592d..3c28c177dd80e46e6d54a1c7b84e227e476c1074 100644 (file)
@@ -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));
 }