git diff 2.3.0..643b4c1fa6993da6bc1f82e7121ca62a7696ee6b ngx_http_upload_module.c -- commit 643b4c1fa6993da6bc1f82e7121ca62a7696ee6b Author: Frankie Dintino Date: Sun Jun 21 17:09:17 2020 -0400 Fix bug caused by uploading a single byte range fixes #115 commit 1ac57ac4d37bfb0c09337f2eef3fab5a1c1e9a38 Author: Romain Pomier Date: Wed Apr 8 18:42:38 2020 +0200 Handle quotes problems in Content-Type and Content-Disposition commit 1b0c530a3abf5c8782feabcfa2e89a312687f968 Author: Frankie Dintino Date: Mon Feb 10 12:48:52 2020 -0500 Fix issue where upload headers were only added to final request fixes #124 commit a350ce9d6814438db56428ad6365db994dbd27f6 Author: Valery Kholodkov Date: Mon Nov 18 18:04:55 2019 +0100 Update read handler commit 8d50edd32ae788c3e0c12fe1940007e9faa3f15d Author: Demid Rudak Date: Mon Apr 1 15:37:52 2019 +0300 Fixed upload_aggregate_form_field 'upload_file_md5' calculation commit 666428bf8ea3ee3e5bd40aaa693624234feb6da3 Author: Frankie Dintino Date: Fri Aug 10 11:39:01 2018 -0400 Fix and add tests for upload_add_header commit e8d93f8f37db62f2588c7b3a20c873514fce4f5d Author: Frankie Dintino Date: Fri Aug 10 11:35:16 2018 -0400 Remove unnecessary ngx_http_upload_variable_set function commit 9c58c8ae9b3f4d44a28eebdffbdc1ab4224e3f90 Author: Frankie Dintino Date: Mon Aug 6 09:38:52 2018 -0400 Fix bug when uc and lc hash aggregate fields present in the same request -- diff --git a/ngx_http_upload_module.c b/ngx_http_upload_module.c index 3ac3279..c51429f 100644 --- a/ngx_http_upload_module.c +++ b/ngx_http_upload_module.c @@ -55,8 +55,8 @@ typedef ngx_md5_t MD5_CTX; #define X_SESSION_ID_STRING "X-Session-ID:" #define FORM_DATA_STRING "form-data" #define ATTACHMENT_STRING "attachment" -#define FILENAME_STRING "filename=\"" -#define FIELDNAME_STRING "name=\"" +#define FILENAME_STRING "filename=" +#define FIELDNAME_STRING "name=" #define BYTES_UNIT_STRING "bytes " #define NGX_UPLOAD_MALFORMED -11 @@ -302,8 +302,6 @@ static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_upload_add_variables(ngx_conf_t *cf); -static void ngx_http_upload_variable_set(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upload_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upload_md5_variable(ngx_http_request_t *r, @@ -326,6 +324,10 @@ static ngx_int_t ngx_http_upload_uint_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static char *ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t +ngx_http_upload_process_field_templates(ngx_http_request_t *r, + ngx_http_upload_field_template_t *t, ngx_str_t *field_name, ngx_str_t *field_value); + static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u); static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u); static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u); @@ -345,9 +347,7 @@ static ngx_int_t ngx_http_read_upload_client_request_body(ngx_http_request_t *r) static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r); + static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r); static ngx_int_t ngx_http_upload_eval_state_path(ngx_http_request_t *r); static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -666,7 +666,7 @@ static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */ { ngx_string("upload_add_header"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE2, - ngx_http_upload_add_header, + ngx_http_upload_set_form_field, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_upload_loc_conf_t, header_templates), NULL}, @@ -710,7 +710,7 @@ static ngx_http_variable_t ngx_http_upload_variables[] = { /* {{{ */ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, { ngx_string("upload_content_type"), - ngx_http_upload_variable_set, + NULL, ngx_http_upload_variable, (uintptr_t) offsetof(ngx_http_upload_ctx_t, content_type), NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, @@ -1052,18 +1052,14 @@ err: static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */ ngx_str_t name; ngx_str_t value; - ngx_http_upload_header_template_t *t; + ngx_http_upload_field_template_t *t; ngx_table_elt_t *h; ngx_uint_t i; - if(ulcf->header_templates != NULL) { + if (ulcf->header_templates != NULL) { t = ulcf->header_templates->elts; - for(i = 0; i < ulcf->header_templates->nelts; i++) { - if(ngx_http_complex_value(r, t->name, &name) != NGX_OK) { - return NGX_ERROR; - } - - if(ngx_http_complex_value(r, t->value, &value) != NGX_OK) { + for (i = 0; i < ulcf->header_templates->nelts; i++) { + if (ngx_http_upload_process_field_templates(r, &t[i], &name, &value) != NGX_OK) { return NGX_ERROR; } @@ -1079,8 +1075,6 @@ static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upl h->value.len = value.len; h->value.data = value.data; } - - t++; } } @@ -1180,10 +1174,6 @@ static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */ ngx_str_t dummy = ngx_string(""); ngx_table_elt_t *h; - if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - if(ctx->prevent_output) { r->headers_out.status = NGX_HTTP_CREATED; @@ -1360,6 +1350,27 @@ static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */ return rc; } /* }}} */ +static ngx_int_t +ngx_http_upload_process_field_templates( + ngx_http_request_t *r, ngx_http_upload_field_template_t *t, + ngx_str_t *name, ngx_str_t *value) +{ + if (t->field_lengths == NULL) { + *name = t->value.key; + } else if (ngx_http_script_run(r, name, t->field_lengths->elts, 0, + t->field_values->elts) == NULL) { + return NGX_UPLOAD_SCRIPTERROR; + } + + if (t->value_lengths == NULL) { + *value = t->value.value; + } else if (ngx_http_script_run(r, value, t->value_lengths->elts, 0, + t->value_values->elts) == NULL) { + return NGX_UPLOAD_SCRIPTERROR; + } + return NGX_OK; +} + static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ ngx_http_request_t *r = u->request; ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module); @@ -1495,28 +1506,10 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{ t = ulcf->field_templates->elts; for (i = 0; i < ulcf->field_templates->nelts; i++) { + rc = ngx_http_upload_process_field_templates(r, &t[i], &field_name, &field_value); - if (t[i].field_lengths == NULL) { - field_name = t[i].value.key; - }else{ - if (ngx_http_script_run(r, &field_name, t[i].field_lengths->elts, 0, - t[i].field_values->elts) == NULL) - { - rc = NGX_UPLOAD_SCRIPTERROR; - goto cleanup_file; - } - } - - if (t[i].value_lengths == NULL) { - field_value = t[i].value.value; - }else{ - if (ngx_http_script_run(r, &field_value, t[i].value_lengths->elts, 0, - t[i].value_values->elts) == NULL) - { - rc = NGX_UPLOAD_SCRIPTERROR; - goto cleanup_file; - } - } + if(rc != NGX_OK) + goto cleanup_file; rc = ngx_http_upload_append_field(u, &field_name, &field_value); @@ -1603,6 +1596,11 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{ u->discard_data = 1; } + + if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return NGX_OK; cleanup_file: @@ -1697,25 +1695,10 @@ static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ if(ulcf->aggregate_field_templates) { af = ulcf->aggregate_field_templates->elts; for (i = 0; i < ulcf->aggregate_field_templates->nelts; i++) { - - if (af[i].field_lengths == NULL) { - aggregate_field_name = af[i].value.key; - }else{ - if (ngx_http_script_run(r, &aggregate_field_name, af[i].field_lengths->elts, 0, - af[i].field_values->elts) == NULL) - { - goto rollback; - } - } - - if (af[i].value_lengths == NULL) { - aggregate_field_value = af[i].value.value; - }else{ - if (ngx_http_script_run(r, &aggregate_field_value, af[i].value_lengths->elts, 0, - af[i].value_values->elts) == NULL) - { - goto rollback; - } + rc = ngx_http_upload_process_field_templates(r, &af[i], &aggregate_field_name, + &aggregate_field_value); + if (rc != NGX_OK) { + goto rollback; } rc = ngx_http_upload_append_field(u, &aggregate_field_name, &aggregate_field_value); @@ -2001,7 +1984,7 @@ ngx_http_upload_buf_merge_range(ngx_http_upload_merger_state_t *ms, ngx_http_upl return NGX_ERROR; } - if(ms->current_range_n.start >= ms->current_range_n.end || ms->current_range_n.start >= ms->current_range_n.total + if(ms->current_range_n.start > ms->current_range_n.end || ms->current_range_n.start > ms->current_range_n.total || ms->current_range_n.end > ms->current_range_n.total) { ngx_log_debug3(NGX_LOG_DEBUG_CORE, ms->log, 0, @@ -2398,21 +2381,6 @@ ngx_http_upload_add_variables(ngx_conf_t *cf) return NGX_OK; } /* }}} */ -static void /* {{{ ngx_http_upload_variable_set */ -ngx_http_upload_variable_set(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - ngx_str_t *s; - ngx_http_upload_ctx_t *u; - - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - s = (ngx_str_t *) ((char *) u + data); - - s->len = v->len; - s->data = v->data; -} /* }}} */ - static ngx_int_t /* {{{ ngx_http_upload_variable */ ngx_http_upload_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -2434,88 +2402,78 @@ ngx_http_upload_variable(ngx_http_request_t *r, return NGX_OK; } /* }}} */ -static ngx_int_t /* {{{ ngx_http_upload_md5_variable */ -ngx_http_upload_md5_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) +static ngx_int_t +ngx_http_upload_hash_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data, u_char *digest, + ngx_uint_t digest_len) { ngx_uint_t i; - ngx_http_upload_ctx_t *u; u_char *c; + u_char *p; u_char *hex_table; - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - if(u->md5_ctx == NULL || u->partial_content) { - v->not_found = 1; - return NGX_OK; - } - v->valid = 1; v->no_cacheable = 0; v->not_found = 0; hex_table = (u_char*)data; - c = u->md5_ctx->md5_digest + MD5_DIGEST_LENGTH * 2; - i = MD5_DIGEST_LENGTH; + p = ngx_palloc(r->pool, digest_len * 2); + if (p == NULL) { + return NGX_ERROR; + } + + c = p + digest_len * 2; + i = digest_len; do{ i--; - *--c = hex_table[u->md5_ctx->md5_digest[i] & 0xf]; - *--c = hex_table[u->md5_ctx->md5_digest[i] >> 4]; + *--c = hex_table[digest[i] & 0xf]; + *--c = hex_table[digest[i] >> 4]; }while(i != 0); - v->data = u->md5_ctx->md5_digest; - v->len = MD5_DIGEST_LENGTH * 2; + v->data = c; + v->len = digest_len * 2; return NGX_OK; } /* }}} */ -static ngx_int_t /* {{{ ngx_http_upload_sha1_variable */ -ngx_http_upload_sha1_variable(ngx_http_request_t *r, +static ngx_int_t /* {{{ ngx_http_upload_md5_variable */ +ngx_http_upload_md5_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t i; ngx_http_upload_ctx_t *u; - u_char *c; - u_char *hex_table; u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - if(u->sha1_ctx == NULL || u->partial_content) { + if(u->md5_ctx == NULL || u->partial_content) { v->not_found = 1; return NGX_OK; } + return ngx_http_upload_hash_variable(r, v, data, u->md5_ctx->md5_digest, MD5_DIGEST_LENGTH); +} /* }}} */ - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - hex_table = (u_char*)data; - c = u->sha1_ctx->sha1_digest + SHA_DIGEST_LENGTH * 2; - - i = SHA_DIGEST_LENGTH; +static ngx_int_t /* {{{ ngx_http_upload_sha1_variable */ +ngx_http_upload_sha1_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_upload_ctx_t *u; - do{ - i--; - *--c = hex_table[u->sha1_ctx->sha1_digest[i] & 0xf]; - *--c = hex_table[u->sha1_ctx->sha1_digest[i] >> 4]; - }while(i != 0); + u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - v->data = u->sha1_ctx->sha1_digest; - v->len = SHA_DIGEST_LENGTH * 2; + if(u->sha1_ctx == NULL || u->partial_content) { + v->not_found = 1; + return NGX_OK; + } - return NGX_OK; + return ngx_http_upload_hash_variable(r, v, data, u->sha1_ctx->sha1_digest, SHA_DIGEST_LENGTH); } /* }}} */ static ngx_int_t /* {{{ ngx_http_upload_sha256_variable */ ngx_http_upload_sha256_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t i; ngx_http_upload_ctx_t *u; - u_char *c; - u_char *hex_table; u = ngx_http_get_module_ctx(r, ngx_http_upload_module); @@ -2524,35 +2482,14 @@ ngx_http_upload_sha256_variable(ngx_http_request_t *r, return NGX_OK; } - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - hex_table = (u_char*)data; - c = u->sha256_ctx->sha256_digest + SHA256_DIGEST_LENGTH * 2; - - i = SHA256_DIGEST_LENGTH; - - do{ - i--; - *--c = hex_table[u->sha256_ctx->sha256_digest[i] & 0xf]; - *--c = hex_table[u->sha256_ctx->sha256_digest[i] >> 4]; - }while(i != 0); - - v->data = u->sha256_ctx->sha256_digest; - v->len = SHA256_DIGEST_LENGTH * 2; - - return NGX_OK; + return ngx_http_upload_hash_variable(r, v, data, u->sha256_ctx->sha256_digest, SHA256_DIGEST_LENGTH); } /* }}} */ static ngx_int_t /* {{{ ngx_http_upload_sha512_variable */ ngx_http_upload_sha512_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t i; ngx_http_upload_ctx_t *u; - u_char *c; - u_char *hex_table; u = ngx_http_get_module_ctx(r, ngx_http_upload_module); @@ -2561,25 +2498,7 @@ ngx_http_upload_sha512_variable(ngx_http_request_t *r, return NGX_OK; } - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - hex_table = (u_char*)data; - c = u->sha512_ctx->sha512_digest + SHA512_DIGEST_LENGTH * 2; - - i = SHA512_DIGEST_LENGTH; - - do{ - i--; - *--c = hex_table[u->sha512_ctx->sha512_digest[i] & 0xf]; - *--c = hex_table[u->sha512_ctx->sha512_digest[i] >> 4]; - }while(i != 0); - - v->data = u->sha512_ctx->sha512_digest; - v->len = SHA512_DIGEST_LENGTH * 2; - - return NGX_OK; + return ngx_http_upload_hash_variable(r, v, data, u->sha512_ctx->sha512_digest, SHA512_DIGEST_LENGTH); } /* }}} */ static ngx_int_t /* {{{ ngx_http_upload_crc32_variable */ @@ -2922,73 +2841,6 @@ ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } /* }}} */ -static char * /* {{{ ngx_http_upload_add_header */ -ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_str_t *value; - ngx_http_upload_header_template_t *h; - ngx_array_t **field; - ngx_http_compile_complex_value_t ccv; - - field = (ngx_array_t**) (((u_char*)conf) + cmd->offset); - - value = cf->args->elts; - - /* - * Add new entry to header template list - */ - if (*field == NULL) { - *field = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_upload_header_template_t)); - if (*field == NULL) { - return NGX_CONF_ERROR; - } - } - - h = ngx_array_push(*field); - if (h == NULL) { - return NGX_CONF_ERROR; - } - - /* - * Compile header name - */ - h->name = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); - if(h->name == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - - ccv.cf = cf; - ccv.value = &value[1]; - ccv.complex_value = h->name; - - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { - return NGX_CONF_ERROR; - } - - /* - * Compile header value - */ - h->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); - if(h->value == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - - ccv.cf = cf; - ccv.value = &value[2]; - ccv.complex_value = h->value; - - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} /* }}} */ - static char * /* {{{ ngx_http_upload_cleanup */ ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -3599,6 +3451,8 @@ ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) ngx_del_timer(c->read); } + r->read_event_handler = ngx_http_block_reading; + rc = ngx_http_process_request_body(r, u->to_write); switch(rc) { @@ -3667,16 +3521,14 @@ static ngx_int_t upload_parse_content_disposition(ngx_http_upload_ctx_t *upload_ if(filename_start != 0) { - filename_start += sizeof(FILENAME_STRING)-1; - - filename_end = filename_start + strcspn(filename_start, "\""); + filename_start += sizeof(FILENAME_STRING) - 1; - if(*filename_end != '\"') { - ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, - "malformed filename in part header"); - return NGX_UPLOAD_MALFORMED; + if (*filename_start == '\"') { + filename_start++; } + filename_end = filename_start + strcspn(filename_start, "\";"); + /* * IE sends full path, strip path from filename * Also strip all UNIX path references @@ -3703,16 +3555,14 @@ static ngx_int_t upload_parse_content_disposition(ngx_http_upload_ctx_t *upload_ // }while((fieldname_start != 0) && (fieldname_start + sizeof(FIELDNAME_STRING) - 1 == filename_start)); if(fieldname_start != 0) { - fieldname_start += sizeof(FIELDNAME_STRING)-1; + fieldname_start += sizeof(FIELDNAME_STRING) - 1; - if(fieldname_start != filename_start) { - fieldname_end = fieldname_start + strcspn(fieldname_start, "\""); + if (*fieldname_start == '\"') { + fieldname_start++; + } - if(*fieldname_end != '\"') { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "malformed fieldname in part header"); - return NGX_UPLOAD_MALFORMED; - } + if(fieldname_start != filename_start) { + fieldname_end = fieldname_start + strcspn(fieldname_start, "\";"); upload_ctx->field_name.len = fieldname_end - fieldname_start; upload_ctx->field_name.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->field_name.len + 1); @@ -4087,6 +3937,12 @@ static ngx_int_t upload_parse_request_headers(ngx_http_upload_ctx_t *upload_ctx, boundary_start_ptr += sizeof(BOUNDARY_STRING) - 1; boundary_end_ptr = boundary_start_ptr + strcspn((char*)boundary_start_ptr, " ;\n\r"); + // Handle quoted boundaries + if ((boundary_end_ptr - boundary_start_ptr) >= 2 && boundary_start_ptr[0] == '"' && *(boundary_end_ptr - 1) == '"') { + boundary_start_ptr++; + boundary_end_ptr--; + } + if(boundary_end_ptr == boundary_start_ptr) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, "boundary is empty");