/* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file util_string.c * \brief Non-standard string functions used throughout Tor. **/ #include "lib/string/util_string.h" #include "lib/string/compat_ctype.h" #include "lib/err/torerr.h" #include "lib/ctime/di_ops.h" #include "lib/defs/digest_sizes.h" #include #include /** Given hlen bytes at haystack and nlen bytes at * needle, return a pointer to the first occurrence of the needle * within the haystack, or NULL if there is no such occurrence. * * This function is not timing-safe. * * Requires that nlen be greater than zero. */ const void * tor_memmem(const void *_haystack, size_t hlen, const void *_needle, size_t nlen) { #if defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2) raw_assert(nlen); return memmem(_haystack, hlen, _needle, nlen); #else /* This isn't as fast as the GLIBC implementation, but it doesn't need to * be. */ const char *p, *last_possible_start; const char *haystack = (const char*)_haystack; const char *needle = (const char*)_needle; char first; raw_assert(nlen); if (nlen > hlen) return NULL; p = haystack; /* Last position at which the needle could start. */ last_possible_start = haystack + hlen - nlen; first = *(const char*)needle; while ((p = memchr(p, first, last_possible_start + 1 - p))) { if (fast_memeq(p, needle, nlen)) return p; if (++p > last_possible_start) { /* This comparison shouldn't be necessary, since if p was previously * equal to last_possible_start, the next memchr call would be * "memchr(p, first, 0)", which will return NULL. But it clarifies the * logic. */ return NULL; } } return NULL; #endif /* defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2) */ } const void * tor_memstr(const void *haystack, size_t hlen, const char *needle) { return tor_memmem(haystack, hlen, needle, strlen(needle)); } /** Return true iff the 'len' bytes at 'mem' are all zero. */ int tor_mem_is_zero(const char *mem, size_t len) { static const char ZERO[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }; while (len >= sizeof(ZERO)) { /* It's safe to use fast_memcmp here, since the very worst thing an * attacker could learn is how many initial bytes of a secret were zero */ if (fast_memcmp(mem, ZERO, sizeof(ZERO))) return 0; len -= sizeof(ZERO); mem += sizeof(ZERO); } /* Deal with leftover bytes. */ if (len) return fast_memeq(mem, ZERO, len); return 1; } /** Return true iff the DIGEST_LEN bytes in digest are all zero. */ int tor_digest_is_zero(const char *digest) { static const uint8_t ZERO_DIGEST[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN); } /** Return true iff the DIGEST256_LEN bytes in digest are all zero. */ int tor_digest256_is_zero(const char *digest) { return tor_mem_is_zero(digest, DIGEST256_LEN); } /** Remove from the string s every character which appears in * strip. */ void tor_strstrip(char *s, const char *strip) { char *readp = s; while (*readp) { if (strchr(strip, *readp)) { ++readp; } else { *s++ = *readp++; } } *s = '\0'; } /** Convert all alphabetic characters in the nul-terminated string s to * lowercase. */ void tor_strlower(char *s) { while (*s) { *s = TOR_TOLOWER(*s); ++s; } } /** Convert all alphabetic characters in the nul-terminated string s to * lowercase. */ void tor_strupper(char *s) { while (*s) { *s = TOR_TOUPPER(*s); ++s; } } /** Return 1 if every character in s is printable, else return 0. */ int tor_strisprint(const char *s) { while (*s) { if (!TOR_ISPRINT(*s)) return 0; s++; } return 1; } /** Return 1 if no character in s is uppercase, else return 0. */ int tor_strisnonupper(const char *s) { while (*s) { if (TOR_ISUPPER(*s)) return 0; s++; } return 1; } /** Return true iff every character in s is whitespace space; else * return false. */ int tor_strisspace(const char *s) { while (*s) { if (!TOR_ISSPACE(*s)) return 0; s++; } return 1; } /** As strcmp, except that either string may be NULL. The NULL string is * considered to be before any non-NULL string. */ int strcmp_opt(const char *s1, const char *s2) { if (!s1) { if (!s2) return 0; else return -1; } else if (!s2) { return 1; } else { return strcmp(s1, s2); } } /** Compares the first strlen(s2) characters of s1 with s2. Returns as for * strcmp. */ int strcmpstart(const char *s1, const char *s2) { size_t n = strlen(s2); return strncmp(s1, s2, n); } /** Compare the s1_len-byte string s1 with s2, * without depending on a terminating nul in s1. Sorting order is first by * length, then lexically; return values are as for strcmp. */ int strcmp_len(const char *s1, const char *s2, size_t s1_len) { size_t s2_len = strlen(s2); if (s1_len < s2_len) return -1; if (s1_len > s2_len) return 1; return fast_memcmp(s1, s2, s2_len); } /** Compares the first strlen(s2) characters of s1 with s2. Returns as for * strcasecmp. */ int strcasecmpstart(const char *s1, const char *s2) { size_t n = strlen(s2); return strncasecmp(s1, s2, n); } /** Compare the value of the string prefix with the start of the * memlen-byte memory chunk at mem. Return as for strcmp. * * [As fast_memcmp(mem, prefix, strlen(prefix)) but returns -1 if memlen is * less than strlen(prefix).] */ int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix) { size_t plen = strlen(prefix); if (memlen < plen) return -1; return fast_memcmp(mem, prefix, plen); } /** Compares the last strlen(s2) characters of s1 with s2. Returns as for * strcmp. */ int strcmpend(const char *s1, const char *s2) { size_t n1 = strlen(s1), n2 = strlen(s2); if (n2>n1) return strcmp(s1,s2); else return strncmp(s1+(n1-n2), s2, n2); } /** Compares the last strlen(s2) characters of s1 with s2. Returns as for * strcasecmp. */ int strcasecmpend(const char *s1, const char *s2) { size_t n1 = strlen(s1), n2 = strlen(s2); if (n2>n1) /* then they can't be the same; figure out which is bigger */ return strcasecmp(s1,s2); else return strncasecmp(s1+(n1-n2), s2, n2); } /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ const char * eat_whitespace(const char *s) { raw_assert(s); while (1) { switch (*s) { case '\0': default: return s; case ' ': case '\t': case '\n': case '\r': ++s; break; case '#': ++s; while (*s && *s != '\n') ++s; } } } /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ const char * eat_whitespace_eos(const char *s, const char *eos) { raw_assert(s); raw_assert(eos && s <= eos); while (s < eos) { switch (*s) { case '\0': default: return s; case ' ': case '\t': case '\n': case '\r': ++s; break; case '#': ++s; while (s < eos && *s && *s != '\n') ++s; } } return s; } /** Return a pointer to the first char of s that is not a space or a tab * or a \\r, or to the terminating NUL if no such character exists. */ const char * eat_whitespace_no_nl(const char *s) { while (*s == ' ' || *s == '\t' || *s == '\r') ++s; return s; } /** As eat_whitespace_no_nl, but stop at eos whether we have * found a non-whitespace character or not. */ const char * eat_whitespace_eos_no_nl(const char *s, const char *eos) { while (s < eos && (*s == ' ' || *s == '\t' || *s == '\r')) ++s; return s; } /** Return a pointer to the first char of s that is whitespace or #, * or to the terminating NUL if no such character exists. */ const char * find_whitespace(const char *s) { /* tor_assert(s); */ while (1) { switch (*s) { case '\0': case '#': case ' ': case '\r': case '\n': case '\t': return s; default: ++s; } } } /** As find_whitespace, but stop at eos whether we have found a * whitespace or not. */ const char * find_whitespace_eos(const char *s, const char *eos) { /* tor_assert(s); */ while (s < eos) { switch (*s) { case '\0': case '#': case ' ': case '\r': case '\n': case '\t': return s; default: ++s; } } return s; } /** Return the first occurrence of needle in haystack that * occurs at the start of a line (that is, at the beginning of haystack * or immediately after a newline). Return NULL if no such string is found. */ const char * find_str_at_start_of_line(const char *haystack, const char *needle) { size_t needle_len = strlen(needle); do { if (!strncmp(haystack, needle, needle_len)) return haystack; haystack = strchr(haystack, '\n'); if (!haystack) return NULL; else ++haystack; } while (*haystack); return NULL; } /** Returns true if string could be a C identifier. A C identifier must begin with a letter or an underscore and the rest of its characters can be letters, numbers or underscores. No length limit is imposed. */ int string_is_C_identifier(const char *string) { size_t iter; size_t length = strlen(string); if (!length) return 0; for (iter = 0; iter < length ; iter++) { if (iter == 0) { if (!(TOR_ISALPHA(string[iter]) || string[iter] == '_')) return 0; } else { if (!(TOR_ISALPHA(string[iter]) || TOR_ISDIGIT(string[iter]) || string[iter] == '_')) return 0; } } return 1; }