1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2025-01-03 14:56:34 -05:00

Merge branch 'ph3-resourcematch'

This commit is contained in:
Philipp Schafft 2018-07-09 10:53:12 +00:00
commit cf093b11d7
5 changed files with 285 additions and 34 deletions

View File

@ -7,6 +7,7 @@ bin_PROGRAMS = icecast
noinst_HEADERS = \
icecasttypes.h \
admin.h \
resourcematch.h \
main.h \
cfgfile.h \
logging.h \
@ -69,6 +70,7 @@ icecast_SOURCES = \
xslt.c \
fserve.c \
admin.c \
resourcematch.c \
md5.c \
matchfile.c \
tls.c \

View File

@ -118,37 +118,37 @@ static void command_updatemetadata (client_t *client, source_t *source, adm
static void command_buildm3u (client_t *client, source_t *source, admin_format_t response);
static const admin_command_handler_t handlers[] = {
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL }, /* for ACL framework */
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback },
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback },
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata },
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata },
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata },
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners },
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners },
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload },
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload },
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts },
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients },
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients },
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client },
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client },
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source },
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source },
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth },
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth },
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata },
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata },
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u },
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats }
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL, NULL}, /* for ACL framework */
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback, NULL},
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback, NULL},
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata, NULL},
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata, NULL},
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata, NULL},
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners, NULL},
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners, NULL},
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload, NULL},
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload, NULL},
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts, NULL},
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients, NULL},
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients, NULL},
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client, NULL},
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client, NULL},
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source, NULL},
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source, NULL},
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth, NULL},
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth, NULL},
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata, NULL},
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata, NULL},
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u, NULL},
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL}
};
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
@ -247,7 +247,7 @@ admin_command_id_t admin_get_command(const char *command)
}
for (i = 0; i < table->length; i++)
if (strcmp(table->handlers[i].route, suffix) == 0)
if (resourcematch_match(table->handlers[i].route, suffix, NULL) == RESOURCEMATCH_MATCH)
return admin_get_command_by_table_and_index(table, i);
return COMMAND_ERROR;
@ -480,7 +480,7 @@ void admin_handle_request(client_t *client, const char *uri)
handler = admin_get_handler(client->admin_command);
/* Check if admin command is valid */
if (handler == NULL || handler->function == NULL) {
if (handler == NULL || (handler->function == NULL && handler->function_with_parameters == NULL)) {
ICECAST_LOG_ERROR("Error parsing command string or unrecognised command: %H",
uri);
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
@ -545,7 +545,25 @@ void admin_handle_request(client_t *client, const char *uri)
switch (client->parser->req_type) {
case httpp_req_get:
case httpp_req_post:
handler->function(client, source, format);
if (handler->function) {
handler->function(client, source, format);
} else {
resourcematch_extract_t *extract = NULL;
const char *suffix = strchr(uri, '/');
if (!suffix) {
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
} else {
suffix++;
if (resourcematch_match(handler->route, suffix, &extract) == RESOURCEMATCH_MATCH) {
handler->function_with_parameters(client, source, format, extract);
resourcematch_extract_free(extract);
} else {
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
}
}
}
break;
case httpp_req_options:
client_send_204(client);

View File

@ -19,6 +19,7 @@
#include "icecasttypes.h"
#include "compat.h"
#include "resourcematch.h"
/* types */
#define ADMINTYPE_ERROR (-1)
@ -31,12 +32,14 @@
#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 void (*admin_request_function_with_parameters_ptr)(client_t * client, source_t * source, admin_format_t format, resourcematch_extract_t *parameters);
typedef struct admin_command_handler {
const char *route;
const int type;
const int format;
const admin_request_function_ptr function;
const admin_request_function_with_parameters_ptr function_with_parameters;
} admin_command_handler_t;
void admin_handle_request(client_t *client, const char *uri);

191
src/resourcematch.c Normal file
View File

@ -0,0 +1,191 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include "resourcematch.h"
static size_t count_groups(const char *pattern)
{
size_t ret = 0;
while (*pattern) {
for (; *pattern && *pattern != '%'; pattern++);
if (!*pattern) {
return ret;
}
pattern++;
if (!*pattern)
return ret;
if (*pattern != '%')
ret++;
pattern++;
}
return ret;
}
static resourcematch_extract_t * allocate_extract(const char *pattern)
{
size_t groups = count_groups(pattern);
resourcematch_extract_t *ret;
ret = calloc(1, sizeof(*ret));
if (!ret)
return NULL;
ret->groups = groups;
ret->group = calloc(groups, sizeof(*ret->group));
return ret;
}
static void strip_common_prefix(const char **pattern, const char **string)
{
const char *p = *pattern;
const char *s = *string;
for (; *p && *p != '%' && *p == *s; p++, s++);
*pattern = p;
*string = s;
}
static inline void setup_group(resourcematch_extract_t *extract, size_t idx, char type)
{
if (!extract)
return;
extract->group[idx].type = type;
extract->group[idx].raw = NULL;
}
static inline resourcematch_result_t match_lli(const char **string, resourcematch_extract_t *extract, size_t idx, int base)
{
long long int ret;
char *endptr;
errno = 0;
ret = strtoll(*string, &endptr, base);
if (errno != 0)
return RESOURCEMATCH_ERROR;
if (extract) {
extract->group[idx].result.lli = ret;
}
*string = endptr;
return RESOURCEMATCH_MATCH;
}
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract)
{
resourcematch_result_t ret;
resourcematch_extract_t *matches = NULL;
size_t idx = 0;
if (!pattern || !string)
return RESOURCEMATCH_ERROR;
if (extract) {
matches = allocate_extract(pattern);
if (!matches)
return RESOURCEMATCH_NOMATCH;
}
while (1) {
strip_common_prefix(&pattern, &string);
if (!*pattern && !*string) {
if (extract)
*extract = matches;
return RESOURCEMATCH_MATCH;
} else if (!*pattern || !*string) {
if (extract)
resourcematch_extract_free(matches);
return RESOURCEMATCH_NOMATCH;
}
if (*pattern != '%') {
if (extract)
resourcematch_extract_free(matches);
return RESOURCEMATCH_NOMATCH;
}
pattern++;
switch (*pattern) {
case '%':
if (*string == '%') {
string++;
} else {
if (extract)
resourcematch_extract_free(matches);
return RESOURCEMATCH_NOMATCH;
}
break;
#define _test_int(type,base) \
case (type): \
setup_group(matches, idx, *pattern); \
\
ret = match_lli(&string, matches, idx, (base)); \
if (ret != RESOURCEMATCH_MATCH) { \
if (extract) \
resourcematch_extract_free(matches); \
\
return ret; \
} \
idx++; \
break;
_test_int('i', 0);
_test_int('d', 10);
_test_int('x', 16);
_test_int('o', 8);
default:
if (extract)
resourcematch_extract_free(matches);
return RESOURCEMATCH_ERROR;
break;
}
pattern++;
}
}
void resourcematch_extract_free(resourcematch_extract_t *extract)
{
size_t i;
if (!extract)
return;
for (i = 0; i < extract->groups; i++) {
free(extract->group[i].raw);
}
free(extract->group);
free(extract);
}

37
src/resourcematch.h Normal file
View File

@ -0,0 +1,37 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __RESOURCEMATCH_H__
#define __RESOURCEMATCH_H__
#include <sys/types.h>
typedef enum {
RESOURCEMATCH_ERROR,
RESOURCEMATCH_MATCH,
RESOURCEMATCH_NOMATCH
} resourcematch_result_t;
typedef struct {
char type;
char *raw;
union {
const char *string;
long long int lli;
} result;
} resourcematch_group_t;
typedef struct {
size_t groups;
resourcematch_group_t *group;
} resourcematch_extract_t;
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract);
void resourcematch_extract_free(resourcematch_extract_t *extract);
#endif