mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-24 07:16:43 +00:00
Improve HTTP Origin header parsing
Parses the separate parts of the URI: scheme, hostname, port. No longer supports "Origin: null", because that is not in the spec.
This commit is contained in:
parent
367d54f5f8
commit
404cc1476d
@ -503,19 +503,19 @@ static inline int _skip_space(struct http_request *r)
|
|||||||
|
|
||||||
static size_t _skip_word_printable(struct http_request *r, struct substring *str, char until)
|
static size_t _skip_word_printable(struct http_request *r, struct substring *str, char until)
|
||||||
{
|
{
|
||||||
|
const char *start = r->cursor;
|
||||||
|
if (str)
|
||||||
|
str->start = str->end = start;
|
||||||
if (_run_out(r) || isspace(*r->cursor) || !isprint(*r->cursor) || *r->cursor == until)
|
if (_run_out(r) || isspace(*r->cursor) || !isprint(*r->cursor) || *r->cursor == until)
|
||||||
return 0;
|
return 0;
|
||||||
const char *start = r->cursor;
|
|
||||||
for (++r->cursor; !_run_out(r) && !isspace(*r->cursor) && isprint(*r->cursor) && *r->cursor != until; ++r->cursor)
|
for (++r->cursor; !_run_out(r) && !isspace(*r->cursor) && isprint(*r->cursor) && *r->cursor != until; ++r->cursor)
|
||||||
;
|
;
|
||||||
if (_run_out(r))
|
if (_run_out(r))
|
||||||
return 0;
|
return 0;
|
||||||
assert(r->cursor > start);
|
assert(r->cursor > start);
|
||||||
assert(isspace(*r->cursor) || *r->cursor == until);
|
assert(isspace(*r->cursor) || *r->cursor == until);
|
||||||
if (str) {
|
if (str)
|
||||||
str->start = start;
|
|
||||||
str->end = r->cursor;
|
str->end = r->cursor;
|
||||||
}
|
|
||||||
return r->cursor - start;
|
return r->cursor - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,6 +600,11 @@ static inline int _parse_uint32(struct http_request *r, uint32_t *uint32p)
|
|||||||
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint32(r->cursor, 10, uint32p, (const char **)&r->cursor);
|
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint32(r->cursor, 10, uint32p, (const char **)&r->cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int _parse_uint16(struct http_request *r, uint16_t *uint16p)
|
||||||
|
{
|
||||||
|
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint16(r->cursor, 10, uint16p, (const char **)&r->cursor);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned _parse_ranges(struct http_request *r, struct http_range *range, unsigned nrange)
|
static unsigned _parse_ranges(struct http_request *r, struct http_range *range, unsigned nrange)
|
||||||
{
|
{
|
||||||
unsigned i = 0;
|
unsigned i = 0;
|
||||||
@ -748,6 +753,43 @@ static int _parse_authorization(struct http_request *r, struct http_client_autho
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _parse_origin(struct http_request *r, struct http_origin *origin, size_t header_bytes)
|
||||||
|
{
|
||||||
|
char *start = r->cursor;
|
||||||
|
char *end = start + header_bytes;
|
||||||
|
if (_skip_literal(r, "http://")) {
|
||||||
|
origin->scheme = "http";
|
||||||
|
} else if (_skip_literal(r, "https://")) {
|
||||||
|
origin->scheme = "https";
|
||||||
|
} else if (_skip_literal(r, "file://")) {
|
||||||
|
origin->scheme = "file";
|
||||||
|
} else {
|
||||||
|
IDEBUGF(r->debug, "Ignoring HTTP Origin with unsupported URI scheme: %s", alloca_toprint(50, start, header_bytes));
|
||||||
|
r->cursor = end;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
origin->hostname = "";
|
||||||
|
origin->port = 0;
|
||||||
|
struct substring hostname;
|
||||||
|
if (_skip_word_printable(r, &hostname, '/') > 0) {
|
||||||
|
const char *port = hostname.end - 1;
|
||||||
|
while (port > hostname.start && isdigit(*port))
|
||||||
|
--port;
|
||||||
|
if (port >= hostname.start && *port == ':' && port < hostname.end - 1) {
|
||||||
|
const char *e = NULL;
|
||||||
|
if (port && port + 1 < r->cursor && str_to_uint16(port + 1, 10, &origin->port, &e)) {
|
||||||
|
assert(e == r->cursor);
|
||||||
|
hostname.end = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(hostname.end > hostname.start);
|
||||||
|
if (!_reserve_substring(r, &origin->hostname, hostname))
|
||||||
|
return 0; // error
|
||||||
|
}
|
||||||
|
_skip_literal(r, "/");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
|
static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
|
||||||
{
|
{
|
||||||
char datestr[40];
|
char datestr[40];
|
||||||
@ -1069,27 +1111,28 @@ static int http_request_parse_header(struct http_request *r)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (r->response.result_code)
|
if (r->response.result_code)
|
||||||
return r->response.result_code;
|
return r->response.result_code;
|
||||||
goto malformed;
|
goto malformed;
|
||||||
}
|
}
|
||||||
_rewind(r);
|
_rewind(r);
|
||||||
if (_skip_literal_nocase(r, "Origin:")) {
|
if (_skip_literal_nocase(r, "Origin:")) {
|
||||||
if (r->request_header.origin) {
|
if (r->request_header.origin.scheme) {
|
||||||
IDEBUGF(r->debug, "Skipping duplicate HTTP header Origin: %s", alloca_toprint(50, sol, r->end - sol));
|
IDEBUGF(r->debug, "Skipping duplicate HTTP header Origin: %s", alloca_toprint(50, sol, r->end - sol));
|
||||||
r->cursor = nextline;
|
r->cursor = nextline;
|
||||||
_commit(r);
|
_commit(r);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
_skip_optional_space(r);
|
_skip_optional_space(r);
|
||||||
struct substring origin;
|
if ( _parse_origin(r, &r->request_header.origin, eol - r->cursor)
|
||||||
if (_skip_word_printable(r, &origin, ' ')
|
|
||||||
&& _skip_optional_space(r)
|
&& _skip_optional_space(r)
|
||||||
&& r->cursor == eol) {
|
&& r->cursor == eol
|
||||||
|
) {
|
||||||
r->cursor = nextline;
|
r->cursor = nextline;
|
||||||
_commit(r);
|
_commit(r);
|
||||||
_reserve_substring(r, &r->request_header.origin, origin);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (r->response.result_code)
|
||||||
|
return r->response.result_code;
|
||||||
goto malformed;
|
goto malformed;
|
||||||
}
|
}
|
||||||
_rewind(r);
|
_rewind(r);
|
||||||
|
@ -87,11 +87,17 @@ struct http_www_authenticate {
|
|||||||
const char *realm;
|
const char *realm;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct http_origin {
|
||||||
|
const char *scheme;
|
||||||
|
const char *hostname;
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
struct http_request_headers {
|
struct http_request_headers {
|
||||||
http_size_t content_length;
|
http_size_t content_length;
|
||||||
struct mime_content_type content_type;
|
struct mime_content_type content_type;
|
||||||
unsigned short content_range_count;
|
unsigned short content_range_count;
|
||||||
const char *origin; // points into buffer; nul terminated
|
struct http_origin origin;
|
||||||
struct http_range content_ranges[5];
|
struct http_range content_ranges[5];
|
||||||
struct http_client_authorization authorization;
|
struct http_client_authorization authorization;
|
||||||
};
|
};
|
||||||
@ -102,7 +108,7 @@ struct http_response_headers {
|
|||||||
http_size_t resource_length; // size of entire resource
|
http_size_t resource_length; // size of entire resource
|
||||||
const char *content_type; // "type/subtype"
|
const char *content_type; // "type/subtype"
|
||||||
const char *boundary;
|
const char *boundary;
|
||||||
char allow_origin[23]; // max supported str (for now) "http://localhost:65537"
|
char allow_origin[24]; // max supported str (for now) "https://localhost:65537"
|
||||||
const char *allow_methods;
|
const char *allow_methods;
|
||||||
const char *allow_headers;
|
const char *allow_headers;
|
||||||
struct http_www_authenticate www_authenticate;
|
struct http_www_authenticate www_authenticate;
|
||||||
|
45
httpd.c
45
httpd.c
@ -357,20 +357,39 @@ int authorize_restful(struct http_request *r)
|
|||||||
{
|
{
|
||||||
if (!is_from_loopback(r))
|
if (!is_from_loopback(r))
|
||||||
return 403;
|
return 403;
|
||||||
if (r->request_header.origin){
|
if (r->request_header.origin.hostname) {
|
||||||
const char *remainder;
|
assert(r->request_header.origin.scheme);
|
||||||
if (strcasecmp(r->request_header.origin, "null")==0
|
if ( ( ( strcmp(r->request_header.origin.scheme, "http") == 0
|
||||||
|| (strcase_startswith(r->request_header.origin, "http://localhost", &remainder)
|
|| strcmp(r->request_header.origin.scheme, "https") == 0
|
||||||
&& (*remainder==':' || *remainder=='\0'))
|
)
|
||||||
|| (strcase_startswith(r->request_header.origin, "http://127.0.0.1", &remainder)
|
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
||||||
&& (*remainder==':' || *remainder=='\0'))
|
|| strcmp(r->request_header.origin.hostname, "127.0.0.1") == 0
|
||||||
|| (strcase_startswith(r->request_header.origin, "file://", &remainder))
|
)
|
||||||
){
|
)
|
||||||
strncpy(r->response.header.allow_origin,r->request_header.origin, sizeof r->response.header.allow_origin);
|
|| ( strcmp(r->request_header.origin.scheme, "file") == 0
|
||||||
r->response.header.allow_methods="GET, POST, OPTIONS";
|
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
||||||
r->response.header.allow_headers="Authorization";
|
|| strcmp(r->request_header.origin.hostname, "127.0.0.1") == 0
|
||||||
}else
|
|| strcmp(r->request_header.origin.hostname, "") == 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
strbuf sb = strbuf_local(r->response.header.allow_origin, sizeof r->response.header.allow_origin);
|
||||||
|
strbuf_puts(sb, r->request_header.origin.scheme);
|
||||||
|
strbuf_puts(sb, "://");
|
||||||
|
strbuf_puts(sb, r->request_header.origin.hostname);
|
||||||
|
if (r->request_header.origin.port) {
|
||||||
|
strbuf_sprintf(sb, ":%u", r->request_header.origin.port);
|
||||||
|
}
|
||||||
|
if (!strbuf_overrun(sb)) {
|
||||||
|
r->response.header.allow_methods = "GET, POST, OPTIONS";
|
||||||
|
r->response.header.allow_headers = "Authorization";
|
||||||
|
} else {
|
||||||
|
r->response.header.allow_origin[0] = '\0';
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return 403;
|
return 403;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (r->verb == HTTP_VERB_OPTIONS){
|
if (r->verb == HTTP_VERB_OPTIONS){
|
||||||
http_request_simple_response(r, 200, NULL);
|
http_request_simple_response(r, 200, NULL);
|
||||||
|
@ -136,15 +136,6 @@ test_CORS_Request(){
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||||
assertStdoutIs '200'
|
assertStdoutIs '200'
|
||||||
assertGrep http.headers "^Access-Control-Allow-Origin: http://localhost:1234$CR\$"
|
assertGrep http.headers "^Access-Control-Allow-Origin: http://localhost:1234$CR\$"
|
||||||
executeOk curl \
|
|
||||||
--silent --fail --show-error --write-out '%{http_code}' \
|
|
||||||
--output http.output \
|
|
||||||
--dump-header http.headers \
|
|
||||||
--header "Origin: null" \
|
|
||||||
--request "OPTIONS" \
|
|
||||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
||||||
assertStdoutIs '200'
|
|
||||||
assertGrep http.headers "^Access-Control-Allow-Origin: null$CR\$"
|
|
||||||
executeOk curl \
|
executeOk curl \
|
||||||
--silent --show-error --write-out '%{http_code}' \
|
--silent --show-error --write-out '%{http_code}' \
|
||||||
--output http.output \
|
--output http.output \
|
||||||
|
Loading…
Reference in New Issue
Block a user