mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-20 05:37:57 +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)
|
||||
{
|
||||
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)
|
||||
return 0;
|
||||
const char *start = r->cursor;
|
||||
for (++r->cursor; !_run_out(r) && !isspace(*r->cursor) && isprint(*r->cursor) && *r->cursor != until; ++r->cursor)
|
||||
;
|
||||
if (_run_out(r))
|
||||
return 0;
|
||||
assert(r->cursor > start);
|
||||
assert(isspace(*r->cursor) || *r->cursor == until);
|
||||
if (str) {
|
||||
str->start = start;
|
||||
if (str)
|
||||
str->end = r->cursor;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned i = 0;
|
||||
@ -748,6 +753,43 @@ static int _parse_authorization(struct http_request *r, struct http_client_autho
|
||||
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)
|
||||
{
|
||||
char datestr[40];
|
||||
@ -1074,22 +1116,23 @@ static int http_request_parse_header(struct http_request *r)
|
||||
}
|
||||
_rewind(r);
|
||||
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));
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
_skip_optional_space(r);
|
||||
struct substring origin;
|
||||
if (_skip_word_printable(r, &origin, ' ')
|
||||
if ( _parse_origin(r, &r->request_header.origin, eol - r->cursor)
|
||||
&& _skip_optional_space(r)
|
||||
&& r->cursor == eol) {
|
||||
&& r->cursor == eol
|
||||
) {
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
_reserve_substring(r, &r->request_header.origin, origin);
|
||||
return 0;
|
||||
}
|
||||
if (r->response.result_code)
|
||||
return r->response.result_code;
|
||||
goto malformed;
|
||||
}
|
||||
_rewind(r);
|
||||
|
@ -87,11 +87,17 @@ struct http_www_authenticate {
|
||||
const char *realm;
|
||||
};
|
||||
|
||||
struct http_origin {
|
||||
const char *scheme;
|
||||
const char *hostname;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
struct http_request_headers {
|
||||
http_size_t content_length;
|
||||
struct mime_content_type content_type;
|
||||
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_client_authorization authorization;
|
||||
};
|
||||
@ -102,7 +108,7 @@ struct http_response_headers {
|
||||
http_size_t resource_length; // size of entire resource
|
||||
const char *content_type; // "type/subtype"
|
||||
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_headers;
|
||||
struct http_www_authenticate www_authenticate;
|
||||
|
45
httpd.c
45
httpd.c
@ -357,21 +357,40 @@ int authorize_restful(struct http_request *r)
|
||||
{
|
||||
if (!is_from_loopback(r))
|
||||
return 403;
|
||||
if (r->request_header.origin){
|
||||
const char *remainder;
|
||||
if (strcasecmp(r->request_header.origin, "null")==0
|
||||
|| (strcase_startswith(r->request_header.origin, "http://localhost", &remainder)
|
||||
&& (*remainder==':' || *remainder=='\0'))
|
||||
|| (strcase_startswith(r->request_header.origin, "http://127.0.0.1", &remainder)
|
||||
&& (*remainder==':' || *remainder=='\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);
|
||||
r->response.header.allow_methods="GET, POST, OPTIONS";
|
||||
r->response.header.allow_headers="Authorization";
|
||||
}else
|
||||
if (r->request_header.origin.hostname) {
|
||||
assert(r->request_header.origin.scheme);
|
||||
if ( ( ( strcmp(r->request_header.origin.scheme, "http") == 0
|
||||
|| strcmp(r->request_header.origin.scheme, "https") == 0
|
||||
)
|
||||
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
||||
|| strcmp(r->request_header.origin.hostname, "127.0.0.1") == 0
|
||||
)
|
||||
)
|
||||
|| ( strcmp(r->request_header.origin.scheme, "file") == 0
|
||||
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
||||
|| strcmp(r->request_header.origin.hostname, "127.0.0.1") == 0
|
||||
|| 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;
|
||||
}
|
||||
}
|
||||
if (r->verb == HTTP_VERB_OPTIONS){
|
||||
http_request_simple_response(r, 200, NULL);
|
||||
return 200;
|
||||
|
@ -136,15 +136,6 @@ test_CORS_Request(){
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '200'
|
||||
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 \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
|
Loading…
Reference in New Issue
Block a user