Switch make_safe_url() to more efficient implementation.

Contributed by: Bert Gijsbers
This commit is contained in:
Emil Mikulic 2018-12-09 23:18:00 +11:00
parent 5f519fec8d
commit bdae96c653
1 changed files with 43 additions and 99 deletions

View File

@ -481,118 +481,62 @@ static char *split_string(const char *src,
return dest; return dest;
} }
/* Consolidate slashes in-place by shifting parts of the string over repeated
* slashes.
*/
static void consolidate_slashes(char *s) {
size_t left = 0, right = 0;
int saw_slash = 0;
assert(s != NULL);
while (s[right] != '\0') {
if (saw_slash) {
if (s[right] == '/')
right++;
else {
saw_slash = 0;
s[left++] = s[right++];
}
} else {
if (s[right] == '/')
saw_slash++;
s[left++] = s[right++];
}
}
s[left] = '\0';
}
/* Resolve /./ and /../ in a URL, in-place. Also strip out query params. /* Resolve /./ and /../ in a URL, in-place. Also strip out query params.
* Returns NULL if the URL is invalid/unsafe, or the original buffer if * Returns NULL if the URL is invalid/unsafe, or the original buffer if
* successful. * successful.
*/ */
static char *make_safe_url(char *url) { static char *make_safe_url(char *const url) {
struct { char *src = url, *dst;
char *start; #define ends(c) ((c) == '/' || (c) == '\0')
size_t len;
} *chunks;
unsigned int num_slashes, num_chunks;
size_t urllen, i, j, pos;
int ends_in_slash;
/* strip query params */ /* URLs not starting with a slash are illegal. */
for (pos=0; url[pos] != '\0'; pos++) { if (*src != '/')
if (url[pos] == '?') {
url[pos] = '\0';
break;
}
}
if (url[0] != '/')
return NULL; return NULL;
consolidate_slashes(url); /* Fast case: skip until first double-slash or dot-dir. */
urllen = strlen(url); for ( ; *src && *src != '?'; ++src) {
if (urllen > 0) if (*src == '/') {
ends_in_slash = (url[urllen-1] == '/'); if (src[1] == '/')
else break;
ends_in_slash = 1; else if (src[1] == '.') {
if (ends(src[2]))
/* count the slashes */ break;
for (i=0, num_slashes=0; i < urllen; i++) else if (src[2] == '.' && ends(src[3]))
if (url[i] == '/') break;
num_slashes++; }
/* make an array for the URL elements */
assert(num_slashes > 0);
chunks = xmalloc(sizeof(*chunks) * num_slashes);
/* split by slashes and build chunks array */
num_chunks = 0;
for (i=1; i<urllen;) {
/* look for the next slash */
for (j=i; j<urllen && url[j] != '/'; j++)
;
/* process url[i,j) */
if ((j == i+1) && (url[i] == '.'))
/* "." */;
else if ((j == i+2) && (url[i] == '.') && (url[i+1] == '.')) {
/* ".." */
if (num_chunks == 0) {
/* unsafe string so free chunks */
free(chunks);
return (NULL);
} else
num_chunks--;
} else {
chunks[num_chunks].start = url+i;
chunks[num_chunks].len = j-i;
num_chunks++;
} }
i = j + 1; /* url[j] is a slash - move along one */
} }
/* reassemble in-place */ /* Copy to dst, while collapsing multi-slashes and handling dot-dirs. */
pos = 0; dst = src;
for (i=0; i<num_chunks; i++) { while (*src && *src != '?') {
assert(pos <= urllen); if (*src != '/')
url[pos++] = '/'; *dst++ = *src++;
else if (*++src == '/')
assert(pos + chunks[i].len <= urllen); ;
assert(url + pos <= chunks[i].start); else if (*src != '.')
*dst++ = '/';
if (url+pos < chunks[i].start) else if (ends(src[1]))
memmove(url+pos, chunks[i].start, chunks[i].len); /* Ignore single-dot component. */
pos += chunks[i].len; ++src;
else if (src[1] == '.' && ends(src[2])) {
/* Double-dot component. */
src += 2;
if (dst == url)
return NULL; /* Illegal URL */
else
/* Backtrack to previous slash. */
while (*--dst != '/' && dst > url);
}
else
*dst++ = '/';
} }
free(chunks);
if ((num_chunks == 0) || ends_in_slash) if (dst == url)
url[pos++] = '/'; ++dst;
assert(pos <= urllen); *dst = '\0';
url[pos] = '\0';
return url; return url;
#undef ends
} }
static void add_forward_mapping(const char * const host, static void add_forward_mapping(const char * const host,