mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-22 04:15:54 -04:00
Merge branch 'CORS' of https://github.com/jucrouzet/Icecast-Server into jucrouzet-CORS
This commit is contained in:
commit
293b7db059
@ -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 tls.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
|
||||
|
264
src/cfgfile.c
264
src/cfgfile.c
@ -148,6 +148,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)
|
||||
@ -219,7 +230,7 @@ static inline void __read_unsigned_int(xmlDocPtr doc, xmlNodePtr node, unsigned
|
||||
}
|
||||
if (str)
|
||||
xmlFree(str);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int __parse_public(const char *str)
|
||||
{
|
||||
@ -657,6 +668,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));
|
||||
}
|
||||
|
||||
@ -1028,6 +1040,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));
|
||||
|
||||
@ -2220,6 +2234,254 @@ 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;
|
||||
|
||||
// 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,
|
||||
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"))) ||
|
||||
!strlen(base)
|
||||
) {
|
||||
ICECAST_LOG_WARN("Ignoring <cors><path> 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;
|
||||
}
|
||||
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);
|
||||
*cors_paths = _cors_sort_paths_by_base_length_desc(*cors_paths);
|
||||
}
|
||||
|
||||
static void _cors_sort_origins_by_length_desc(char **origins)
|
||||
{
|
||||
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++);
|
||||
|
||||
// Sort origin by length, descending.
|
||||
for (int step = 0; step < length; step++)
|
||||
{
|
||||
for (int i = 0; i < length - step - 1; i++)
|
||||
{
|
||||
if (strlen(origins[i]) < strlen(origins[i+1]))
|
||||
{
|
||||
temp = origins[i];
|
||||
origins[i] = origins[i + 1];
|
||||
origins[i + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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("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 (!allowed_count && !forbidden_count && !exposed_headers_count) {
|
||||
return 1;
|
||||
}
|
||||
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 (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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
free(path->exposed_headers);
|
||||
}
|
||||
xmlFree(path->base);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
config_options_t *config_parse_options(xmlNodePtr node)
|
||||
{
|
||||
config_options_t *ret = NULL;
|
||||
|
@ -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 <jack@xiph.org>,
|
||||
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org>,
|
||||
* Michael Smith <msmith@xiph.org>,
|
||||
* oddsock <oddsock@xiph.org>,
|
||||
* Karl Heyes <karl@xiph.org>
|
||||
@ -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 */
|
||||
@ -175,6 +175,21 @@ typedef struct _listener_t {
|
||||
tlsmode_t tls;
|
||||
} 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 _config_tls_context {
|
||||
char *cert_file;
|
||||
char *key_file;
|
||||
@ -220,6 +235,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;
|
||||
|
38
src/client.c
38
src/client.c
@ -44,6 +44,8 @@
|
||||
/* for ADMIN_COMMAND_ERROR */
|
||||
#include "admin.h"
|
||||
|
||||
#include "cors.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
@ -292,6 +294,42 @@ 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;
|
||||
char *message;
|
||||
|
||||
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
|
||||
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) {
|
||||
free(message);
|
||||
ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
|
||||
client_send_500(client, "Header generation failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
client_send_bytes(client, message, strlen(message));
|
||||
client_destroy(client);
|
||||
free(message);
|
||||
}
|
||||
|
||||
void client_send_426(client_t *client, reuse_t reuse)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
@ -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);
|
||||
|
@ -134,7 +134,7 @@ void connection_shutdown(void)
|
||||
tls_ctx_unref(tls_ctx);
|
||||
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);
|
||||
@ -1130,6 +1130,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;
|
||||
|
||||
@ -1139,7 +1140,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);
|
||||
@ -1157,6 +1164,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");
|
||||
|
243
src/cors.c
Normal file
243
src/cors.c
Normal file
@ -0,0 +1,243 @@
|
||||
/* Icecast
|
||||
*
|
||||
* This program is distributed under the GNU General Public License, version 2.
|
||||
* A copy of this license is included with this source.
|
||||
*
|
||||
* Copyright 2017, Julien CROUZET <contact@juliencrouzet.fr>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cors handling functions
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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 ((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;
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
22
src/cors.h
Normal file
22
src/cors.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* Icecast
|
||||
*
|
||||
* This program is distributed under the GNU General Public License, version 2.
|
||||
* A copy of this license is included with this source.
|
||||
*
|
||||
* Copyright 2017, Julien CROUZET <contact@juliencrouzet.fr>
|
||||
*/
|
||||
|
||||
#ifndef __CORS_H__
|
||||
#define __CORS_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#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__ */
|
13
src/util.c
13
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;
|
||||
}
|
||||
|
||||
@ -754,13 +761,13 @@ 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,
|
||||
connection_header,
|
||||
(client && client->admin_command == ADMIN_COMMAND_ERROR ?
|
||||
"GET, SOURCE" : "GET"),
|
||||
"GET, OPTIONS, SOURCE" : "GET, OPTIONS"),
|
||||
upgrade_header,
|
||||
currenttime_buffer,
|
||||
contenttype_buffer,
|
||||
|
Loading…
Reference in New Issue
Block a user