...
 
Commits (25)
......@@ -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 Schafft <lion@lion.leolix.org>
* Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
......@@ -31,7 +31,7 @@ struct acl_tag {
/* admin/ interface */
struct {
int command;
admin_command_id_t command;
acl_policy_t policy;
} admin_commands[MAX_ADMIN_COMMANDS];
size_t admin_commands_len;
......@@ -253,7 +253,7 @@ int acl_set_admin_str__callbck(acl_t *acl,
const char *str)
{
size_t read_i, write_i;
int command = admin_get_command(str);
admin_command_id_t command = admin_get_command(str);
if (command == ADMIN_COMMAND_ERROR)
return -1;
......@@ -279,7 +279,7 @@ int acl_set_admin_str__callbck(acl_t *acl,
return 0;
}
acl_policy_t acl_test_admin(acl_t *acl, int command)
acl_policy_t acl_test_admin(acl_t *acl, admin_command_id_t command)
{
size_t i;
......
......@@ -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 Schafft <lion@lion.leolix.org>
* Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __ACL_H__
......@@ -18,6 +18,11 @@
#include <libxml/tree.h>
#include "common/httpp/httpp.h"
struct acl_tag;
typedef struct acl_tag acl_t;
#include "admin.h"
typedef enum acl_policy_tag {
/* Error on function call */
ACL_POLICY_ERROR = -1,
......@@ -27,8 +32,6 @@ typedef enum acl_policy_tag {
ACL_POLICY_DENY = 1
} acl_policy_t;
struct acl_tag;
typedef struct acl_tag acl_t;
/* basic functions to work with ACLs */
acl_t * acl_new(void);
......@@ -48,7 +51,7 @@ acl_policy_t acl_test_method(acl_t * acl, httpp_request_type_e method);
/* admin/ interface specific functions */
int acl_set_admin_str__callbck(acl_t * acl, acl_policy_t policy, const char * str);
#define acl_set_admin_str(acl,policy,str) acl_set_ANY_str((acl), (policy), (str), acl_set_admin_str__callbck)
acl_policy_t acl_test_admin(acl_t * acl, int command);
acl_policy_t acl_test_admin(acl_t * acl, admin_command_id_t command);
/* web/ interface specific functions */
int acl_set_web_policy(acl_t * acl, acl_policy_t policy);
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2011-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __ADMIN_H__
......@@ -15,7 +16,20 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <stdint.h>
/* Command IDs */
typedef int32_t admin_command_id_t;
/* formats */
typedef enum {
ADMIN_FORMAT_AUTO,
ADMIN_FORMAT_RAW,
ADMIN_FORMAT_TRANSFORMED,
ADMIN_FORMAT_PLAINTEXT
} admin_format_t;
#include "compat.h"
#include "refbuf.h"
#include "client.h"
#include "source.h"
......@@ -27,21 +41,25 @@
#define ADMINTYPE_MOUNT 2
#define ADMINTYPE_HYBRID (ADMINTYPE_GENERAL|ADMINTYPE_MOUNT)
/* formats */
#define RAW 1
#define TRANSFORMED 2
#define PLAINTEXT 3
/* special commands */
#define ADMIN_COMMAND_ERROR (-1)
#define ADMIN_COMMAND_ANY 0 /* for ACL framework */
#define ADMIN_COMMAND_ERROR ((admin_command_id_t)(-1))
#define ADMIN_COMMAND_ANY ((admin_command_id_t)0) /* for ACL framework */
typedef void (*admin_request_function_ptr)(client_t * client, source_t * source, admin_format_t format);
typedef struct admin_command_handler {
const char *route;
const int type;
const int format;
const admin_request_function_ptr function;
} admin_command_handler_t;
void admin_handle_request(client_t *client, const char *uri);
void admin_send_response(xmlDocPtr doc,
client_t *client,
int response,
const char *xslt_template);
void admin_send_response(xmlDocPtr doc,
client_t *client,
admin_format_t response,
const char *xslt_template);
void admin_add_listeners_to_mount(source_t *source,
xmlNodePtr parent,
......@@ -49,7 +67,13 @@ void admin_add_listeners_to_mount(source_t *source,
xmlNodePtr admin_add_role_to_authentication(auth_t *auth, xmlNodePtr parent);
int admin_get_command(const char *command);
int admin_get_command_type(int command);
admin_command_id_t admin_get_command(const char *command);
int admin_get_command_type(admin_command_id_t command);
/* Register and unregister admin commands below /admin/$prefix/.
* All parameters must be kept in memory as long as the registration is valid as there will be no copy made.
*/
int admin_command_table_register(const char *prefix, size_t handlers_length, const admin_command_handler_t *handlers);
int admin_command_table_unregister(const char *prefix);
#endif /* __ADMIN_H__ */
......@@ -394,7 +394,7 @@ static void auth_add_client(auth_t *auth, client_t *client, void (*on_no_match)(
auth_user->on_no_match = on_no_match;
auth_user->on_result = on_result;
auth_user->userdata = userdata;
ICECAST_LOG_INFO("adding client %p for authentication on %p", client, auth);
ICECAST_LOG_DEBUG("adding client %p for authentication on %p", client, auth);
queue_auth_client(auth_user);
}
......
......@@ -8,7 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __AUTH_H__
......@@ -19,7 +19,7 @@
#endif
struct source_tag;
struct auth_tag;
typedef struct auth_tag auth_t;
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
......@@ -66,7 +66,7 @@ typedef struct auth_client_tag
} auth_client;
typedef struct auth_tag
struct auth_tag
{
/* unique ID */
unsigned long id;
......@@ -113,7 +113,7 @@ typedef struct auth_tag
acl_t *acl;
/* role name for later matching, may be NULL if no role name was given in config */
char *role;
} auth_t;
};
typedef struct auth_stack_tag auth_stack_t;
......
......@@ -191,6 +191,11 @@ static auth_result htpasswd_auth (auth_client *auth_user)
}
htpasswd_recheckfile (htpasswd);
if (htpasswd->users) {
ICECAST_LOG_ERROR("No user list.");
return AUTH_NOMATCH;
}
thread_rwlock_rlock (&htpasswd->file_rwlock);
entry.name = client->username;
if (avl_get_by_key (htpasswd->users, &entry, &result) == 0) {
......@@ -257,8 +262,18 @@ static auth_result htpasswd_adduser (auth_t *auth, const char *username, const c
htpasswd_user entry;
void *result;
if (!state->filename) {
ICECAST_LOG_ERROR("No filename given in options for authenticator.");
return AUTH_FAILED;
}
htpasswd_recheckfile (state);
if (state->users) {
ICECAST_LOG_ERROR("No user list.");
return AUTH_FAILED;
}
thread_rwlock_wlock (&state->file_rwlock);
entry.name = (char*)username;
......@@ -301,6 +316,17 @@ static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
struct stat file_info;
state = auth->state;
if (!state->filename) {
ICECAST_LOG_ERROR("No filename given in options for authenticator.");
return AUTH_FAILED;
}
if (state->users) {
ICECAST_LOG_ERROR("No user list.");
return AUTH_FAILED;
}
thread_rwlock_wlock (&state->file_rwlock);
passwdfile = fopen(state->filename, "rb");
......@@ -383,8 +409,18 @@ static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
state = auth->state;
if (!state->filename) {
ICECAST_LOG_ERROR("No filename given in options for authenticator.");
return AUTH_FAILED;
}
htpasswd_recheckfile(state);
if (state->users) {
ICECAST_LOG_ERROR("No user list.");
return AUTH_FAILED;
}
thread_rwlock_rlock(&state->file_rwlock);
node = avl_get_first(state->users);
while (node) {
......
......@@ -28,6 +28,7 @@
#include "common/thread/thread.h"
#include "cfgfile.h"
#include "global.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
......@@ -551,6 +552,21 @@ static void config_clear_mount(mount_proxy *mount)
free(mount);
}
static void config_clear_resource(resource_t *resource)
{
resource_t *nextresource;
while (resource) {
nextresource = resource->next;
xmlFree(resource->source);
xmlFree(resource->destination);
xmlFree(resource->bind_address);
xmlFree(resource->vhost);
free(resource);
resource = nextresource;
}
}
listener_t *config_clear_listener(listener_t *listener)
{
listener_t *next = NULL;
......@@ -572,8 +588,6 @@ void config_clear(ice_config_t *c)
*nextrelay;
mount_proxy *mount,
*nextmount;
aliases *alias,
*nextalias;
#ifdef USE_YP
int i;
#endif
......@@ -632,16 +646,7 @@ void config_clear(ice_config_t *c)
mount = nextmount;
}
alias = c->aliases;
while (alias) {
nextalias = alias->next;
xmlFree(alias->source);
xmlFree(alias->destination);
xmlFree(alias->bind_address);
xmlFree(alias->vhost);
free(alias);
alias = nextalias;
}
config_clear_resource(c->resources);
dirnode = c->dir_list;
while (dirnode) {
......@@ -1899,14 +1904,82 @@ static void _parse_directory(xmlDocPtr doc,
configuration->num_yp_directories++;
}
static void _parse_resource(xmlDocPtr doc,
xmlNodePtr node,
ice_config_t *configuration)
{
char *temp;
resource_t *resource,
*current,
*last;
resource = calloc(1, sizeof(resource_t));
if (resource == NULL) {
ICECAST_LOG_ERROR("Can not allocate memory for resource.");
return;
}
resource->next = NULL;
resource->source = (char *)xmlGetProp(node, XMLSTR("source"));
resource->destination = (char *)xmlGetProp(node, XMLSTR("destination"));
if (!resource->destination)
resource->destination = (char *)xmlGetProp(node, XMLSTR("dest"));
if (!resource->source && resource->destination) {
resource->source = resource->destination;
resource->destination = NULL;
} else if (!resource->source && !resource->destination) {
config_clear_resource(resource);
return;
}
temp = (char *)xmlGetProp(node, XMLSTR("port"));
if(temp != NULL) {
resource->port = util_str_to_int(temp, resource->port);
xmlFree(temp);
} else {
resource->port = -1;
}
resource->bind_address = (char *)xmlGetProp(node, XMLSTR("bind-address"));
resource->vhost = (char *)xmlGetProp(node, XMLSTR("vhost"));
temp = (char *)xmlGetProp(node, XMLSTR("omode"));
if (temp) {
resource->omode = config_str_to_omode(temp);
xmlFree(temp);
} else {
resource->omode = OMODE_DEFAULT;
}
temp = (char *)xmlGetProp(node, XMLSTR("prefixmatch"));
if (temp) {
resource->flags |= util_str_to_bool(temp) ? ALIAS_FLAG_PREFIXMATCH : 0;
xmlFree(temp);
}
/* Attach new <resource> as last entry into the global list. */
current = configuration->resources;
last = NULL;
while (current) {
last = current;
current = current->next;
}
if (last) {
last->next = resource;
} else {
configuration->resources = resource;
}
}
static void _parse_paths(xmlDocPtr doc,
xmlNodePtr node,
ice_config_t *configuration)
{
char *temp;
aliases *alias,
*current,
*last;
char *temp;
do {
if (node == NULL)
......@@ -1988,49 +2061,7 @@ static void _parse_paths(xmlDocPtr doc,
if (configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] == '/')
configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] = 0;
} else if (xmlStrcmp(node->name, XMLSTR("resource")) == 0 || xmlStrcmp(node->name, XMLSTR("alias")) == 0) {
alias = malloc(sizeof(aliases));
alias->next = NULL;
alias->source = (char *)xmlGetProp(node, XMLSTR("source"));
alias->destination = (char *)xmlGetProp(node, XMLSTR("destination"));
if (!alias->destination)
alias->destination = (char *)xmlGetProp(node, XMLSTR("dest"));
if (!alias->source && alias->destination) {
alias->source = alias->destination;
alias->destination = NULL;
} else if (!alias->source && !alias->destination) {
xmlFree(alias->source);
xmlFree(alias->destination);
free(alias);
continue;
}
temp = (char *)xmlGetProp(node, XMLSTR("port"));
if(temp != NULL) {
alias->port = util_str_to_int(temp, alias->port);
xmlFree(temp);
} else {
alias->port = -1;
}
alias->bind_address = (char *)xmlGetProp(node, XMLSTR("bind-address"));
alias->vhost = (char *)xmlGetProp(node, XMLSTR("vhost"));
temp = (char *)xmlGetProp(node, XMLSTR("omode"));
if (temp) {
alias->omode = config_str_to_omode(temp);
xmlFree(temp);
} else {
alias->omode = OMODE_DEFAULT;
}
current = configuration->aliases;
last = NULL;
while (current) {
last = current;
current = current->next;
}
if(last) {
last->next = alias;
} else {
configuration->aliases = alias;
}
_parse_resource(doc, node, configuration);
}
} while ((node = node->next));
}
......
......@@ -27,7 +27,7 @@ struct _mount_proxy;
#include <libxml/tree.h>
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "global.h"
#include "slave.h"
#include "connection.h"
#define XMLSTR(str) ((xmlChar *)(str))
......@@ -155,15 +155,18 @@ typedef struct _mount_proxy {
struct _mount_proxy *next;
} mount_proxy;
typedef struct _aliases {
#define ALIAS_FLAG_PREFIXMATCH 0x0001
typedef struct _resource {
char *source;
char *destination;
int port;
char *bind_address;
char *vhost;
operation_mode omode;
struct _aliases *next;
} aliases;
unsigned int flags;
struct _resource *next;
} resource_t;
typedef struct _listener_t {
struct _listener_t *next;
......@@ -238,7 +241,7 @@ typedef struct ice_config_tag {
char *allowfile;
char *webroot_dir;
char *adminroot_dir;
aliases *aliases;
resource_t *resources;
char *access_log;
char *error_log;
......
......@@ -28,6 +28,7 @@
#include "common/avl/avl.h"
#include "common/httpp/httpp.h"
#include "global.h"
#include "cfgfile.h"
#include "connection.h"
#include "refbuf.h"
......@@ -54,6 +55,45 @@
static inline void client_send_500(client_t *client, const char *message);
/* This returns the protocol ID based on the string.
* If the string is invalid for any reason we return ICECAST_PROTOCOL_HTTP.
*/
protocol_t client_protocol_from_string(const char *str)
{
if (!str) {
ICECAST_LOG_ERROR("No protocol string given. Returning ICECAST_PROTOCOL_HTTP.");
return ICECAST_PROTOCOL_HTTP;
}
if (strcasecmp(str, "http") == 0) {
return ICECAST_PROTOCOL_HTTP;
} else if (strcasecmp(str, "icy") == 0 || strcasecmp(str, "shoutcast") == 0) {
return ICECAST_PROTOCOL_SHOUTCAST;
} else if (strcasecmp(str, "gopher") == 0) {
return ICECAST_PROTOCOL_GOPHER;
} else {
ICECAST_LOG_ERROR("Unknown protocol \"%H\" string given. Returning ICECAST_PROTOCOL_HTTP.", str);
return ICECAST_PROTOCOL_HTTP;
}
}
const char * client_protocol_to_string(protocol_t protocol)
{
switch (protocol) {
case ICECAST_PROTOCOL_HTTP:
return "http";
break;
case ICECAST_PROTOCOL_SHOUTCAST:
return "icy";
break;
case ICECAST_PROTOCOL_GOPHER:
return "gopher";
break;
}
return NULL;
}
/* create a client_t with the provided connection and parser details. Return
* 0 on success, -1 if server limit has been reached. In either case a
* client_t is returned just in case a message needs to be returned. Should
......@@ -133,7 +173,7 @@ void client_destroy(client_t *client)
if (client == NULL)
return;
if (client->reuse != ICECAST_REUSE_CLOSE) {
if (client->protocol != ICECAST_PROTOCOL_GOPHER && client->reuse != ICECAST_REUSE_CLOSE) {
client_reuseconnection(client);
return;
}
......@@ -148,6 +188,10 @@ void client_destroy(client_t *client)
if (auth_release_client(client))
return;
if (client->protocol == ICECAST_PROTOCOL_GOPHER && client->respcode != 0) {
client_send_bytes(client, "\r\n.\r\n", 5);
}
/* write log entry if ip is set (some things don't set it, like outgoing
* slave requests
*/
......@@ -255,9 +299,11 @@ static inline void _client_send_error(client_t *client, int plain, const icecast
}
data->len = strlen(data->data);
snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
if (client->protocol != ICECAST_PROTOCOL_GOPHER) {
snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
"Content-Length: %llu\r\n\r\n",
(long long unsigned int)data->len);
}
client->respcode = error->http_status;
client->refbuf->len = strlen (client->refbuf->data);
......@@ -266,7 +312,7 @@ static inline void _client_send_error(client_t *client, int plain, const icecast
fserve_add_client (client, NULL);
}
void client_send_error_by_id(client_t *client, int id)
void client_send_error_by_id(client_t *client, icecast_error_id_t id)
{
const icecast_error_t *error = error_get_by_id(id);
const char *pref;
......@@ -365,6 +411,26 @@ static inline void client_send_500(client_t *client, const char *message)
client_destroy(client);
}
admin_format_t client_get_admin_format_by_content_negotiation(client_t *client)
{
const char *pref;
if (!client || !client->parser)
return CLIENT_DEFAULT_ADMIN_FORMAT;
pref = util_http_select_best(httpp_getvar(client->parser, "accept"), "text/xml", "text/html", "text/plain", (const char*)NULL);
if (strcmp(pref, "text/xml") == 0) {
return ADMIN_FORMAT_RAW;
} else if (strcmp(pref, "text/html") == 0) {
return ADMIN_FORMAT_TRANSFORMED;
} else if (strcmp(pref, "text/plain") == 0) {
return ADMIN_FORMAT_PLAINTEXT;
} else {
return CLIENT_DEFAULT_ADMIN_FORMAT;
}
}
/* helper function for sending the data to a client */
int client_send_bytes(client_t *client, const void *buf, unsigned len)
{
......
......@@ -19,16 +19,23 @@
#ifndef __CLIENT_H__
#define __CLIENT_H__
typedef struct _client_tag client_t;
#include "errors.h"
#include "connection.h"
#include "refbuf.h"
#include "acl.h"
#include "cfgfile.h"
#include "admin.h"
#include "common/httpp/httpp.h"
#include "common/httpp/encoding.h"
#define CLIENT_DEFAULT_ADMIN_FORMAT ADMIN_FORMAT_TRANSFORMED
typedef enum _protocol_tag {
ICECAST_PROTOCOL_HTTP = 0,
ICECAST_PROTOCOL_SHOUTCAST
ICECAST_PROTOCOL_SHOUTCAST,
ICECAST_PROTOCOL_GOPHER,
} protocol_t;
typedef enum _reuse_tag {
......@@ -40,8 +47,7 @@ typedef enum _reuse_tag {
ICECAST_REUSE_UPGRADETLS
} reuse_t;
typedef struct _client_tag
{
struct _client_tag {
/* mode of operation for this client */
operation_mode mode;
......@@ -64,7 +70,7 @@ typedef struct _client_tag
int respcode;
/* admin command if any. ADMIN_COMMAND_ERROR if not an admin command. */
int admin_command;
admin_command_id_t admin_command;
/* authentication instances we still need to go thru */
struct auth_stack_tag *authstack;
......@@ -104,14 +110,17 @@ typedef struct _client_tag
/* function to check if refbuf needs updating */
int (*check_buffer)(struct source_tag *source, struct _client_tag *client);
};
} client_t;
protocol_t client_protocol_from_string(const char *str);
const char * client_protocol_to_string(protocol_t protocol);
int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client);
void client_send_error_by_id(client_t *client, int id);
void client_send_error_by_id(client_t *client, icecast_error_id_t id);
void client_send_101(client_t *client, reuse_t reuse);
void client_send_426(client_t *client, reuse_t reuse);
admin_format_t client_get_admin_format_by_content_negotiation(client_t *client);
int client_send_bytes (client_t *client, const void *buf, unsigned len);
int client_read_bytes (client_t *client, void *buf, unsigned len);
void client_set_queue (client_t *client, refbuf_t *refbuf);
......
......@@ -503,6 +503,8 @@ static void process_request_queue (void)
node->stream_offset = (ptr+2) - client->refbuf->data;
break;
}
if (client->refbuf->data[0] == '/' && strstr(client->refbuf->data, "\n") != NULL)
break;
pass_it = 0;
} while (0);
......@@ -915,18 +917,6 @@ static void _handle_get_request(client_t *client, char *uri) {
stats_event_inc(NULL, "client_connections");
/* Dispatch legacy admin.cgi requests */
if (strcmp(uri, "/admin.cgi") == 0) {
ICECAST_LOG_DEBUG("Client %p requesting admin interface.", client);
admin_handle_request(client, uri + 1);
return;
} /* Dispatch all admin requests */
else if (strncmp(uri, "/admin/", 7) == 0) {
ICECAST_LOG_DEBUG("Client %p requesting admin interface.", client);
admin_handle_request(client, uri + 7);
return;
}
/* this is a web/ request. let's check if we are allowed to do that. */
if (acl_test_web(client->acl) != ACL_POLICY_ALLOW) {
/* doesn't seem so, sad client :( */
......@@ -1074,10 +1064,53 @@ static void _handle_shoutcast_compatible(client_queue_t *node)
return;
}
/* Handle <alias> lookups here.
static int _handle_gopher_compatible(client_queue_t *node)
{
size_t n_len;
char *n;
char *ptr;
http_parser_t *parser;
client_t *client = node->client;
ptr = strstr(client->refbuf->data, "\r\n");
if (!ptr)
ptr = strstr(client->refbuf->data, "\n");
if (!ptr)
return -1;
*ptr = 0;
n_len = strlen(client->refbuf->data) + 80;
n = calloc(1, n_len);
if (!n) {
client_destroy(client);
free(node);
return 0;
}
snprintf(n, n_len, "GET %s HTTP/1.0\r\n\r\n", client->refbuf->data);
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, n, strlen(n))) {
client->refbuf->len = 0;
client->parser = parser;
client->protocol = ICECAST_PROTOCOL_GOPHER;
return 1;
} else {
httpp_destroy(parser);
client_destroy(client);
}
free(n);
free(node);
return 0;
}
/* Handle <resource> lookups here.
*/
static int _handle_aliases(client_t *client, char **uri)
static int _handle_resources(client_t *client, char **uri)
{
const char *http_host = httpp_getvar(client->parser, "host");
char *serverhost = NULL;
......@@ -1087,7 +1120,7 @@ static int _handle_aliases(client_t *client, char **uri)
char *new_uri = NULL;
ice_config_t *config;
listener_t *listen_sock;
aliases *alias;
resource_t *resource;
if (http_host) {
vhost = strdup(http_host);
......@@ -1105,21 +1138,49 @@ static int _handle_aliases(client_t *client, char **uri)
serverport = listen_sock->port;
}
alias = config->aliases;
while (alias) {
if (strcmp(*uri, alias->source) == 0 &&
(alias->port == -1 || alias->port == serverport) &&
(alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0)) &&
(alias->vhost == NULL || (vhost != NULL && strcmp(alias->vhost, vhost) == 0)) ) {
if (alias->destination)
new_uri = strdup(alias->destination);
if (alias->omode != OMODE_DEFAULT)
client->mode = alias->omode;
ICECAST_LOG_DEBUG("alias has made %s into %s", *uri, new_uri);
break;
resource = config->resources;
/* We now go thru all resources and see if any matches. */
for (; resource; resource = resource->next) {
/* We check for several aspects, if they DO NOT match, we continue with our search. */
/* Check for the URI to match. */
if (resource->flags & ALIAS_FLAG_PREFIXMATCH) {
size_t len = strlen(resource->source);
if (strncmp(*uri, resource->source, len) != 0)
continue;
ICECAST_LOG_DEBUG("Match: *uri='%s', resource->source='%s', len=%zu", *uri, resource->source, len);
} else {
if (strcmp(*uri, resource->source) != 0)
continue;
}
alias = alias->next;
/* Check for the server's port to match. */
if (resource->port != -1 && resource->port != serverport)
continue;
/* Check for the server's bind address to match. */
if (resource->bind_address != NULL && serverhost != NULL && strcmp(resource->bind_address, serverhost) != 0)
continue;
/* Check for the vhost to match. */
if (resource->vhost != NULL && vhost != NULL && strcmp(resource->vhost, vhost) != 0)
continue;
/* Ok, we found a matching entry. */
if (resource->destination) {
if (resource->flags & ALIAS_FLAG_PREFIXMATCH) {
size_t len = strlen(resource->source);
asprintf(&new_uri, "%s%s", resource->destination, (*uri) + len);
} else {
new_uri = strdup(resource->destination);
}
}
if (resource->omode != OMODE_DEFAULT)
client->mode = resource->omode;
ICECAST_LOG_DEBUG("resource has made %s into %s", *uri, new_uri);
break;
}
config_release_config();
......@@ -1135,6 +1196,23 @@ static int _handle_aliases(client_t *client, char **uri)
return 0;
}
static void _handle_admin_request(client_t *client, char *adminuri)
{
ICECAST_LOG_DEBUG("Client %p requesting admin interface.", client);
stats_event_inc(NULL, "client_connections");
switch (client->parser->req_type) {
case httpp_req_get:
admin_handle_request(client, adminuri);
break;
default:
ICECAST_LOG_ERROR("Wrong request type from client");
client_send_error_by_id(client, ICECAST_ERROR_CON_UNKNOWN_REQUEST);
break;
}
}
/* Handle any client that passed the authing process.
*/
static void _handle_authed_client(client_t *client, void *uri, auth_result result)
......@@ -1155,6 +1233,18 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul
return;
}
/* Dispatch legacy admin.cgi requests */
if (strcmp(uri, "/admin.cgi") == 0) {
_handle_admin_request(client, uri + 1);
free(uri);
return;
} /* Dispatch all admin requests */
else if (strncmp(uri, "/admin/", 7) == 0) {
_handle_admin_request(client, uri + 7);
free(uri);
return;
}
switch (client->parser->req_type) {
case httpp_req_source:
case httpp_req_put:
......@@ -1315,6 +1405,13 @@ static void _handle_connection(void)
continue;
}
/* check for gopher clients */
if (client->refbuf->data[0] == '/') {
if (_handle_gopher_compatible(node) == 0) {
continue;
}
}
/* process normal HTTP headers */
if (client->parser) {
already_parsed = 1;
......@@ -1377,7 +1474,7 @@ static void _handle_connection(void)
client->mode = config_str_to_omode(httpp_get_query_param(client->parser, "omode"));
if (_handle_aliases(client, &uri) != 0) {
if (_handle_resources(client, &uri) != 0) {
client_destroy (client);
continue;
}
......
......@@ -135,7 +135,7 @@ static const icecast_error_t __errors[] = {
.message = "XSLT problem"}
};
const icecast_error_t * error_get_by_id(int id) {
const icecast_error_t * error_get_by_id(icecast_error_id_t id) {
size_t i;
for (i = 0; i < (sizeof(__errors)/sizeof(*__errors)); i++) {
......
......@@ -9,48 +9,50 @@
#ifndef __ERRORS_H__
#define __ERRORS_H__
#define ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING 1
#define ICECAST_ERROR_ADMIN_METADAT_BADCALL 2
#define ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION 3
#define ICECAST_ERROR_ADMIN_MISSING_PARAMETER 4
#define ICECAST_ERROR_ADMIN_missing_parameter 5 /* what is this? */
#define ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES 6
#define ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION 7
#define ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS 8
#define ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS 9
#define ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND 10
#define ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST 11
#define ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE 12
#define ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL 13
#define ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND 14
#define ICECAST_ERROR_AUTH_BUSY 15
#define ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS 16
#define ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR 17
#define ICECAST_ERROR_CON_MISSING_PASS_PARAMETER 18
#define ICECAST_ERROR_CON_MOUNT_IN_USE 19
#define ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH 20
#define ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN 21
#define ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT 22
#define ICECAST_ERROR_CON_rejecting_client_for_whatever_reason 23 /* ??? */
#define ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT 24
#define ICECAST_ERROR_CON_UNIMPLEMENTED 25
#define ICECAST_ERROR_CON_UNKNOWN_REQUEST 26
#define ICECAST_ERROR_CON_UPGRADE_ERROR 27
#define ICECAST_ERROR_FSERV_FILE_NOT_FOUND 28
#define ICECAST_ERROR_FSERV_FILE_NOT_READABLE 29
#define ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE 30
#define ICECAST_ERROR_GEN_BUFFER_REALLOC 31
#define ICECAST_ERROR_GEN_CLIENT_LIMIT 32
#define ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE 33
#define ICECAST_ERROR_GEN_HEADER_GEN_FAILED 34
#define ICECAST_ERROR_GEN_MEMORY_EXHAUSTED 35
#define ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE 36
#define ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR 37
#define ICECAST_ERROR_XSLT_PARSE 38
#define ICECAST_ERROR_XSLT_problem 39
typedef enum {
ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING,
ICECAST_ERROR_ADMIN_METADAT_BADCALL,
ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION,
ICECAST_ERROR_ADMIN_MISSING_PARAMETER,
ICECAST_ERROR_ADMIN_missing_parameter, /* what is this? */
ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES,
ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION,
ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS,
ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS,
ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND,
ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST,
ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE,
ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL,
ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND,
ICECAST_ERROR_AUTH_BUSY,
ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS,
ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR,
ICECAST_ERROR_CON_MISSING_PASS_PARAMETER,
ICECAST_ERROR_CON_MOUNT_IN_USE,
ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH,
ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN,
ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT,
ICECAST_ERROR_CON_rejecting_client_for_whatever_reason, /* ??? */
ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT,
ICECAST_ERROR_CON_UNIMPLEMENTED,
ICECAST_ERROR_CON_UNKNOWN_REQUEST,
ICECAST_ERROR_CON_UPGRADE_ERROR,
ICECAST_ERROR_FSERV_FILE_NOT_FOUND,
ICECAST_ERROR_FSERV_FILE_NOT_READABLE,
ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE,
ICECAST_ERROR_GEN_BUFFER_REALLOC,
ICECAST_ERROR_GEN_CLIENT_LIMIT,
ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE,
ICECAST_ERROR_GEN_HEADER_GEN_FAILED,
ICECAST_ERROR_GEN_MEMORY_EXHAUSTED,
ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE,
ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR,
ICECAST_ERROR_XSLT_PARSE,
ICECAST_ERROR_XSLT_problem
} icecast_error_id_t;
struct icecast_error_tag {
const int id;
const icecast_error_id_t id;
const int http_status;
const char *uuid;
const char *message;
......@@ -58,6 +60,6 @@ struct icecast_error_tag {
typedef struct icecast_error_tag icecast_error_t;
const icecast_error_t * error_get_by_id(int id);
const icecast_error_t * error_get_by_id(icecast_error_id_t id);
#endif /* __ERRORS_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 2014, Philipp Schafft <lion@lion.leolix.org>
* Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __EVENT_H__
......@@ -61,7 +61,7 @@ struct event_tag {
char *client_role; /* from client->role */
char *client_username; /* from client->username */
char *client_useragent; /* from httpp_getvar(client->parser, "user-agent") */
int client_admin_command; /* from client->admin_command */
admin_command_id_t client_admin_command; /* from client->admin_command */
};
struct event_registration_tag {
......
......@@ -21,6 +21,7 @@
#endif
#include "event.h"
#include "global.h"
#include "logging.h"
#define CATMODULE "event_exec"
......
......@@ -287,7 +287,7 @@ int format_advance_queue(source_t *source, client_t *client)
* calling functions will use a already freed client struct and
* cause a segfault!
*/
static int format_prepare_headers (source_t *source, client_t *client)
static int format_prepare_headers_http (source_t *source, client_t *client)
{
size_t remaining;
char *ptr;
......@@ -404,13 +404,34 @@ static int format_prepare_headers (source_t *source, client_t *client)
ptr += bytes;
client->refbuf->len -= remaining;
if (source->format->create_client_data)
return 0;
}
static int format_prepare_headers (source_t *source, client_t *client)
{
switch (client->protocol) {
case ICECAST_PROTOCOL_HTTP:
case ICECAST_PROTOCOL_SHOUTCAST:
if (format_prepare_headers_http(source, client) != 0)
return -1;
break;
case ICECAST_PROTOCOL_GOPHER:
client->refbuf->len = 0;
break;
}
if (source->format->create_client_data) {
if (source->format->create_client_data (source, client) < 0) {
ICECAST_LOG_ERROR("Client format header generation failed. "
"(Likely not enough or wrong source data) Dropping client.");
client->respcode = 500;
return -1;
}
}
return 0;
}
......
......@@ -507,7 +507,7 @@ int fserve_client_create (client_t *httpclient, const char *path)
*eol = '\0';
doc = stats_get_xml (0, reference, httpclient->mode);
free (reference);
admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested);
admin_send_response (doc, httpclient, ADMIN_FORMAT_TRANSFORMED, xslt_playlist_requested);
xmlFreeDoc(doc);
free (fullpath);
return 0;
......@@ -621,10 +621,12 @@ int fserve_client_create (client_t *httpclient, const char *path)
fclose(file);
return -1;
}
bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
"Accept-Ranges: bytes\r\n"
"Content-Length: %" PRI_OFF_T "\r\n\r\n",
content_length);
if (httpclient->protocol != ICECAST_PROTOCOL_GOPHER) {
bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
"Accept-Ranges: bytes\r\n"
"Content-Length: %" PRI_OFF_T "\r\n\r\n",
content_length);
}
free (type);
}
httpclient->refbuf->len = bytes;
......
......@@ -19,15 +19,9 @@
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/httpp/httpp.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "source.h"
#include "format.h"
#include "global.h"
#include "source.h"
ice_global_t global;
......
......@@ -21,6 +21,7 @@
#define ICECAST_VERSION_STRING "Icecast " PACKAGE_VERSION
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "slave.h"
#include "common/net/sock.h"
......
......@@ -8,12 +8,14 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Copyright 2012-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __SOURCE_H__
#define __SOURCE_H__
typedef struct source_tag source_t;
#include "cfgfile.h"
#include "yp.h"
#include "util.h"
......@@ -23,8 +25,7 @@
#include <stdio.h>
typedef struct source_tag
{
struct source_tag {
mutex_t lock;
client_t *client;
connection_t *con;
......@@ -81,8 +82,7 @@ typedef struct source_tag
refbuf_t *stream_data_tail;
playlist_t *history;
} source_t;
};
source_t *source_reserve (const char *mount);
void *source_client_thread (void *arg);
......
......@@ -697,6 +697,10 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
out += offset;
len -= offset;
if (client && client->protocol == ICECAST_PROTOCOL_GOPHER) {
return snprintf(out, len, "%s", (datablock) ? datablock : "");
}
if (status == -1)
{
status_buffer[0] = '\0';
......
......@@ -376,7 +376,11 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
}
if (!failed) {
snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);
if (client->protocol == ICECAST_PROTOCOL_GOPHER) {
snprintf(refbuf->data + ret, full_len - ret, "%s", string);
} else {
snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);
}
client->respcode = 200;
client_set_queue (client, NULL);
......
......@@ -21,6 +21,7 @@
#include "common/thread/thread.h"
#include "global.h"
#include "curl.h"
#include "connection.h"
#include "refbuf.h"
......
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0">
<xsl:output omit-xml-declaration="yes" media-type="application/x-gopher-menu"
method="text" indent="no" encoding="UTF-8" />
<xsl:template match = "/icestats">
<xsl:text>iIcecast Server * * *
iVersion: </xsl:text><xsl:value-of select="server_id" /><xsl:text> * * *
</xsl:text>
<xsl:for-each select="source">
<xsl:choose>
<xsl:when test="listeners">
<xsl:text>s</xsl:text>
<xsl:value-of select="server_name" />
<xsl:text> </xsl:text>
<xsl:value-of select="@mount" />
<xsl:text> </xsl:text>
<xsl:value-of select="../host" />
<xsl:text> 8000</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>i</xsl:text>
<xsl:value-of select="@mount" />
<xsl:text> is offline * * *</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>