diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2017-01-08 18:24:53 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2017-01-08 18:59:56 +0000 |
commit | f22e29d1bf2b850ff79e5959a19df534744de75f (patch) | |
tree | 9349eebf580e1cb3445087221a4f845609c12baf /src/libutil | |
parent | 5dcc6259674c783fc76ffde7789d3c76b9d49fee (diff) | |
download | rspamd-f22e29d1bf2b850ff79e5959a19df534744de75f.tar.gz rspamd-f22e29d1bf2b850ff79e5959a19df534744de75f.zip |
[Test] Add tests, fix normalization algorithm
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/http.c | 114 |
1 files changed, 96 insertions, 18 deletions
diff --git a/src/libutil/http.c b/src/libutil/http.c index 73db0d17a..fdc9d1294 100644 --- a/src/libutil/http.c +++ b/src/libutil/http.c @@ -3435,7 +3435,7 @@ rspamd_http_message_unref (struct rspamd_http_message *msg) void rspamd_http_normalize_path_inplace (gchar *path, gsize len, gsize *nlen) { - const gchar *p, *end, *c, *slash; + const gchar *p, *end, *slash = NULL, *dot = NULL; gchar *o; enum { st_normal = 0, @@ -3446,7 +3446,6 @@ rspamd_http_normalize_path_inplace (gchar *path, gsize len, gsize *nlen) } state = st_normal; p = path; - c = path; end = path + len; o = path; @@ -3455,11 +3454,11 @@ rspamd_http_normalize_path_inplace (gchar *path, gsize len, gsize *nlen) case st_normal: if (G_UNLIKELY (*p == '/')) { state = st_got_slash; - c = p; + slash = p; } else if (G_UNLIKELY (*p == '.')) { state = st_got_dot; - c = p; + dot = p; } else { *o++ = *p; @@ -3473,24 +3472,38 @@ rspamd_http_normalize_path_inplace (gchar *path, gsize len, gsize *nlen) state = st_got_slash_slash; } else if (G_UNLIKELY (*p == '.')) { + dot = p; state = st_got_dot; } else { + *o++ = '/'; *o++ = *p; + slash = NULL; + dot = NULL; state = st_normal; } p ++; break; case st_got_slash_slash: if (G_LIKELY (*p != '/')) { - *o++ = *p; + slash = p - 1; + dot = NULL; state = st_normal; + continue; } p ++; break; case st_got_dot: if (G_UNLIKELY (*p == '/')) { /* Remove any /./ or ./ paths */ + if (((o > path && *(o - 1) != '/') || (o == path)) && slash) { + /* Preserve one slash */ + *o++ = '/'; + } + + slash = p; + dot = NULL; + /* Ignore last slash */ state = st_normal; } else if (*p == '.') { @@ -3499,52 +3512,117 @@ rspamd_http_normalize_path_inplace (gchar *path, gsize len, gsize *nlen) } else { /* We have something like .some or /.some */ - if (p > c) { - memcpy (o, c, p - c); - o += p - c; + if (dot && p > dot) { + memmove (o, dot, p - dot); + o += p - dot; } + slash = NULL; + dot = NULL; state = st_normal; + continue; } + p ++; break; case st_got_dot_dot: if (*p == '/') { /* We have something like /../ or ../ */ - if (*c == '/') { + if (slash) { /* We need to remove the last component from o if it is there */ - slash = rspamd_memrchr (path, '/', o - path); + if (o > path + 2 && *(o - 1) == '/') { + slash = rspamd_memrchr (path, '/', o - path - 2); + } + else if (o > path + 1) { + slash = rspamd_memrchr (path, '/', o - path - 1); + } + else { + slash = NULL; + } if (slash) { o = (gchar *)slash; } - /* Otherwise we remove these dots */ - state = st_normal; + /* Otherwise we keep these dots */ + slash = p; + state = st_got_slash; } else { /* We have something like bla../, so we need to copy it as is */ - if (p > c) { - memcpy (o, c, p - c); - o += p - c; + + if (slash) { + *o ++ = '/'; + } + if (dot && p > dot) { + memcpy (o, dot, p - dot); + o += p - dot; } + slash = NULL; + dot = NULL; state = st_normal; + continue; } } else { /* We have something like ..bla or ... */ - if (p > c) { - memcpy (o, c, p - c); - o += p - c; + if (slash) { + *o ++ = '/'; } + if (dot && p > dot) { + memmove (o, dot, p - dot); + o += p - dot; + } + + slash = NULL; + dot = NULL; state = st_normal; + continue; } + p ++; break; } } + /* Leftover */ + switch (state) { + case st_got_dot_dot: + /* Trailing .. */ + if (slash) { + /* We need to remove the last component from o if it is there */ + if (o > path + 2 && *(o - 1) == '/') { + slash = rspamd_memrchr (path, '/', o - path - 2); + } + else if (o > path + 1) { + slash = rspamd_memrchr (path, '/', o - path - 1); + } + else { + if (o == path) { + /* Corner case */ + *o++ = '/'; + } + + slash = NULL; + } + + if (slash) { + /* Remove last / */ + o = (gchar *)slash; + } + } + break; + case st_got_slash: + *o++ = '/'; + break; + default: + if (o > path + 1 && *(o - 1) == '/') { + o --; + } + break; + } + if (nlen) { *nlen = (o - path); } |