]> git.kaiwu.me - nginx.git/commitdiff
Merging r4036, r4055, r4056, r4057, r4058, r4059, r4060, r4061, r4062, r4063,
authorIgor Sysoev <igor@sysoev.ru>
Fri, 30 Sep 2011 14:06:08 +0000 (14:06 +0000)
committerIgor Sysoev <igor@sysoev.ru>
Fri, 30 Sep 2011 14:06:08 +0000 (14:06 +0000)
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.

src/http/modules/ngx_http_range_filter_module.c
src/http/ngx_http_core_module.c
src/http/ngx_http_core_module.h

index e1124066066ecce604b08cb3af08175e3e014f76..02d2bf925b400e32841f5316396485206c99eb65 100644 (file)
@@ -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;
 }
 
 
index bbb9311cf0fd097121ec76676c128f9195750ff4..ebe22ceaf9b7a253b5e4fd2598dcaf743f64f544 100644 (file)
@@ -631,6 +631,13 @@ static ngx_command_t  ngx_http_core_commands[] = {
       offsetof(ngx_http_core_loc_conf_t, if_modified_since),
       &ngx_http_core_if_modified_since },
 
+    { ngx_string("max_ranges"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, max_ranges),
+      NULL },
+
     { ngx_string("chunked_transfer_encoding"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_flag_slot,
@@ -3253,6 +3260,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
     clcf->keepalive_disable = NGX_CONF_UNSET_UINT;
     clcf->satisfy = NGX_CONF_UNSET_UINT;
     clcf->if_modified_since = NGX_CONF_UNSET_UINT;
+    clcf->max_ranges = NGX_CONF_UNSET_UINT;
     clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT;
     clcf->client_body_in_single_buffer = NGX_CONF_UNSET;
     clcf->internal = NGX_CONF_UNSET;
@@ -3459,6 +3467,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                               NGX_HTTP_SATISFY_ALL);
     ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,
                               NGX_HTTP_IMS_EXACT);
+    ngx_conf_merge_uint_value(conf->max_ranges, prev->max_ranges,
+                              0x7fffffff);
     ngx_conf_merge_uint_value(conf->client_body_in_file_only,
                               prev->client_body_in_file_only, 0);
     ngx_conf_merge_value(conf->client_body_in_single_buffer,
index 165e7c051d795bebf43606c1f4a92970a7873c0d..df20b5d3a115cafd21cd84b198e404604a6db810 100644 (file)
@@ -363,6 +363,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_uint_t    satisfy;                 /* satisfy */
     ngx_uint_t    lingering_close;         /* lingering_close */
     ngx_uint_t    if_modified_since;       /* if_modified_since */
+    ngx_uint_t    max_ranges;              /* max_ranges */
     ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */
 
     ngx_flag_t    client_body_in_single_buffer;