diff --git a/README b/README index bdb1e2b..f57defb 100644 --- a/README +++ b/README @@ -17,7 +17,7 @@ If-Unmodified-Since, If-Modified-Since, Date, Accept-Encoding, Accept-Language, Accept-Charset, Max-Forwards, If-Match, If-None-Match, Last-Modified, Content-Length, Expires, - Content-Language, Content-Encoding, Allow, Host + Content-Language, Content-Encoding, Allow, Host, Accept Report Bugs Create a ticket on the issue tracking interface of GitHub: diff --git a/ngx_http_header_inspect.c b/ngx_http_header_inspect.c index 5bbd96b..4ce71ad 100644 --- a/ngx_http_header_inspect.c +++ b/ngx_http_header_inspect.c @@ -36,6 +36,7 @@ static ngx_int_t ngx_header_inspect_ifmatch_header(char* header, ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value); static ngx_int_t ngx_header_inspect_allow_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value); static ngx_int_t ngx_header_inspect_host_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value); +static ngx_int_t ngx_header_inspect_accept_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value); static ngx_int_t ngx_header_inspect_ifrange_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value); static ngx_int_t ngx_header_inspect_date_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, char *header, ngx_str_t value); static ngx_int_t ngx_header_inspect_process_request(ngx_http_request_t *r); @@ -768,6 +769,66 @@ return NGX_OK; } +static ngx_int_t ngx_header_inspect_parse_mediatype(u_char *data, ngx_uint_t maxlen, ngx_uint_t *len) { + ngx_uint_t i; + u_char d; + ngx_uint_t secondpart = 0; + + if (maxlen < 1) { + *len = 0; + return NGX_ERROR; + } + + /* TODO: check with RFC if single '*' is valid */ +// if (data[0] == '*') { +// *len = 1; +// return NGX_OK; +// } + + *len = 1; + for ( i = 0; i < maxlen; i++ ) { + d = data[i]; + if ( d == '/' ) { + if ( i < 1 ) { + *len = 1; + return NGX_ERROR; + } else { + if ( secondpart == 0 ) { + secondpart = 1; + continue; + } else { + *len = i; + return NGX_ERROR; + } + } + } + + if ( + ((d < '0') || (d > '9')) && + ((d < 'a') || (d > 'z')) && + ((d < 'A') || (d > 'Z')) && + (d != '-') && (d != '_') && + (d != '+') && (d != '.') && + (d != ':') && (d != '*') + /* TODO: check with RFC which chars are valid */ + ) { + *len = i; + if (secondpart == 0) { + return NGX_ERROR; + } else { + return NGX_OK; + } + } + } + + *len = i; + if (secondpart == 0) { + return NGX_ERROR; + } else { + return NGX_OK; + } +} + static ngx_int_t ngx_header_inspect_parse_charset(u_char *data, ngx_uint_t maxlen, ngx_uint_t *len) { ngx_uint_t i; u_char d; @@ -1222,6 +1283,7 @@ return rc; } + static ngx_int_t ngx_header_inspect_acceptencoding_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value) { ngx_int_t rc = NGX_AGAIN; ngx_uint_t i = 0; @@ -1298,6 +1360,83 @@ return rc; } +static ngx_int_t ngx_header_inspect_accept_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value) { + ngx_int_t rc = NGX_AGAIN; + ngx_uint_t i = 0; + ngx_uint_t v; + + /* TODO: check with RFC if single '*' is valid */ + if ((value.len == 0) || ((value.len == 1) && (value.data[0] == '*'))) { + return NGX_OK; + } + + while ( i < value.len ) { + if (ngx_header_inspect_parse_mediatype(&(value.data[i]), value.len-i, &v) != NGX_OK) { + if ( conf->log ) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "header_inspect: invalid media-type at position %d in Accept header \"%s\"", i, value.data); + } + rc = NGX_ERROR; + break; + } + i += v; + if ((value.data[i] == ' ') && (i < value.len)) { + i++; + } + if (i == value.len) { + rc = NGX_OK; + break; + } + if (value.data[i] == ';') { + i++; + if (i >= value.len) { + if ( conf->log ) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "header_inspect: unexpected end of Accept header \"%s\"", value.data); + } + rc = NGX_ERROR; + break; + } + if ((value.data[i] == ' ') && (i < value.len)) { + i++; + } + if (ngx_header_inspect_parse_qvalue(&(value.data[i]), value.len-i, &v) != NGX_OK) { + if ( conf->log ) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "header_inspect: invalid qvalue at position %d in Accept header \"%s\"", i, value.data); + } + rc = NGX_ERROR; + break; + } + i += v; + if ((value.data[i] == ' ') && (i < value.len)) { + i++; + } + if (i == value.len) { + rc = NGX_OK; + break; + } + } + if (value.data[i] != ',') { + if ( conf->log ) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "header_inspect: illegal char at position %d in Accept header \"%s\"", i, value.data); + } + rc = NGX_ERROR; + break; + } + i++; + if ((value.data[i] == ' ') && (i < value.len)) { + i++; + } + } + + if (rc == NGX_AGAIN) { + if ( conf->log ) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "header_inspect: unexpected end of Accept header \"%s\"", value.data); + } + rc = NGX_ERROR; + } + + return rc; +} + static ngx_int_t ngx_header_inspect_host_header(ngx_header_inspect_loc_conf_t *conf, ngx_log_t *log, ngx_str_t value) { u_char d = '\0'; ngx_uint_t i = 0; @@ -1476,6 +1615,8 @@ return NGX_OK; } + + static ngx_int_t ngx_header_inspect_process_request(ngx_http_request_t *r) { ngx_header_inspect_loc_conf_t *conf; ngx_table_elt_t *h; @@ -1580,6 +1721,11 @@ if ((rc != NGX_OK) && conf->block) { return NGX_HTTP_BAD_REQUEST; } + } else if ((h[i].key.len == 6) && (ngx_strcmp("Accept", h[i].key.data) == 0) ) { + rc = ngx_header_inspect_accept_header(conf, r->connection->log, h[i].value); + if ((rc != NGX_OK) && conf->block) { + return NGX_HTTP_BAD_REQUEST; + } } else { /* TODO: support for other headers */ if (conf->log_uninspected) {