summaryrefslogtreecommitdiffhomepage
path: root/src/http/modules
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2011-09-30 14:06:08 +0000
committerIgor Sysoev <igor@sysoev.ru>2011-09-30 14:06:08 +0000
commit0b2b252cfbafe2744aa84953729105c8e7c1dc61 (patch)
tree1e09bf16f9358ebdae8dc5ef5700bfea8e9142ec /src/http/modules
parent66ef8eba590ab96429356650297e173f41396eff (diff)
downloadnginx-0b2b252cfbafe2744aa84953729105c8e7c1dc61.tar.gz
nginx-0b2b252cfbafe2744aa84953729105c8e7c1dc61.tar.bz2
Merging r4036, r4055, r4056, r4057, r4058, r4059, r4060, r4061, r4062, r4063,
r4064: Ranges related fixes: The "max_ranges" directive. "max_ranges 0" disables ranges support at all, "max_ranges 1" allows the single range, etc. By default number of ranges is unlimited, to be precise, 2^31-1. If client requests more ranges than "max_ranges" permits, nginx disables ranges and returns just the source response. If total size of all ranges is greater than source response size, then nginx disables ranges and returns just the source response. This fix should not affect well-behaving applications but will defeat DoS attempts exploiting malicious byte ranges. Now unsatisfiable ranges are processed according to RFC 2616.
Diffstat (limited to 'src/http/modules')
-rw-r--r--src/http/modules/ngx_http_range_filter_module.c105
1 files changed, 56 insertions, 49 deletions
diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c
index e11240660..02d2bf925 100644
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -58,8 +58,8 @@ typedef struct {
} ngx_http_range_filter_ctx_t;
-ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
- ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx);
static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
@@ -146,7 +146,7 @@ static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
time_t if_range;
- ngx_int_t rc;
+ ngx_http_core_loc_conf_t *clcf;
ngx_http_range_filter_ctx_t *ctx;
if (r->http_version < NGX_HTTP_VERSION_10
@@ -158,6 +158,12 @@ ngx_http_range_header_filter(ngx_http_request_t *r)
return ngx_http_next_header_filter(r);
}
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->max_ranges == 0) {
+ return ngx_http_next_header_filter(r);
+ }
+
if (r->headers_in.range == NULL
|| r->headers_in.range->value.len < 7
|| ngx_strncasecmp(r->headers_in.range->value.data,
@@ -192,10 +198,9 @@ ngx_http_range_header_filter(ngx_http_request_t *r)
return NGX_ERROR;
}
- rc = ngx_http_range_parse(r, ctx);
-
- if (rc == NGX_OK) {
+ switch (ngx_http_range_parse(r, ctx, clcf->max_ranges)) {
+ case NGX_OK:
ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
@@ -206,15 +211,16 @@ ngx_http_range_header_filter(ngx_http_request_t *r)
}
return ngx_http_range_multipart_header(r, ctx);
- }
- if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
+ case NGX_HTTP_RANGE_NOT_SATISFIABLE:
return ngx_http_range_not_satisfiable(r);
- }
- /* rc == NGX_ERROR */
+ case NGX_ERROR:
+ return NGX_ERROR;
- return rc;
+ default: /* NGX_DECLINED */
+ break;
+ }
next_filter:
@@ -231,15 +237,18 @@ next_filter:
}
-ngx_int_t
-ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
+static ngx_int_t
+ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
+ ngx_uint_t ranges)
{
u_char *p;
- off_t start, end;
+ off_t start, end, size, content_length;
ngx_uint_t suffix;
ngx_http_range_t *range;
p = r->headers_in.range->value.data + 6;
+ size = 0;
+ content_length = r->headers_out.content_length_n;
for ( ;; ) {
start = 0;
@@ -263,26 +272,11 @@ ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
- if (start >= r->headers_out.content_length_n) {
- return NGX_HTTP_RANGE_NOT_SATISFIABLE;
- }
-
while (*p == ' ') { p++; }
if (*p == ',' || *p == '\0') {
- range = ngx_array_push(&ctx->ranges);
- if (range == NULL) {
- return NGX_ERROR;
- }
-
- range->start = start;
- range->end = r->headers_out.content_length_n;
-
- if (*p++ != ',') {
- return NGX_OK;
- }
-
- continue;
+ end = content_length;
+ goto found;
}
} else {
@@ -305,36 +299,49 @@ ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
}
if (suffix) {
- start = r->headers_out.content_length_n - end;
- end = r->headers_out.content_length_n - 1;
+ start = content_length - end;
+ end = content_length - 1;
}
- if (start > end) {
- return NGX_HTTP_RANGE_NOT_SATISFIABLE;
- }
+ if (end >= content_length) {
+ end = content_length;
- range = ngx_array_push(&ctx->ranges);
- if (range == NULL) {
- return NGX_ERROR;
+ } else {
+ end++;
}
- range->start = start;
+ found:
- if (end >= r->headers_out.content_length_n) {
- /*
- * Download Accelerator sends the last byte position
- * that equals to the file length
- */
- range->end = r->headers_out.content_length_n;
+ if (start < end) {
+ range = ngx_array_push(&ctx->ranges);
+ if (range == NULL) {
+ return NGX_ERROR;
+ }
- } else {
- range->end = end + 1;
+ range->start = start;
+ range->end = end;
+
+ size += end - start;
+
+ if (ranges-- == 0) {
+ return NGX_DECLINED;
+ }
}
if (*p++ != ',') {
- return NGX_OK;
+ break;
}
}
+
+ if (ctx->ranges.nelts == 0) {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ if (size > content_length) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
}