From 5909d155deaf1978314392aded56bf87a1038806 Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Fri, 6 Jan 2017 16:43:15 +0100 Subject: [PATCH 1/8] Config file parsing for option --- src/cfgfile.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cfgfile.h | 17 ++++++ 2 files changed, 181 insertions(+) diff --git a/src/cfgfile.c b/src/cfgfile.c index 4785e866..537ee706 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -146,6 +146,17 @@ static void _parse_events(event_registration_t **events, xmlNodePtr node); static void merge_mounts(mount_proxy * dst, mount_proxy * src); static inline void _merge_mounts_all(ice_config_t *c); +static void _parse_cors(xmlDocPtr doc, + xmlNodePtr node, + ice_config_cors_path_t **cors_paths); + +static int _parse_cors_path(xmlDocPtr doc, + xmlNodePtr node, + ice_config_cors_path_t *cors_path); + +static void config_clear_cors(ice_config_cors_path_t *cors_paths); + + operation_mode config_str_to_omode(const char *str) { if (!str || !*str) @@ -599,6 +610,7 @@ void config_clear(ice_config_t *c) #endif config_clear_http_header(c->http_headers); + config_clear_cors(c->cors_paths); memset(c, 0, sizeof(ice_config_t)); } @@ -969,6 +981,8 @@ static void _parse_root(xmlDocPtr doc, } else if (xmlStrcmp(node->name, XMLSTR("event-bindings")) == 0 || xmlStrcmp(node->name, XMLSTR("kartoffelsalat")) == 0) { _parse_events(&configuration->event, node->xmlChildrenNode); + } else if (xmlStrcmp(node->name, XMLSTR("cors")) == 0) { + _parse_cors(doc, node->xmlChildrenNode, &(configuration->cors_paths)); } } while ((node = node->next)); @@ -2101,6 +2115,156 @@ static void _parse_events(event_registration_t **events, xmlNodePtr node) } } +static void _parse_cors(xmlDocPtr doc, + xmlNodePtr node, + ice_config_cors_path_t **cors_paths) +{ + ice_config_cors_path_t *path = NULL; + ice_config_cors_path_t *next = NULL; + char *base = NULL; + + do { + if (node == NULL) + break; + if (xmlIsBlankNode(node)) + continue; + if (!node->name) + continue; + if (xmlStrcmp(node->name, XMLSTR("path")) != 0) { + continue; + } + if (!(base = (char *)xmlGetProp(node, XMLSTR("base")))) { + ICECAST_LOG_WARN("Ignoring tag without base attribute"); + xmlFree(xmlGetProp(node, XMLSTR("base"))); + continue; + } + + path = calloc(1, sizeof(ice_config_cors_path_t)); + if (!path) { + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + break; + } + path->base = base; + if (_parse_cors_path(doc, node, path)) { + base = NULL; + if (!*cors_paths) { + *cors_paths = path; + continue; + } + next = *cors_paths; + while (next->next) { + next = next->next; + } + next->next = path; + } else { + free(path); + } + } while ((node = node->next)); + /* in case we used break we may need to clean those up */ + if (base) + xmlFree(base); +} + +static int _parse_cors_path(xmlDocPtr doc, + xmlNodePtr node, + ice_config_cors_path_t *cors_path) +{ + int allowed_count = 0; + int forbidden_count = 0; + int exposed_headers_count = 0; + xmlNodePtr tmpNode = node->xmlChildrenNode; + + while ((tmpNode = tmpNode->next)) { + if (tmpNode == NULL) + break; + if (!tmpNode->name) + continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { + allowed_count++; + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { + forbidden_count++; + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { + exposed_headers_count++; + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("no-cors")) == 0) { + cors_path->no_cors = 1; + return 1; + } + } + if (!allowed_count && !forbidden_count && !exposed_headers_count) { + return 0; + } + if (allowed_count) { + cors_path->allowed = calloc(allowed_count + 1, sizeof(char *)); + cors_path->allowed[allowed_count] = NULL; + } + if (forbidden_count) { + cors_path->forbidden = calloc(forbidden_count + 1, sizeof(char *)); + cors_path->forbidden[forbidden_count] = NULL; + } + if (exposed_headers_count) { + cors_path->exposed_headers = calloc(exposed_headers_count + 1, sizeof(char *)); + cors_path->exposed_headers[exposed_headers_count] = NULL; + } + + tmpNode = node->xmlChildrenNode; + allowed_count = forbidden_count = exposed_headers_count = 0; + + while ((tmpNode = tmpNode->next)) { + if (tmpNode == NULL) + break; + if (!tmpNode->name) + continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { + cors_path->allowed[allowed_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { + cors_path->forbidden[forbidden_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { + cors_path->exposed_headers[exposed_headers_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + continue; + } + } + return 1; +} + +void config_clear_cors(ice_config_cors_path_t *cors_paths) +{ + while (cors_paths) { + ice_config_cors_path_t *path = cors_paths; + + cors_paths = path->next; + if (path->allowed) { + for (int i = 0; path->allowed[i]; i++) { + xmlFree(path->allowed[i]); + } + free(path->allowed); + } + if (path->forbidden) { + for (int i = 0; path->forbidden[i]; i++) { + xmlFree(path->forbidden[i]); + } + free(path->forbidden); + } + if (path->exposed_headers) { + for (int i = 0; path->exposed_headers[i]; i++) { + xmlFree(path->exposed_headers[i]); + } + free(path->exposed_headers); + } + xmlFree(path->base); + free(path); + } +} + config_options_t *config_parse_options(xmlNodePtr node) { config_options_t *ret = NULL; diff --git a/src/cfgfile.h b/src/cfgfile.h index d18725c6..91e3f438 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -175,6 +175,22 @@ typedef struct _listener_t { int ssl; } listener_t; +typedef struct ice_config_cors_path { + /* base path */ + char *base; + /* no-cors path */ + int no_cors; + /* allowed origins */ + char **allowed; + /* forbidden origins */ + char **forbidden; + /* exposed headers */ + char **exposed_headers; + /* link to the next list element */ + struct ice_config_cors_path *next; +} ice_config_cors_path_t; + + typedef struct ice_config_tag { char *config_filename; @@ -214,6 +230,7 @@ typedef struct ice_config_tag { char *master_password; ice_config_http_header_t *http_headers; + ice_config_cors_path_t *cors_paths; /* is TLS supported by the server? */ int tls_ok; From 052c3302a4a03c5b5592789f3ff13755e4420cda Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Fri, 6 Jan 2017 19:09:04 +0100 Subject: [PATCH 2/8] Sort paths and origins to optimize lookups --- src/cfgfile.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/src/cfgfile.c b/src/cfgfile.c index 537ee706..8dd48179 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -2115,6 +2115,38 @@ static void _parse_events(event_registration_t **events, xmlNodePtr node) } } +static ice_config_cors_path_t* _cors_sort_paths_by_base_length_desc(ice_config_cors_path_t *cors_paths) +{ + ice_config_cors_path_t *curr = cors_paths; + ice_config_cors_path_t *prev = cors_paths; + ice_config_cors_path_t *largest = cors_paths; + ice_config_cors_path_t *largestPrev = cors_paths; + ice_config_cors_path_t *tmp; + + // Enf of sorting or only one path or no path + if (!cors_paths || !cors_paths->next) { + return cors_paths; + } + // Find the largest base and set it first. + while(curr != NULL) { + if(strlen(curr->base) > strlen(largest->base)) { + largestPrev = prev; + largest = curr; + } + prev = curr; + curr = curr->next; + } + if(largest != cors_paths) { + largestPrev->next = cors_paths; + tmp = cors_paths->next; + cors_paths->next = largest->next; + largest->next = tmp; + } + // Recurse to the rest of the list + largest->next = _cors_sort_paths_by_base_length_desc(largest->next); + return largest; +} + static void _parse_cors(xmlDocPtr doc, xmlNodePtr node, ice_config_cors_path_t **cors_paths) @@ -2133,8 +2165,11 @@ static void _parse_cors(xmlDocPtr doc, if (xmlStrcmp(node->name, XMLSTR("path")) != 0) { continue; } - if (!(base = (char *)xmlGetProp(node, XMLSTR("base")))) { - ICECAST_LOG_WARN("Ignoring tag without base attribute"); + if ( + !(base = (char *)xmlGetProp(node, XMLSTR("base"))) || + !strlen(base) + ) { + ICECAST_LOG_WARN("Ignoring tag without base attribute or empty"); xmlFree(xmlGetProp(node, XMLSTR("base"))); continue; } @@ -2163,6 +2198,28 @@ static void _parse_cors(xmlDocPtr doc, /* in case we used break we may need to clean those up */ if (base) xmlFree(base); + *cors_paths = _cors_sort_paths_by_base_length_desc(*cors_paths); +} + +static void _cors_sort_origins_by_length_desc(char **origins) +{ + int length; + char *temp; + + if (!origins || !origins[1]) { + return; + } + for (length = 0; origins[length]; length++); + for(int step = 0; step name) continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("no-cors")) == 0) { + cors_path->no_cors = 1; + return 1; + } + if (xmlIsBlankNode(tmpNode)) + continue; if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { allowed_count++; continue; @@ -2191,10 +2254,6 @@ static int _parse_cors_path(xmlDocPtr doc, exposed_headers_count++; continue; } - if (xmlStrcmp(tmpNode->name, XMLSTR("no-cors")) == 0) { - cors_path->no_cors = 1; - return 1; - } } if (!allowed_count && !forbidden_count && !exposed_headers_count) { return 0; @@ -2220,6 +2279,8 @@ static int _parse_cors_path(xmlDocPtr doc, break; if (!tmpNode->name) continue; + if (xmlIsBlankNode(tmpNode)) + continue; if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { cors_path->allowed[allowed_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); continue; @@ -2233,6 +2294,9 @@ static int _parse_cors_path(xmlDocPtr doc, continue; } } + _cors_sort_origins_by_length_desc(cors_path->allowed); + _cors_sort_origins_by_length_desc(cors_path->forbidden); + _cors_sort_origins_by_length_desc(cors_path->exposed_headers); return 1; } From a8debbab5576cfe92fab689cdace7c569672f11b Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Fri, 6 Jan 2017 23:41:22 +0100 Subject: [PATCH 3/8] Handle cors in headers response --- src/Makefile.am | 4 +- src/cfgfile.c | 42 ++++++--- src/cfgfile.h | 6 +- src/cors.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cors.h | 27 ++++++ src/util.c | 11 ++- 6 files changed, 296 insertions(+), 21 deletions(-) create mode 100644 src/cors.c create mode 100644 src/cors.h diff --git a/src/Makefile.am b/src/Makefile.am index 59f4bdc4..29b2ba59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,14 +13,14 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \ acl.h auth.h \ format.h format_ogg.h format_mp3.h format_ebml.h \ format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \ - format_kate.h format_skeleton.h format_opus.h + format_kate.h format_skeleton.h format_opus.h cors.h icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \ util.c slave.c source.c stats.c refbuf.c client.c playlist.c \ xslt.c fserve.c admin.c md5.c matchfile.c \ format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \ format_kate.c format_skeleton.c format_opus.c \ event.c event_log.c event_exec.c \ - acl.c auth.c auth_htpasswd.c auth_anonymous.c auth_static.c + acl.c auth.c auth_htpasswd.c auth_anonymous.c auth_static.c cors.c EXTRA_icecast_SOURCES = curl.c yp.c \ auth_url.c event_url.c \ format_vorbis.c format_theora.c format_speex.c diff --git a/src/cfgfile.c b/src/cfgfile.c index 8dd48179..ef18855f 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -228,7 +228,7 @@ static inline void __read_unsigned_int(xmlDocPtr doc, xmlNodePtr node, unsigned } if (str) xmlFree(str); -} +} static inline int __parse_public(const char *str) { @@ -2205,7 +2205,7 @@ static void _cors_sort_origins_by_length_desc(char **origins) { int length; char *temp; - + if (!origins || !origins[1]) { return; } @@ -2230,7 +2230,7 @@ static int _parse_cors_path(xmlDocPtr doc, int forbidden_count = 0; int exposed_headers_count = 0; xmlNodePtr tmpNode = node->xmlChildrenNode; - + while ((tmpNode = tmpNode->next)) { if (tmpNode == NULL) break; @@ -2266,11 +2266,7 @@ static int _parse_cors_path(xmlDocPtr doc, cors_path->forbidden = calloc(forbidden_count + 1, sizeof(char *)); cors_path->forbidden[forbidden_count] = NULL; } - if (exposed_headers_count) { - cors_path->exposed_headers = calloc(exposed_headers_count + 1, sizeof(char *)); - cors_path->exposed_headers[exposed_headers_count] = NULL; - } - + tmpNode = node->xmlChildrenNode; allowed_count = forbidden_count = exposed_headers_count = 0; @@ -2290,13 +2286,34 @@ static int _parse_cors_path(xmlDocPtr doc, continue; } if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { - cors_path->exposed_headers[exposed_headers_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + char *orig_value = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + int first_value = 1; + + if (!cors_path->exposed_headers) { + cors_path->exposed_headers = calloc(strlen(orig_value) + 1, sizeof(char)); + } else { + cors_path->exposed_headers = realloc( + cors_path->exposed_headers, + (strlen(cors_path->exposed_headers) + strlen(orig_value) + 3) + ); + first_value = 0; + } + if (!cors_path->exposed_headers) { + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + break; + } + if (!first_value) { + cors_path->exposed_headers = strcat(cors_path->exposed_headers, ", "); + } + cors_path->exposed_headers = strcat(cors_path->exposed_headers, orig_value); + xmlFree(orig_value); continue; } } + _cors_sort_origins_by_length_desc(cors_path->allowed); _cors_sort_origins_by_length_desc(cors_path->forbidden); - _cors_sort_origins_by_length_desc(cors_path->exposed_headers); + return 1; } @@ -2304,7 +2321,7 @@ void config_clear_cors(ice_config_cors_path_t *cors_paths) { while (cors_paths) { ice_config_cors_path_t *path = cors_paths; - + cors_paths = path->next; if (path->allowed) { for (int i = 0; path->allowed[i]; i++) { @@ -2319,9 +2336,6 @@ void config_clear_cors(ice_config_cors_path_t *cors_paths) free(path->forbidden); } if (path->exposed_headers) { - for (int i = 0; path->exposed_headers[i]; i++) { - xmlFree(path->exposed_headers[i]); - } free(path->exposed_headers); } xmlFree(path->base); diff --git a/src/cfgfile.h b/src/cfgfile.h index 91e3f438..b960d322 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -3,7 +3,7 @@ * This program is distributed under the GNU General Public License, version 2. * A copy of this license is included with this source. * - * Copyright 2000-2004, Jack Moffitt , + * Copyright 2000-2004, Jack Moffitt , * Michael Smith , * oddsock , * Karl Heyes @@ -30,7 +30,7 @@ struct _mount_proxy; #include "global.h" #include "connection.h" -#define XMLSTR(str) ((xmlChar *)(str)) +#define XMLSTR(str) ((xmlChar *)(str)) typedef enum _operation_mode { /* Default operation mode. may depend on context */ @@ -185,7 +185,7 @@ typedef struct ice_config_cors_path { /* forbidden origins */ char **forbidden; /* exposed headers */ - char **exposed_headers; + char *exposed_headers; /* link to the next list element */ struct ice_config_cors_path *next; } ice_config_cors_path_t; diff --git a/src/cors.c b/src/cors.c new file mode 100644 index 00000000..7897fd2a --- /dev/null +++ b/src/cors.c @@ -0,0 +1,227 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2014, Philipp "ph3-der-loewe" Schafft , + */ + +/** + * Cors handling functions + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "cfgfile.h" +#include "client.h" +#include "logging.h" + +#define CATMODULE "CORS" + +static const char* cors_header_names[7] = { + "access-control-allow-origin", + "access-control-expose-headers", + "access-control-max-age", + "access-control-allow-credentials", + "access-control-allow-methods", + "access-control-allow-headers", + NULL +}; + +static const char *icy_headers = "icy-br, icy-caps, icy-description, icy-genre, icy-metaint, icy-metadata-interval, icy-name, icy-pub, icy-public, icy-url"; + +static ice_config_cors_path_t* _find_matching_path(ice_config_cors_path_t *paths, char *path) +{ + ice_config_cors_path_t *matching_path = paths; + + while(matching_path) { + if (strncmp(matching_path->base, path, strlen(matching_path->base)) == 0) { + return matching_path; + } + matching_path = matching_path->next; + } + return NULL; +} + +static int _cors_valid_origin(ice_config_cors_path_t *path, const char *origin) { + if (path->forbidden) { + for (int i = 0; path->forbidden[i]; i++) { + if (strstr(origin, path->forbidden[i]) == origin) { + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" matches forbidden origin \"%s\", not sending CORS", + origin, + path->forbidden[i] + ); + return 0; + } + } + } + if (path->allowed) { + for (int i = 0; path->allowed[i]; i++) { + if (strstr(origin, path->allowed[i]) == origin) { + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" matches allowed origin \"%s\", sending CORS", + origin, + path->allowed[i] + ); + return 1; + } + } + } + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" does not matches any declared origin, not sending CORS", + origin + ); + return 0; +} + +static void _add_header(char **out, + size_t *len, + const char *header_name, + const char *header_value) +{ + int new_length; + char *new_out; + + if (!header_name || !header_value || !strlen(header_name)) { + return; + } + new_length = strlen(header_name) + strlen(header_value) + 4; + new_out = realloc(*out, *len + new_length); + if (!new_out) { + ICECAST_LOG_ERROR("Out of memory while setting CORS header."); + return; + } + strcat(new_out, header_name); + strcat(new_out, ": "); + strcat(new_out, header_value); + strcat(new_out, "\r\n"); + *len += new_length; + *out = new_out; +} + + +static void _remove_header(char **out, + size_t *len, + const char *header_name) +{ + int header_start[100]; + int header_end[100]; + int current_position = 0; + int found_count = 0; + char *new_out; + + if (!*len) + return; + while((current_position < (*len -1)) && found_count < 100) { + char *substr = strcasestr((*out + current_position), header_name); + char *substr_end; + if (!substr) { + break; + } + substr_end = strstr(substr, "\r\n"); + if (!substr_end) { + return; + } + header_start[found_count] = substr - *out; + header_end[found_count] = substr_end - *out + 2; + current_position = header_end[found_count]; + found_count++; + } + if (!found_count) { + return; + } + current_position = 0; + new_out = calloc(*len + 1, sizeof(char)); + if (!new_out) { + return; + } + for (int i = 0; i < found_count; i++) { + while (current_position < header_start[i]) { + new_out[current_position] = *out[current_position]; + } + current_position = header_end[i]; + } + while (current_position < *len) { + new_out[current_position] = 0; + current_position++; + } + *out = new_out; + for (int i = 0; i < found_count; i++) { + *len -= header_end[i] - header_start[i]; + } + return; +} + + +static void _add_cors(char **out, + size_t *len, + ice_config_cors_path_t *path, + char *origin) +{ + _add_header(out, len, "Access-Control-Allow-Origin", origin); + if (path->exposed_headers) { + _add_header(out, len, "Access-Control-Expose-Headers", path->exposed_headers); + } else { + _add_header(out, len, "Access-Control-Expose-Headers", icy_headers); + } + _add_header(out, len, "Access-Control-Max-Age", "3600"); + _add_header(out, len, "Access-Control-Allow-Credentials", "true"); + _add_header(out, len, "Access-Control-Allow-Methods", "GET"); + _add_header(out, len, "Access-Control-Allow-Headers", "Icy-MetaData"); + return; +} + +static void _remove_cors(char **out, size_t *len) { + for(int i = 0; cors_header_names[i]; i++) { + _remove_header(out, len, cors_header_names[i]); + } + return; +} + +void cors_set_headers(char **out, + size_t *len, + ice_config_cors_path_t *paths, + struct _client_tag *client) +{ + char *origin = NULL; + char *path = (char *)client->parser->uri; + ice_config_cors_path_t *matching_path; + + if (!paths) + return; + if (!(origin = (char *)httpp_getvar(client->parser, "origin"))) + return; + if (!path) + return; + + matching_path = _find_matching_path(paths, path); + if (!matching_path) { + ICECAST_LOG_DEBUG( + "Requested path \"%s\" does not matches any declared CORS configured path", + path + ); + return; + } + + ICECAST_LOG_DEBUG( + "Requested path \"%s\" matches the \"%s\" declared CORS path", + path, + matching_path->base + ); + + _remove_cors(out, len); + + if ( + !matching_path->no_cors && + _cors_valid_origin(matching_path, origin) + ) { + _add_cors(out, len, matching_path, origin); + } + return; +} diff --git a/src/cors.h b/src/cors.h new file mode 100644 index 00000000..b044ee75 --- /dev/null +++ b/src/cors.h @@ -0,0 +1,27 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2000-2004, Jack Moffitt , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + * Copyright 2014, Philipp "ph3-der-loewe" Schafft , + */ + +#ifndef __CORS_H__ +#define __CORS_H__ + +#include + +#include "cfgfile.h" +#include "client.h" + +void cors_set_headers(char **out, + size_t *len, + ice_config_cors_path_t *options, + struct _client_tag *client); + +#endif /* __CORS_H__ */ diff --git a/src/util.c b/src/util.c index d163d852..99770e37 100644 --- a/src/util.c +++ b/src/util.c @@ -49,6 +49,7 @@ #include "util.h" #include "source.h" #include "admin.h" +#include "cors.h" #define CATMODULE "util" @@ -641,7 +642,11 @@ static inline void _build_headers_loop(char **ret, size_t *len, ice_config_htt } while ((header = header->next)); *ret = r; } -static inline char * _build_headers(int status, ice_config_t *config, source_t *source) { +static inline char * _build_headers(int status, + ice_config_t *config, + source_t *source, + struct _client_tag *client) +{ mount_proxy *mountproxy = NULL; char *ret = NULL; size_t len = 1; @@ -656,6 +661,8 @@ static inline char * _build_headers(int status, ice_config_t *config, source_t * if (mountproxy && mountproxy->http_headers) _build_headers_loop(&ret, &len, mountproxy->http_headers, status); + cors_set_headers(&ret, &len, config->cors_paths, client); + return ret; } @@ -751,7 +758,7 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset, currenttime_buffer[0] = '\0'; config = config_get_config(); - extra_headers = _build_headers(status, config, source); + extra_headers = _build_headers(status, config, source, client); ret = snprintf (out, len, "%sServer: %s\r\nConnection: %s\r\nAccept-Encoding: identity\r\nAllow: %s\r\n%s%s%s%s%s%s%s%s", status_buffer, config->server_id, From d272760e7b093bf81618f439244bc58808d7f29f Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Tue, 10 Jan 2017 22:40:16 +0100 Subject: [PATCH 4/8] Handle pre-flighted requests --- src/client.c | 30 ++++++++++++++++++++++++++++++ src/client.h | 1 + src/connection.c | 14 ++++++++++++-- src/util.c | 2 +- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/client.c b/src/client.c index 5fafd8cf..6d10df60 100644 --- a/src/client.c +++ b/src/client.c @@ -44,6 +44,8 @@ /* for ADMIN_COMMAND_ERROR */ #include "admin.h" +#include "cors.h" + #ifdef _WIN32 #define snprintf _snprintf #endif @@ -294,6 +296,34 @@ void client_send_101(client_t *client, reuse_t reuse) fserve_add_client(client, NULL); } +/* Sends an empty 204 response (for OPTIONS) */ +void client_send_204(client_t *client) +{ + ssize_t ret; + + ret = util_http_build_header(client->refbuf->data, // Response buffer + PER_CLIENT_REFBUF_SIZE, // Buffer size + 0, // Offset + 0, // Prevent cache + 204, // Status code + "No Content", // Status message + NULL, // Content-Type + NULL, // Charset + NULL, // Data + NULL, // Source + client); + + if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) { + ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); + client_send_500(client, "Header generation failed."); + return; + } + + client->respcode = 204; + client->refbuf->len = strlen(client->refbuf->data); + fserve_add_client(client, NULL); +} + void client_send_426(client_t *client, reuse_t reuse) { ssize_t ret; diff --git a/src/client.h b/src/client.h index 2f2a3259..2ccb0c93 100644 --- a/src/client.h +++ b/src/client.h @@ -111,6 +111,7 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser); void client_destroy(client_t *client); void client_send_error(client_t *client, int status, int plain, const char *message); void client_send_101(client_t *client, reuse_t reuse); +void client_send_204(client_t *client); void client_send_426(client_t *client, reuse_t reuse); int client_send_bytes (client_t *client, const void *buf, unsigned len); int client_read_bytes (client_t *client, void *buf, unsigned len); diff --git a/src/connection.c b/src/connection.c index 9e5f95d2..c41fdd9f 100644 --- a/src/connection.c +++ b/src/connection.c @@ -136,7 +136,7 @@ void connection_shutdown(void) #endif matchfile_release(banned_ip); matchfile_release(allowed_ip); - + thread_cond_destroy(&global.shutdown_cond); thread_rwlock_destroy(&_source_shutdown_rwlock); thread_spin_destroy (&_connection_lock); @@ -1142,6 +1142,7 @@ static int _handle_aliases(client_t *client, char **uri) */ static void _handle_authed_client(client_t *client, void *uri, auth_result result) { + httpp_request_type_e req_type; auth_stack_release(client->authstack); client->authstack = NULL; @@ -1151,7 +1152,13 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul return; } - if (acl_test_method(client->acl, client->parser->req_type) != ACL_POLICY_ALLOW) { + // If path is not /admin/ OPTIONS should respect the same acl as GET + // for preflighted request + req_type = client->parser->req_type; + if (strstr(client->parser->uri, "/admin/") != client->parser->uri) { + req_type = httpp_req_get; + } + if (acl_test_method(client->acl, req_type) != ACL_POLICY_ALLOW) { ICECAST_LOG_ERROR("Client (role=%s, username=%s) not allowed to use this request method on %H", client->role, client->username, uri); client_send_error(client, 401, 1, "You need to authenticate\r\n"); free(uri); @@ -1169,6 +1176,9 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul case httpp_req_get: _handle_get_request(client, uri); break; + case httpp_req_options: + client_send_204(client); + break; default: ICECAST_LOG_ERROR("Wrong request type from client"); client_send_error(client, 400, 0, "unknown request"); diff --git a/src/util.c b/src/util.c index 99770e37..9cb17607 100644 --- a/src/util.c +++ b/src/util.c @@ -764,7 +764,7 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset, config->server_id, connection_header, (client && client->admin_command == ADMIN_COMMAND_ERROR ? - "GET, SOURCE" : "GET"), + "GET, OPTIONS, SOURCE" : "GET, OPTIONS"), (config->tls_ok ? "Upgrade: TLS/1.0\r\n" : ""), currenttime_buffer, contenttype_buffer, From 74153ca19fc1cc4923a6888464ae1e0c8b14d966 Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Tue, 10 Jan 2017 23:52:51 +0100 Subject: [PATCH 5/8] Access-Control-Allow-Headers should be lowercase & handle "*" as allowed origin --- src/cors.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cors.c b/src/cors.c index 7897fd2a..09efceca 100644 --- a/src/cors.c +++ b/src/cors.c @@ -63,6 +63,13 @@ static int _cors_valid_origin(ice_config_cors_path_t *path, const char *origin) } if (path->allowed) { for (int i = 0; path->allowed[i]; i++) { + if ((strlen(path->allowed[i]) == 1) && path->allowed[i][0] == '*') { + ICECAST_LOG_DEBUG( + "All (\"*\") allowed origin for \"%s\", sending CORS", + origin + ); + return 1; + } if (strstr(origin, path->allowed[i]) == origin) { ICECAST_LOG_DEBUG( "Declared origin \"%s\" matches allowed origin \"%s\", sending CORS", @@ -173,7 +180,7 @@ static void _add_cors(char **out, _add_header(out, len, "Access-Control-Max-Age", "3600"); _add_header(out, len, "Access-Control-Allow-Credentials", "true"); _add_header(out, len, "Access-Control-Allow-Methods", "GET"); - _add_header(out, len, "Access-Control-Allow-Headers", "Icy-MetaData"); + _add_header(out, len, "Access-Control-Allow-Headers", "icy-metadata"); return; } From 74e3399dd52972de28315d6b1198a0701e6ce816 Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Sat, 4 Feb 2017 18:50:11 +0100 Subject: [PATCH 6/8] ePirat review changes --- src/cfgfile.c | 181 ++++++++++++++------------- src/client.c | 24 ++-- src/connection.c | 4 +- src/cors.c | 315 ++++++++++++++++++++++++----------------------- src/cors.h | 7 +- 5 files changed, 278 insertions(+), 253 deletions(-) diff --git a/src/cfgfile.c b/src/cfgfile.c index ef18855f..04577309 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -2180,7 +2180,7 @@ static void _parse_cors(xmlDocPtr doc, break; } path->base = base; - if (_parse_cors_path(doc, node, path)) { + if (!_parse_cors_path(doc, node, path)) { base = NULL; if (!*cors_paths) { *cors_paths = path; @@ -2226,95 +2226,108 @@ static int _parse_cors_path(xmlDocPtr doc, xmlNodePtr node, ice_config_cors_path_t *cors_path) { - int allowed_count = 0; - int forbidden_count = 0; - int exposed_headers_count = 0; - xmlNodePtr tmpNode = node->xmlChildrenNode; + int allowed_count = 0; + int forbidden_count = 0; + int exposed_headers_count = 0; + xmlNodePtr tmpNode = node->xmlChildrenNode; - while ((tmpNode = tmpNode->next)) { - if (tmpNode == NULL) - break; - if (!tmpNode->name) - continue; - if (xmlStrcmp(tmpNode->name, XMLSTR("no-cors")) == 0) { - cors_path->no_cors = 1; - return 1; + while ((tmpNode = tmpNode->next)) { + if (tmpNode == NULL) + break; + if (!tmpNode->name) + continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("no-cors")) == 0) { + cors_path->no_cors = 1; + return 0; + } + if (xmlIsBlankNode(tmpNode)) + continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { + allowed_count++; + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { + forbidden_count++; + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { + exposed_headers_count++; + continue; + } } - if (xmlIsBlankNode(tmpNode)) - continue; - if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { - allowed_count++; - continue; + if (!allowed_count && !forbidden_count && !exposed_headers_count) { + return 1; } - if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { - forbidden_count++; - continue; + if (allowed_count) { + cors_path->allowed = calloc(allowed_count + 1, sizeof(char *)); + if (!cors_path->allowed) { + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + return 1; + } + cors_path->allowed[allowed_count] = NULL; } - if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { - exposed_headers_count++; - continue; + if (forbidden_count) { + cors_path->forbidden = calloc(forbidden_count + 1, sizeof(char *)); + if (!cors_path->forbidden) { + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + if (cors_path->allowed) + free(cors_path->allowed); + return 1; + } + cors_path->forbidden[forbidden_count] = NULL; } - } - if (!allowed_count && !forbidden_count && !exposed_headers_count) { + + tmpNode = node->xmlChildrenNode; + allowed_count = forbidden_count = exposed_headers_count = 0; + + while ((tmpNode = tmpNode->next)) { + if (tmpNode == NULL) + break; + if (!tmpNode->name) + continue; + if (xmlIsBlankNode(tmpNode)) + continue; + if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { + cors_path->allowed[allowed_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { + cors_path->forbidden[forbidden_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + continue; + } + if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { + char *orig_value = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); + int first_value = 1; + + if (!cors_path->exposed_headers) { + cors_path->exposed_headers = calloc(strlen(orig_value) + 1, sizeof(char)); + } else { + cors_path->exposed_headers = realloc( + cors_path->exposed_headers, + (strlen(cors_path->exposed_headers) + strlen(orig_value) + 3) + ); + first_value = 0; + } + if (!cors_path->exposed_headers) { + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + if (cors_path->allowed) + free(cors_path->allowed); + if (cors_path->forbidden) + free(cors_path->forbidden); + return 1; + } + if (!first_value) + cors_path->exposed_headers = strcat(cors_path->exposed_headers, ", "); + cors_path->exposed_headers = strcat(cors_path->exposed_headers, orig_value); + xmlFree(orig_value); + continue; + } + } + + _cors_sort_origins_by_length_desc(cors_path->allowed); + _cors_sort_origins_by_length_desc(cors_path->forbidden); + return 0; - } - if (allowed_count) { - cors_path->allowed = calloc(allowed_count + 1, sizeof(char *)); - cors_path->allowed[allowed_count] = NULL; - } - if (forbidden_count) { - cors_path->forbidden = calloc(forbidden_count + 1, sizeof(char *)); - cors_path->forbidden[forbidden_count] = NULL; - } - - tmpNode = node->xmlChildrenNode; - allowed_count = forbidden_count = exposed_headers_count = 0; - - while ((tmpNode = tmpNode->next)) { - if (tmpNode == NULL) - break; - if (!tmpNode->name) - continue; - if (xmlIsBlankNode(tmpNode)) - continue; - if (xmlStrcmp(tmpNode->name, XMLSTR("allowed-origin")) == 0) { - cors_path->allowed[allowed_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); - continue; - } - if (xmlStrcmp(tmpNode->name, XMLSTR("forbidden-origin")) == 0) { - cors_path->forbidden[forbidden_count++] = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); - continue; - } - if (xmlStrcmp(tmpNode->name, XMLSTR("exposed-header")) == 0) { - char *orig_value = (char *)xmlNodeListGetString(doc, tmpNode->xmlChildrenNode, 1); - int first_value = 1; - - if (!cors_path->exposed_headers) { - cors_path->exposed_headers = calloc(strlen(orig_value) + 1, sizeof(char)); - } else { - cors_path->exposed_headers = realloc( - cors_path->exposed_headers, - (strlen(cors_path->exposed_headers) + strlen(orig_value) + 3) - ); - first_value = 0; - } - if (!cors_path->exposed_headers) { - ICECAST_LOG_ERROR("Out of memory while parsing config file"); - break; - } - if (!first_value) { - cors_path->exposed_headers = strcat(cors_path->exposed_headers, ", "); - } - cors_path->exposed_headers = strcat(cors_path->exposed_headers, orig_value); - xmlFree(orig_value); - continue; - } - } - - _cors_sort_origins_by_length_desc(cors_path->allowed); - _cors_sort_origins_by_length_desc(cors_path->forbidden); - - return 1; } void config_clear_cors(ice_config_cors_path_t *cors_paths) diff --git a/src/client.c b/src/client.c index 6d10df60..c4d89418 100644 --- a/src/client.c +++ b/src/client.c @@ -299,9 +299,16 @@ void client_send_101(client_t *client, reuse_t reuse) /* Sends an empty 204 response (for OPTIONS) */ void client_send_204(client_t *client) { - ssize_t ret; + ssize_t ret; + char *message; - ret = util_http_build_header(client->refbuf->data, // Response buffer + message = calloc(PER_CLIENT_REFBUF_SIZE, sizeof(char)); + + if (!message) { + client_send_500(client, "Unable to allocate memory for response"); + return; + } + ret = util_http_build_header(message, // Response buffer PER_CLIENT_REFBUF_SIZE, // Buffer size 0, // Offset 0, // Prevent cache @@ -314,14 +321,15 @@ void client_send_204(client_t *client) client); if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) { - ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); - client_send_500(client, "Header generation failed."); - return; + free(message); + ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); + client_send_500(client, "Header generation failed."); + return; } - client->respcode = 204; - client->refbuf->len = strlen(client->refbuf->data); - fserve_add_client(client, NULL); + client_send_bytes(client, message, strlen(message)); + client_destroy(client); + free(message); } void client_send_426(client_t *client, reuse_t reuse) diff --git a/src/connection.c b/src/connection.c index c41fdd9f..540c35d8 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1156,7 +1156,7 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul // for preflighted request req_type = client->parser->req_type; if (strstr(client->parser->uri, "/admin/") != client->parser->uri) { - req_type = httpp_req_get; + req_type = httpp_req_get; } if (acl_test_method(client->acl, req_type) != ACL_POLICY_ALLOW) { ICECAST_LOG_ERROR("Client (role=%s, username=%s) not allowed to use this request method on %H", client->role, client->username, uri); @@ -1177,7 +1177,7 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul _handle_get_request(client, uri); break; case httpp_req_options: - client_send_204(client); + client_send_204(client); break; default: ICECAST_LOG_ERROR("Wrong request type from client"); diff --git a/src/cors.c b/src/cors.c index 09efceca..81f5c844 100644 --- a/src/cors.c +++ b/src/cors.c @@ -3,7 +3,7 @@ * This program is distributed under the GNU General Public License, version 2. * A copy of this license is included with this source. * - * Copyright 2014, Philipp "ph3-der-loewe" Schafft , + * Copyright 2017, Julien CROUZET */ /** @@ -24,67 +24,67 @@ #define CATMODULE "CORS" static const char* cors_header_names[7] = { - "access-control-allow-origin", - "access-control-expose-headers", - "access-control-max-age", - "access-control-allow-credentials", - "access-control-allow-methods", - "access-control-allow-headers", - NULL + "access-control-allow-origin", + "access-control-expose-headers", + "access-control-max-age", + "access-control-allow-credentials", + "access-control-allow-methods", + "access-control-allow-headers", + NULL }; static const char *icy_headers = "icy-br, icy-caps, icy-description, icy-genre, icy-metaint, icy-metadata-interval, icy-name, icy-pub, icy-public, icy-url"; static ice_config_cors_path_t* _find_matching_path(ice_config_cors_path_t *paths, char *path) { - ice_config_cors_path_t *matching_path = paths; + ice_config_cors_path_t *matching_path = paths; - while(matching_path) { - if (strncmp(matching_path->base, path, strlen(matching_path->base)) == 0) { - return matching_path; + while(matching_path) { + if (strncmp(matching_path->base, path, strlen(matching_path->base)) == 0) { + return matching_path; + } + matching_path = matching_path->next; } - matching_path = matching_path->next; - } - return NULL; + return NULL; } static int _cors_valid_origin(ice_config_cors_path_t *path, const char *origin) { - if (path->forbidden) { - for (int i = 0; path->forbidden[i]; i++) { - if (strstr(origin, path->forbidden[i]) == origin) { - ICECAST_LOG_DEBUG( - "Declared origin \"%s\" matches forbidden origin \"%s\", not sending CORS", - origin, - path->forbidden[i] - ); - return 0; - } + if (path->forbidden) { + for (int i = 0; path->forbidden[i]; i++) { + if (strstr(origin, path->forbidden[i]) == origin) { + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" matches forbidden origin \"%s\", not sending CORS", + origin, + path->forbidden[i] + ); + return 0; + } + } } - } - if (path->allowed) { - for (int i = 0; path->allowed[i]; i++) { - if ((strlen(path->allowed[i]) == 1) && path->allowed[i][0] == '*') { - ICECAST_LOG_DEBUG( - "All (\"*\") allowed origin for \"%s\", sending CORS", - origin - ); - return 1; - } - if (strstr(origin, path->allowed[i]) == origin) { - ICECAST_LOG_DEBUG( - "Declared origin \"%s\" matches allowed origin \"%s\", sending CORS", - origin, - path->allowed[i] - ); - return 1; - } + if (path->allowed) { + for (int i = 0; path->allowed[i]; i++) { + if ((strlen(path->allowed[i]) == 1) && path->allowed[i][0] == '*') { + ICECAST_LOG_DEBUG( + "All (\"*\") allowed origin for \"%s\", sending CORS", + origin + ); + return 1; + } + if (strstr(origin, path->allowed[i]) == origin) { + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" matches allowed origin \"%s\", sending CORS", + origin, + path->allowed[i] + ); + return 1; + } + } } - } - ICECAST_LOG_DEBUG( - "Declared origin \"%s\" does not matches any declared origin, not sending CORS", - origin - ); - return 0; + ICECAST_LOG_DEBUG( + "Declared origin \"%s\" does not matches any declared origin, not sending CORS", + origin + ); + return 0; } static void _add_header(char **out, @@ -92,77 +92,86 @@ static void _add_header(char **out, const char *header_name, const char *header_value) { - int new_length; - char *new_out; + int new_length; + char *new_out; - if (!header_name || !header_value || !strlen(header_name)) { - return; - } - new_length = strlen(header_name) + strlen(header_value) + 4; - new_out = realloc(*out, *len + new_length); - if (!new_out) { - ICECAST_LOG_ERROR("Out of memory while setting CORS header."); - return; - } - strcat(new_out, header_name); - strcat(new_out, ": "); - strcat(new_out, header_value); - strcat(new_out, "\r\n"); - *len += new_length; - *out = new_out; + if (!header_name || !header_value || !strlen(header_name)) { + return; + } + new_length = strlen(header_name) + strlen(header_value) + 4; + new_out = calloc(*len + new_length, sizeof(char)); + if (!new_out) { + ICECAST_LOG_ERROR("Out of memory while setting CORS header."); + return; + } + snprintf(new_out, + *len + new_length, + "%s%s: %s\r\n", + *out, + header_name, + header_value); + free(*out); + *len += new_length; + *out = new_out; } +/** + * Removes an header by its name in current headers list. + * Header removal is needed to remove any manually added headers + * added while a forbidden rule is active. + */ static void _remove_header(char **out, size_t *len, const char *header_name) { - int header_start[100]; - int header_end[100]; - int current_position = 0; - int found_count = 0; - char *new_out; + int header_start[100]; + int header_end[100]; + int current_position = 0; + int found_count = 0; + char *new_out; - if (!*len) - return; - while((current_position < (*len -1)) && found_count < 100) { - char *substr = strcasestr((*out + current_position), header_name); - char *substr_end; - if (!substr) { - break; + if (!*len) + return; + while((current_position < (*len -1)) && found_count < 100) { + char *substr = strcasestr((*out + current_position), header_name); + char *substr_end; + if (!substr) { + break; + } + substr_end = strstr(substr, "\r\n"); + if (!substr_end) { + return; + } + header_start[found_count] = substr - *out; + header_end[found_count] = substr_end - *out + 2; + current_position = header_end[found_count]; + found_count++; } - substr_end = strstr(substr, "\r\n"); - if (!substr_end) { - return; + if (!found_count) { + return; } - header_start[found_count] = substr - *out; - header_end[found_count] = substr_end - *out + 2; - current_position = header_end[found_count]; - found_count++; - } - if (!found_count) { - return; - } - current_position = 0; - new_out = calloc(*len + 1, sizeof(char)); - if (!new_out) { - return; - } - for (int i = 0; i < found_count; i++) { - while (current_position < header_start[i]) { - new_out[current_position] = *out[current_position]; + current_position = 0; + new_out = calloc(*len + 1, sizeof(char)); + if (!new_out) { + return; } - current_position = header_end[i]; - } - while (current_position < *len) { - new_out[current_position] = 0; - current_position++; - } - *out = new_out; - for (int i = 0; i < found_count; i++) { - *len -= header_end[i] - header_start[i]; - } - return; + free(*out); + for (int i = 0; i < found_count; i++) { + while (current_position < header_start[i]) { + new_out[current_position] = *out[current_position]; + } + current_position = header_end[i]; + } + while (current_position < *len) { + new_out[current_position] = 0; + current_position++; + } + *out = new_out; + for (int i = 0; i < found_count; i++) { + *len -= header_end[i] - header_start[i]; + } + return; } @@ -171,24 +180,24 @@ static void _add_cors(char **out, ice_config_cors_path_t *path, char *origin) { - _add_header(out, len, "Access-Control-Allow-Origin", origin); - if (path->exposed_headers) { - _add_header(out, len, "Access-Control-Expose-Headers", path->exposed_headers); - } else { - _add_header(out, len, "Access-Control-Expose-Headers", icy_headers); - } - _add_header(out, len, "Access-Control-Max-Age", "3600"); - _add_header(out, len, "Access-Control-Allow-Credentials", "true"); - _add_header(out, len, "Access-Control-Allow-Methods", "GET"); - _add_header(out, len, "Access-Control-Allow-Headers", "icy-metadata"); - return; + _add_header(out, len, "Access-Control-Allow-Origin", origin); + if (path->exposed_headers) { + _add_header(out, len, "Access-Control-Expose-Headers", path->exposed_headers); + } else { + _add_header(out, len, "Access-Control-Expose-Headers", icy_headers); + } + _add_header(out, len, "Access-Control-Max-Age", "3600"); + _add_header(out, len, "Access-Control-Allow-Credentials", "true"); + _add_header(out, len, "Access-Control-Allow-Methods", "GET"); + _add_header(out, len, "Access-Control-Allow-Headers", "icy-metadata"); + return; } static void _remove_cors(char **out, size_t *len) { - for(int i = 0; cors_header_names[i]; i++) { - _remove_header(out, len, cors_header_names[i]); - } - return; + for(int i = 0; cors_header_names[i]; i++) { + _remove_header(out, len, cors_header_names[i]); + } + return; } void cors_set_headers(char **out, @@ -196,39 +205,39 @@ void cors_set_headers(char **out, ice_config_cors_path_t *paths, struct _client_tag *client) { - char *origin = NULL; - char *path = (char *)client->parser->uri; - ice_config_cors_path_t *matching_path; + char *origin = NULL; + char *path = (char *)client->parser->uri; + ice_config_cors_path_t *matching_path; - if (!paths) - return; - if (!(origin = (char *)httpp_getvar(client->parser, "origin"))) - return; - if (!path) - return; + if (!paths) + return; + if (!(origin = (char *)httpp_getvar(client->parser, "origin"))) + return; + if (!path) + return; + + matching_path = _find_matching_path(paths, path); + if (!matching_path) { + ICECAST_LOG_DEBUG( + "Requested path \"%s\" does not matches any declared CORS configured path", + path + ); + return; + } - matching_path = _find_matching_path(paths, path); - if (!matching_path) { ICECAST_LOG_DEBUG( - "Requested path \"%s\" does not matches any declared CORS configured path", - path + "Requested path \"%s\" matches the \"%s\" declared CORS path", + path, + matching_path->base ); + + _remove_cors(out, len); + + if ( + !matching_path->no_cors && + _cors_valid_origin(matching_path, origin) + ) { + _add_cors(out, len, matching_path, origin); + } return; - } - - ICECAST_LOG_DEBUG( - "Requested path \"%s\" matches the \"%s\" declared CORS path", - path, - matching_path->base - ); - - _remove_cors(out, len); - - if ( - !matching_path->no_cors && - _cors_valid_origin(matching_path, origin) - ) { - _add_cors(out, len, matching_path, origin); - } - return; } diff --git a/src/cors.h b/src/cors.h index b044ee75..9010992b 100644 --- a/src/cors.h +++ b/src/cors.h @@ -3,12 +3,7 @@ * This program is distributed under the GNU General Public License, version 2. * A copy of this license is included with this source. * - * Copyright 2000-2004, Jack Moffitt , - * oddsock , - * Karl Heyes - * and others (see AUTHORS for details). - * Copyright 2014, Philipp "ph3-der-loewe" Schafft , + * Copyright 2017, Julien CROUZET */ #ifndef __CORS_H__ From 2d35fa42836dc0adfff0540c95a61495d6a79ad6 Mon Sep 17 00:00:00 2001 From: Julien CROUZET Date: Sat, 4 Feb 2017 18:58:11 +0100 Subject: [PATCH 7/8] 4 spaces indent --- src/cfgfile.c | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/cfgfile.c b/src/cfgfile.c index 04577309..62141fcb 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -2117,34 +2117,34 @@ static void _parse_events(event_registration_t **events, xmlNodePtr node) static ice_config_cors_path_t* _cors_sort_paths_by_base_length_desc(ice_config_cors_path_t *cors_paths) { - ice_config_cors_path_t *curr = cors_paths; - ice_config_cors_path_t *prev = cors_paths; - ice_config_cors_path_t *largest = cors_paths; - ice_config_cors_path_t *largestPrev = cors_paths; - ice_config_cors_path_t *tmp; + ice_config_cors_path_t *curr = cors_paths; + ice_config_cors_path_t *prev = cors_paths; + ice_config_cors_path_t *largest = cors_paths; + ice_config_cors_path_t *largestPrev = cors_paths; + ice_config_cors_path_t *tmp; - // Enf of sorting or only one path or no path - if (!cors_paths || !cors_paths->next) { - return cors_paths; - } - // Find the largest base and set it first. - while(curr != NULL) { - if(strlen(curr->base) > strlen(largest->base)) { - largestPrev = prev; - largest = curr; - } - prev = curr; - curr = curr->next; - } - if(largest != cors_paths) { - largestPrev->next = cors_paths; - tmp = cors_paths->next; - cors_paths->next = largest->next; - largest->next = tmp; - } - // Recurse to the rest of the list - largest->next = _cors_sort_paths_by_base_length_desc(largest->next); - return largest; + // End of sorting or only one path or no path + if (!cors_paths || !cors_paths->next) { + return cors_paths; + } + // Find the largest base and set it first. + while(curr != NULL) { + if(strlen(curr->base) > strlen(largest->base)) { + largestPrev = prev; + largest = curr; + } + prev = curr; + curr = curr->next; + } + if(largest != cors_paths) { + largestPrev->next = cors_paths; + tmp = cors_paths->next; + cors_paths->next = largest->next; + largest->next = tmp; + } + // Recurse to the rest of the list + largest->next = _cors_sort_paths_by_base_length_desc(largest->next); + return largest; } static void _parse_cors(xmlDocPtr doc, @@ -2169,30 +2169,30 @@ static void _parse_cors(xmlDocPtr doc, !(base = (char *)xmlGetProp(node, XMLSTR("base"))) || !strlen(base) ) { - ICECAST_LOG_WARN("Ignoring tag without base attribute or empty"); - xmlFree(xmlGetProp(node, XMLSTR("base"))); - continue; + ICECAST_LOG_WARN("Ignoring tag without base attribute or empty"); + xmlFree(xmlGetProp(node, XMLSTR("base"))); + continue; } path = calloc(1, sizeof(ice_config_cors_path_t)); if (!path) { - ICECAST_LOG_ERROR("Out of memory while parsing config file"); - break; + ICECAST_LOG_ERROR("Out of memory while parsing config file"); + break; } path->base = base; if (!_parse_cors_path(doc, node, path)) { - base = NULL; - if (!*cors_paths) { - *cors_paths = path; - continue; - } - next = *cors_paths; - while (next->next) { - next = next->next; - } - next->next = path; + base = NULL; + if (!*cors_paths) { + *cors_paths = path; + continue; + } + next = *cors_paths; + while (next->next) { + next = next->next; + } + next->next = path; } else { - free(path); + free(path); } } while ((node = node->next)); /* in case we used break we may need to clean those up */ @@ -2203,14 +2203,14 @@ static void _parse_cors(xmlDocPtr doc, static void _cors_sort_origins_by_length_desc(char **origins) { - int length; - char *temp; + int length; + char *temp; - if (!origins || !origins[1]) { - return; - } - for (length = 0; origins[length]; length++); - for(int step = 0; step Date: Tue, 10 Oct 2017 19:20:02 +0200 Subject: [PATCH 8/8] Rearrange/comment origins sorting. Linting --- src/cfgfile.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cfgfile.c b/src/cfgfile.c index f3b5335f..a9e23a8e 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -2322,21 +2322,28 @@ static void _parse_cors(xmlDocPtr doc, static void _cors_sort_origins_by_length_desc(char **origins) { - int length; + uint length; char *temp; + // If there are no origins or only one, no sort. if (!origins || !origins[1]) { return; } + + // Count origins. for (length = 0; origins[length]; length++); - for(int step = 0; step