MEDIUM: http: url-encoded parsing function can run throught wrapped buffer
The functions smp_fetch_param(), find_next_url_param() and
find_url_param_pos() can look for argument in 2 chunks and not only
one.
diff --git a/src/proto_http.c b/src/proto_http.c
index 33a3568..fa3be6a 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -11503,31 +11503,108 @@
return c == '&' || c == ';' || c == delim;
}
+/* after increasing a pointer value, it can exceed the first buffer
+ * size. This function transform the value of <ptr> according with
+ * the expected position. <chunks> is an array of the one or two
+ * avalaible chunks. The first value is the start of the first chunk,
+ * the second value if the end+1 of the first chunks. The third value
+ * is NULL or the start of the second chunk and the fourth value is
+ * the end+1 of the second chunk. The function returns 1 if does a
+ * wrap, else returns 0.
+ */
+static inline int fix_pointer_if_wrap(const char **chunks, const char **ptr)
+{
+ if (*ptr < chunks[1])
+ return 0;
+ if (!chunks[2])
+ return 0;
+ *ptr = chunks[2] + ( *ptr - chunks[1] );
+ return 1;
+}
+
/*
* Given a url parameter, find the starting position of the first occurence,
* or NULL if the parameter is not found.
*
* Example: if query_string is "yo=mama;ye=daddy" and url_param_name is "ye",
* the function will return query_string+8.
+ *
+ * Warning:this function returns a pointer that can be point to the first chunk
+ * or the second chunk. The caller must be check the position before using the
+ * result.
*/
-static char*
-find_url_param_pos(char* query_string, size_t query_string_l,
+static const char *
+find_url_param_pos(const char **chunks,
const char* url_param_name, size_t url_param_name_l,
char delim)
{
- char *pos, *last;
+ const char *pos, *last, *equal;
+ const char **bufs = chunks;
+ int l1, l2;
- pos = query_string;
- last = query_string + query_string_l - url_param_name_l - 1;
+ pos = bufs[0];
+ last = bufs[1];
while (pos <= last) {
- if (pos[url_param_name_l] == '=') {
- if (memcmp(pos, url_param_name, url_param_name_l) == 0)
- return pos;
- pos += url_param_name_l + 1;
+ /* Check the equal. */
+ equal = pos + url_param_name_l;
+ if (fix_pointer_if_wrap(chunks, &equal)) {
+ if (equal >= chunks[3])
+ return NULL;
+ } else {
+ if (equal >= chunks[1])
+ return NULL;
+ }
+ if (*equal == '=') {
+ if (pos + url_param_name_l > last) {
+ /* process wrap case, we detect a wrap. In this case, the
+ * comparison is performed in two parts.
+ */
+
+ /* This is the end, we dont have any other chunk. */
+ if (bufs != chunks || !bufs[2])
+ return NULL;
+
+ /* Compute the length of each part of the comparison. */
+ l1 = last - pos;
+ l2 = url_param_name_l - l1;
+
+ /* The second buffer is too short to contain the compared string. */
+ if (bufs[2] + l2 > bufs[3])
+ return NULL;
+
+ if (memcmp(pos, url_param_name, l1) == 0 &&
+ memcmp(bufs[2], url_param_name+l1, l2) == 0)
+ return pos;
+
+ /* Perform wrapping and jump the string who fail the comparison. */
+ bufs += 2;
+ pos = bufs[0] + l2;
+ last = bufs[1];
+
+ } else {
+ /* process a simple comparison. */
+ if (memcmp(pos, url_param_name, url_param_name_l) == 0) {
+ return pos; }
+ pos += url_param_name_l + 1;
+ if (fix_pointer_if_wrap(chunks, &pos))
+ last = bufs[2];
+ }
+ }
+
+ while (1) {
+ /* Look for the next delimiter. */
+ while (pos <= last && !is_param_delimiter(*pos, delim))
+ pos++;
+ if (pos < last)
+ break;
+ /* process buffer wrapping. */
+ if (bufs != chunks || !bufs[2])
+ return NULL;
+ bufs += 2;
+ pos = bufs[0];
+ last = bufs[1];
}
- while (pos <= last && !is_param_delimiter(*pos, delim))
- pos++;
pos++;
}
return NULL;
@@ -11540,67 +11617,131 @@
* not found, zero is returned and value/value_l are not touched.
*/
static int
-find_next_url_param(char* query_string, char *qs_end,
+find_next_url_param(const char **chunks,
const char* url_param_name, size_t url_param_name_l,
- char** value, int* value_l, char delim)
+ const char **vstart, const char **vend, char delim)
{
- char *arg_start;
- char *value_start, *value_end;
+ const char *arg_start, *qs_end;
+ const char *value_start, *value_end;
- arg_start = query_string;
+ arg_start = chunks[0];
+ qs_end = chunks[1];
if (url_param_name_l) {
- arg_start = find_url_param_pos(query_string, qs_end - query_string,
+ /* Looks for an argument name. */
+ arg_start = find_url_param_pos(chunks,
url_param_name, url_param_name_l,
delim);
+ /* Check for wrapping. */
+ if (arg_start > qs_end)
+ qs_end = chunks[3];
}
if (!arg_start)
return 0;
if (!url_param_name_l) {
- value_start = memchr(arg_start, '=', qs_end - arg_start);
- if (!value_start)
- return 0;
+ while (1) {
+ /* looks for the first argument. */
+ value_start = memchr(arg_start, '=', qs_end - arg_start);
+ if (!value_start) {
+
+ /* Check for wrapping. */
+ if (arg_start >= chunks[0] &&
+ arg_start <= chunks[1] &&
+ chunks[2]) {
+ arg_start = chunks[2];
+ qs_end = chunks[3];
+ continue;
+ }
+ return 0;
+ }
+ break;
+ }
value_start++;
}
- else
+ else {
+ /* Jump the argument length. */
value_start = arg_start + url_param_name_l + 1;
+ /* Check for pointer wrapping. */
+ if (fix_pointer_if_wrap(chunks, &value_start)) {
+ /* Update the end pointer. */
+ qs_end = chunks[3];
+
+ /* Check for overflow. */
+ if (value_start > qs_end)
+ return 0;
+ }
+ }
+
value_end = value_start;
- while ((value_end < qs_end) && !is_param_delimiter(*value_end, delim))
- value_end++;
+ while (1) {
+ while ((value_end < qs_end) && !is_param_delimiter(*value_end, delim))
+ value_end++;
+ if (value_end < qs_end)
+ break;
+ /* process buffer wrapping. */
+ if (value_end >= chunks[0] &&
+ value_end <= chunks[1] &&
+ chunks[2]) {
+ value_end = chunks[2];
+ qs_end = chunks[3];
+ continue;
+ }
+ break;
+ }
- *value = value_start;
- *value_l = value_end - value_start;
+ *vstart = value_start;
+ *vend = value_end;
return value_end != value_start;
}
-/* This scans a URL-encoded query string. It relies on ctx->a[0] to point to
- * the beginning of the string and ctx->a[1] to point to the end. The string
- * must be contigous. The pointers are updated for next iteration before
- * leaving.
+/* This scans a URL-encoded query string. It takes an optionally wrapping
+ * string whose first contigous chunk has its beginning in ctx->a[0] and end
+ * in ctx->a[1], and the optional second part in (ctx->a[2]..ctx->a[3]). The
+ * pointers are updated for next iteration before leaving.
*/
static int
smp_fetch_param(char delim, const char *name, int name_len, const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- char *query_string, *qs_end;
+ const char *vstart, *vend;
+ struct chunk *temp;
+ const char **chunks = (const char **)smp->ctx.a;
- query_string = smp->ctx.a[0];
- qs_end = smp->ctx.a[1];
-
- if (!find_next_url_param(query_string, qs_end,
+ if (!find_next_url_param(chunks,
name, name_len,
- &smp->data.str.str, &smp->data.str.len,
+ &vstart, &vend,
delim))
return 0;
- query_string = smp->data.str.str + smp->data.str.len + 1;
- smp->ctx.a[0] = query_string;
-
+ /* Create sample. If the value is contiguous, return the pointer as CONST,
+ * if the value is wrapped, copy-it in a buffer.
+ */
smp->type = SMP_T_STR;
- smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
+ if (chunks[2] &&
+ vstart >= chunks[0] && vstart <= chunks[1] &&
+ vend >= chunks[2] && vend <= chunks[3]) {
+ /* Wrapped case. */
+ temp = get_trash_chunk();
+ memcpy(temp->str, vstart, chunks[1] - vstart);
+ memcpy(temp->str + ( chunks[1] - vstart ), chunks[2], vend - chunks[2]);
+ smp->data.str.str = temp->str;
+ smp->data.str.len = ( chunks[1] - vstart ) + ( vend - chunks[2] );
+ } else {
+ /* Contiguous case. */
+ smp->data.str.str = (char *)vstart;
+ smp->data.str.len = vend - vstart;
+ smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
+ }
+
+ /* Update context, check wrapping. */
+ chunks[0] = vend;
+ if (chunks[2] && vend >= chunks[2] && vend <= chunks[3]) {
+ chunks[1] = chunks[3];
+ chunks[2] = NULL;
+ }
- if (query_string < qs_end)
+ if (chunks[0] < chunks[1])
smp->flags |= SMP_F_NOT_LAST;
return 1;
@@ -11608,9 +11749,10 @@
/* This function iterates over each parameter of the query string. It uses
* ctx->a[0] and ctx->a[1] to store the beginning and end of the current
- * parameter. An optional parameter name is passed in args[0], otherwise
- * any parameter is considered. It supports an optional delimiter argument
- * for the beginning of the string in args[1], which defaults to "?".
+ * parameter. Since it uses smp_fetch_param(), ctx->a[2..3] are both NULL.
+ * An optional parameter name is passed in args[0], otherwise any parameter is
+ * considered. It supports an optional delimiter argument for the beginning of
+ * the string in args[1], which defaults to "?".
*/
static int
smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
@@ -11646,6 +11788,12 @@
return 0;
smp->ctx.a[1] = msg->chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l;
+
+ /* Assume that the context is filled with NULL pointer
+ * before the first call.
+ * smp->ctx.a[2] = NULL;
+ * smp->ctx.a[3] = NULL;
+ */
}
return smp_fetch_param(delim, name, name_len, args, smp, kw, private);
@@ -11653,9 +11801,10 @@
/* This function iterates over each parameter of the body. This requires
* that the body has been waited for using http-buffer-request. It uses
- * ctx->a[0] and ctx->a[1] to store the beginning and end of the current
- * parameter. An optional parameter name is passed in args[0], otherwise
- * any parameter is considered.
+ * ctx->a[0] and ctx->a[1] to store the beginning and end of the first
+ * contigous part of the body, and optionally ctx->a[2..3] to reference the
+ * optional second part if the body wraps at the end of the buffer. An optional
+ * parameter name is passed in args[0], otherwise any parameter is considered.
*/
static int
smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
@@ -11697,11 +11846,19 @@
/* buffer is not wrapped (or empty) */
smp->ctx.a[0] = body;
smp->ctx.a[1] = body + len;
+
+ /* Assume that the context is filled with NULL pointer
+ * before the first call.
+ * smp->ctx.a[2] = NULL;
+ * smp->ctx.a[3] = NULL;
+ */
}
else {
/* buffer is wrapped, we need to defragment it */
smp->ctx.a[0] = body;
smp->ctx.a[1] = body + block1;
+ smp->ctx.a[2] = msg->chn->buf->data;
+ smp->ctx.a[3] = msg->chn->buf->data + ( len - block1 );
}
}
return smp_fetch_param('&', name, name_len, args, smp, kw, private);