#include #include #include #include #include #include "lib/replace/replace.h" #include #include #include #include #include #include "lib/util/samba_util.h" /* These flags say what can be asserted about a relationship between a string and its supposedly escaped equivalent. The first part of the flag name indicates the direction of transformation; the second part is the expected result. For example, ESCAPE_EQ means the escape is expected to succeed and result is expected to be equal to the given answer. ESCAPE_EQ_CASECMP is only equal when compared case-insensitively. UNESCAPE_ERR means unescaping the escaped string should result in an error. */ #define UNESCAPE_ERR 1 #define ESCAPE_ERR 2 #define ESCAPE_EQ 4 #define UNESCAPE_EQ 8 #define ESCAPE_NE 16 #define UNESCAPE_NE 32 #define ESCAPE_EQ_CASECMP 64 struct rfc1738_test { const char *escaped; /* original for unescape; result for escape */ const char *unescaped; /* result in unescape; original for escape */ uint32_t flags; /* see above */ int unesc_len; /* end - start will be this */ int unesc_strlen; /* strlen() will say this */ int esc_len; /* escaped string length */ }; /* unreserved = ALPHA DIGIT - . _ ~ */ char spectrum[255 + 1]; char spectrum_escaped[255 * 3 + 1]; struct rfc1738_test examples[] = { #define SIMPLE1 "this_is_a_simple-string._With_no_escapes~" /* maps to self */ { SIMPLE1, SIMPLE1, ESCAPE_EQ | UNESCAPE_EQ, /* round trip should work */ sizeof(SIMPLE1) - 1, sizeof(SIMPLE1) - 1, sizeof(SIMPLE1) - 1, }, #define SIMPLE2 "no escapes, but\n non-printables \xc5\x8d\x99" #define SIMPLE2_ESC "no%20escapes%2C%20but%0A%20non-printables%20%C5%8D%99" { SIMPLE2_ESC, SIMPLE2, ESCAPE_EQ | UNESCAPE_EQ, sizeof(SIMPLE2) - 1, sizeof(SIMPLE2) - 1, sizeof(SIMPLE2_ESC) - 1, }, #define SIMPLE3 "this @#$^&*()_+{}:;" #define SIMPLE3_ESC "this%20%40%23%24%5E%26%2A%28%29_%2B%7B%7D%3A%3B" { SIMPLE3_ESC, SIMPLE3, ESCAPE_EQ | UNESCAPE_EQ, sizeof(SIMPLE3) - 1, sizeof(SIMPLE3) - 1, sizeof(SIMPLE3_ESC) - 1, }, #define ESCAPE1 "%/\x06this string has expected escapes" #define ESCAPE1_ESC "%25%2F%06this%20string%20has%20expected%20escapes" #define ESCAPE1_ESC_ESC "%2525%252F%2506this%2520string%2520has%2520expected"\ "%2520escapes" { ESCAPE1_ESC, ESCAPE1, ESCAPE_EQ | UNESCAPE_EQ, sizeof(ESCAPE1) - 1, sizeof(ESCAPE1) - 1, sizeof(ESCAPE1_ESC) - 1, }, { ESCAPE1_ESC_ESC, /*re-escaping */ ESCAPE1_ESC, ESCAPE_EQ | UNESCAPE_EQ, sizeof(ESCAPE1_ESC) - 1, sizeof(ESCAPE1_ESC) - 1, sizeof(ESCAPE1_ESC_ESC) - 1, }, #define ESCAPE2 "%25%2f%06-this-string-has-expected-lowercase-escapes-%ab" #define ESCAPE2_UNESC "%/\x06-this-string-has-expected-lowercase-escapes-\xab" { ESCAPE2, ESCAPE2_UNESC, ESCAPE_EQ_CASECMP | UNESCAPE_EQ, /* escape won't match case */ sizeof(ESCAPE2_UNESC) - 1, sizeof(ESCAPE2_UNESC) - 1, sizeof(ESCAPE2) - 1, }, #define ESCAPE3 "%25%2f%06 %32 %44 %6a%AA THIS string h%61s random escapes %ab" #define ESCAPE3_UNESC "%/\x06 2 D j\xAA THIS string has random escapes \xab" { ESCAPE3, ESCAPE3_UNESC, ESCAPE_NE | UNESCAPE_EQ, /* escape will have escaped spaces */ sizeof(ESCAPE3_UNESC) - 1, sizeof(ESCAPE3_UNESC) - 1, sizeof(ESCAPE3) - 1, }, #define ESCAPE4 "%25%25%25" /* */ #define ESCAPE4_UNESC "%%%" /* */ #define ESCAPE4_ESC "%2525%2525%2525" { ESCAPE4, ESCAPE4_UNESC, ESCAPE_EQ | UNESCAPE_EQ, sizeof(ESCAPE4_UNESC) - 1, sizeof(ESCAPE4_UNESC) - 1, sizeof(ESCAPE4) - 1, }, { ESCAPE4_ESC, ESCAPE4, ESCAPE_EQ | UNESCAPE_EQ, sizeof(ESCAPE4) - 1, sizeof(ESCAPE4) - 1, sizeof(ESCAPE4_ESC) - 1, }, #define BAD1 "trailing percent is bad %" #define BAD1_ESC "trailing%20percent%20is%20bad%20%25" { BAD1_ESC, BAD1, UNESCAPE_EQ |ESCAPE_EQ, sizeof(BAD1) - 1, sizeof(BAD1) - 1, sizeof(BAD1_ESC) - 1, }, { BAD1, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD1) - 1, }, #define BAD2 "trailing percent is bad %1" #define BAD3 "bad characters %1 " { BAD2, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD2) - 1, }, { BAD3, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD3) - 1, }, #define BAD4 "bad characters %1 " { BAD4, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD4) - 1, }, #define BAD5 "bad characters %1- " { BAD5, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD5) - 1, }, #define BAD6 "bad characters %1G " { BAD6, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD6) - 1, }, #define BAD7 "bad characters %%1 " { BAD7, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD7) - 1, }, #define BAD8 "bad characters %sb " { BAD8, NULL, UNESCAPE_ERR, 0, 0, sizeof(BAD8) - 1, }, #define BAD_SSCANF "sscanf would be happy with this\n" #define BAD_SSCANF_ESC "sscanf would be happy with this% a" { BAD_SSCANF_ESC, BAD_SSCANF, ESCAPE_NE | UNESCAPE_ERR, sizeof(BAD_SSCANF) - 1, sizeof(BAD_SSCANF) - 1, sizeof(BAD_SSCANF_ESC) - 1, }, /* now try some with zeros in. escaping can't see past zeros, and the result is truncated */ #define ZERO "%00" #define ZERO_UNESC "\0" { ESCAPE4 ZERO ESCAPE4, ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC, ESCAPE_NE | UNESCAPE_EQ, sizeof(ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC) - 1, sizeof(ESCAPE4_UNESC) - 1, sizeof(ESCAPE4 ZERO ESCAPE4) - 1, }, { ZERO ESCAPE4, ZERO_UNESC ESCAPE4_UNESC, ESCAPE_NE | UNESCAPE_EQ, sizeof(ZERO_UNESC ESCAPE4_UNESC) - 1, 0, sizeof(ZERO ESCAPE4) - 1, }, { ZERO, ZERO_UNESC, ESCAPE_NE | UNESCAPE_EQ, sizeof(ZERO_UNESC) - 1, 0, sizeof(ZERO) - 1, }, { spectrum_escaped, spectrum, ESCAPE_EQ | UNESCAPE_EQ, 255, 255, 255 * 3, }, }; static struct rfc1738_test * dup_test(struct rfc1738_test *src) { struct rfc1738_test *dest = malloc(sizeof(*dest)); char *esc = NULL, *unesc = NULL; if (dest == NULL) { return NULL; } *dest = *src; if (src->esc_len) { esc = malloc(src->esc_len + 1); if (esc == NULL) { free(dest); return NULL; } memcpy(esc, src->escaped, src->esc_len + 1); dest->escaped = esc; } if (src->unesc_len) { unesc = malloc(src->unesc_len + 1); if (unesc == NULL) { free(esc); free(dest); return NULL; } memcpy(unesc, src->unescaped, src->unesc_len + 1); dest->unescaped = unesc; } return dest; } static void free_test(struct rfc1738_test *t) { free(discard_const_p(char, t->escaped)); free(discard_const_p(char, t->unescaped)); free(t); } static void test_unescape(void **state) { uint i; char *s, *e; struct rfc1738_test *test, *orig; for (i = 0; i < ARRAY_SIZE(examples); i++) { orig = &examples[i]; if ((orig->flags & (UNESCAPE_ERR | UNESCAPE_EQ | UNESCAPE_NE)) == 0) { continue; } test = dup_test(&examples[i]); s = discard_const_p(char, test->escaped); e = rfc1738_unescape(s); if (test->flags & UNESCAPE_ERR) { assert_null(e); free_test(test); continue; } assert_non_null(e); assert_int_equal(e - s, test->unesc_len); if (test->flags & UNESCAPE_EQ) { assert_memory_equal(s, orig->unescaped, orig->unesc_len); assert_int_equal(strlen(s), orig->unesc_strlen); } else { assert_memory_not_equal(s, orig->unescaped, orig->unesc_len); assert_int_equal(strlen(s), orig->unesc_strlen); } free_test(test); } } static void test_escape(void **state) { uint i; char *s, *e; struct rfc1738_test *test, *orig; for (i = 0; i < ARRAY_SIZE(examples); i++) { orig = &examples[i]; if ((orig->flags & (ESCAPE_EQ | ESCAPE_EQ_CASECMP | ESCAPE_NE)) == 0) { continue; } test = dup_test(&examples[i]); s = discard_const_p(char, test->unescaped); e = rfc1738_escape_part(NULL, s); if (test->flags & ESCAPE_EQ) { assert_memory_equal(e, test->escaped, test->esc_len + 1); } else if (test->flags & ESCAPE_EQ_CASECMP) { int cmp = strcasecmp(e, test->escaped); assert_int_equal(cmp, 0); assert_string_not_equal(e, test->escaped); } else { assert_string_not_equal(e, test->escaped); } free_test(test); } } static void gen_spectrum(void) { int i, j = 0; const char *lut = "0123456789ABCDEF"; for (i = 1; i < 256; i++) { spectrum[i - 1] = i; if (isalnum(i) || i == '-' || i == '.' || i == '_' || i == '-' || i == '~') { spectrum_escaped[j] = i; j++; } else { spectrum_escaped[j] = '%'; spectrum_escaped[j + 1] = lut[i >> 4]; spectrum_escaped[j + 2] = lut[i & 15]; j += 3; } } spectrum[i - 1] = '\0'; spectrum_escaped[j] = '\0'; } int main(int argc, const char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_escape), cmocka_unit_test(test_unescape), }; gen_spectrum(); cmocka_set_message_output(CM_OUTPUT_SUBUNIT); return cmocka_run_group_tests(tests, NULL, NULL); }