mirror of
https://github.com/emikulic/darkhttpd.git
synced 2023-08-10 21:13:08 +03:00
Switch make_safe_url() to more efficient implementation.
Contributed by: Bert Gijsbers
This commit is contained in:
parent
5f519fec8d
commit
bdae96c653
142
darkhttpd.c
142
darkhttpd.c
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user