diff --git a/src/common/util.c b/src/common/util.c index 93e2ba8e14..b2f12bfb6a 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -865,6 +865,36 @@ tor_digest_is_zero(const char *digest) return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN); } +/** Return true if string is a valid '=' string. + * is optional, to indicate the empty string. */ +int +string_is_key_value(const char *string) +{ + /* position of equal sign in string */ + char *equal_sign_pos = NULL; + + tor_assert(string); + + if (strlen(string) < 2) { /* "x=a" is shortest args string */ + log_warn(LD_GENERAL, "'%s' is too short to be a k=v value.", string); + return 0; + } + + equal_sign_pos = strchr(string, '='); + if (!equal_sign_pos) { + log_warn(LD_GENERAL, "'%s' is not a k=v value.", string); + return 0; + } + + /* validate that the '=' is not in the beginning of the string. */ + if (equal_sign_pos == string) { + log_warn(LD_GENERAL, "'%s' is not a valid k=v value.", string); + return 0; + } + + return 1; +} + /** Return true iff the DIGEST256_LEN bytes in digest are all zero. */ int tor_digest256_is_zero(const char *digest) @@ -1249,6 +1279,43 @@ wrap_string(smartlist_t *out, const char *string, size_t width, } } +/** Escape every character of string that belongs to the set of + * characters set. Use escape_char as the character to + * use for escaping. */ +char * +tor_escape_str_for_socks_arg(const char *string) +{ + char *new_string = NULL; + char *new_cp = NULL; + size_t length, new_length; + static const char *chars_to_escape = ";\\"; + + tor_assert(string); + + length = strlen(string); + + if (!length) + return NULL; + /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) => + (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */ + if (length > (SIZE_MAX - 1)/2) /* check for overflow */ + return NULL; + + /* this should be enough even if all characters must be escaped */ + new_length = (length * 2) + 1; + + new_string = new_cp = tor_malloc_zero(new_length); + + while (*string) { + if (strchr(chars_to_escape, *string)) + *new_cp++ = '\\'; + + *new_cp++ = *string++; + } + + return new_string; +} + /* ===== * Time * ===== */ diff --git a/src/common/util.h b/src/common/util.h index 911b1b5a37..e3cd72118c 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -209,12 +209,16 @@ const char *find_whitespace_eos(const char *s, const char *eos); const char *find_str_at_start_of_line(const char *haystack, const char *needle); int string_is_C_identifier(const char *string); +int string_is_key_value(const char *string); int tor_mem_is_zero(const char *mem, size_t len); int tor_digest_is_zero(const char *digest); int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); + +char *tor_escape_str_for_socks_arg(const char *string); + struct smartlist_t; void wrap_string(struct smartlist_t *out, const char *string, size_t width, const char *prefix0, const char *prefixRest); diff --git a/src/test/test_util.c b/src/test/test_util.c index bed33fac25..b41f23571b 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -795,6 +795,54 @@ test_util_expand_filename(void) } #endif +/** Test tor_escape_str_for_socks_arg(). */ +static void +test_util_escape_string_socks(void) +{ + char *escaped_string = NULL; + + /** Simple backslash escape. */ + escaped_string = tor_escape_str_for_socks_arg("This is a backslash: \\"); + test_assert(escaped_string); + test_streq(escaped_string, "This is a backslash: \\\\"); + tor_free(escaped_string); + + /** Simple semicolon escape. */ + escaped_string = tor_escape_str_for_socks_arg("First rule: Do not use ;"); + test_assert(escaped_string); + test_streq(escaped_string, "First rule: Do not use \\;"); + tor_free(escaped_string); + + /** Ilegal: Empty string. */ + escaped_string = tor_escape_str_for_socks_arg(""); + test_assert(!escaped_string); + + /** Escape all characters. */ + escaped_string = tor_escape_str_for_socks_arg(";\\;\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;\\\\\\;\\\\"); + tor_free(escaped_string); + + done: + tor_free(escaped_string); +} + +static void +test_util_string_is_key_value(void *ptr) +{ + (void)ptr; + test_assert(string_is_key_value("key=value")); + test_assert(string_is_key_value("k=v")); + test_assert(string_is_key_value("key=")); + test_assert(!string_is_key_value("=value")); + test_assert(!string_is_key_value("=")); + + /* ??? */ + /* test_assert(!string_is_key_value("===")); */ + done: + ; +} + /** Test basic string functionality. */ static void test_util_strmisc(void) @@ -3271,6 +3319,8 @@ struct testcase_t util_tests[] = { #ifndef _WIN32 UTIL_LEGACY(expand_filename), #endif + UTIL_LEGACY(escape_string_socks), + UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip),