mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Cleanup: Removed parts of connection.[ch] into new connection_handle.[ch]
This commit is contained in:
parent
6f16abf1df
commit
843aff3266
@ -13,6 +13,7 @@ noinst_HEADERS = \
|
|||||||
logging.h \
|
logging.h \
|
||||||
sighandler.h \
|
sighandler.h \
|
||||||
connection.h \
|
connection.h \
|
||||||
|
connection_handle.h \
|
||||||
global.h \
|
global.h \
|
||||||
util.h \
|
util.h \
|
||||||
errors.h \
|
errors.h \
|
||||||
@ -68,6 +69,7 @@ icecast_SOURCES = \
|
|||||||
logging.c \
|
logging.c \
|
||||||
sighandler.c \
|
sighandler.c \
|
||||||
connection.c \
|
connection.c \
|
||||||
|
connection_handle.c \
|
||||||
global.c \
|
global.c \
|
||||||
util.c \
|
util.c \
|
||||||
errors.c \
|
errors.c \
|
||||||
|
707
src/connection.c
707
src/connection.c
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
#include "connection_handle.h"
|
||||||
#include "cfgfile.h"
|
#include "cfgfile.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -107,7 +108,6 @@ static matchfile_t *banned_ip, *allowed_ip;
|
|||||||
|
|
||||||
rwlock_t _source_shutdown_rwlock;
|
rwlock_t _source_shutdown_rwlock;
|
||||||
|
|
||||||
static int _update_admin_command(client_t *client);
|
|
||||||
static void _handle_connection(void);
|
static void _handle_connection(void);
|
||||||
static void get_tls_certificate(ice_config_t *config);
|
static void get_tls_certificate(ice_config_t *config);
|
||||||
|
|
||||||
@ -815,295 +815,6 @@ int connection_complete_source(source_t *source, int response)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void source_startup(client_t *client)
|
|
||||||
{
|
|
||||||
source_t *source;
|
|
||||||
source = source_reserve(client->uri);
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
source->client = client;
|
|
||||||
source->parser = client->parser;
|
|
||||||
source->con = client->con;
|
|
||||||
if (connection_complete_source(source, 1) < 0) {
|
|
||||||
source_clear_source(source);
|
|
||||||
source_free_source(source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
client->respcode = 200;
|
|
||||||
if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
|
|
||||||
client->respcode = 200;
|
|
||||||
/* send this non-blocking but if there is only a partial write
|
|
||||||
* then leave to header timeout */
|
|
||||||
client_send_bytes(client, "OK2\r\nicy-caps:11\r\n\r\n", 20); /* TODO: Replace Magic Number! */
|
|
||||||
source->shoutcast_compat = 1;
|
|
||||||
source_client_callback(client, source);
|
|
||||||
} else {
|
|
||||||
refbuf_t *ok = refbuf_new(PER_CLIENT_REFBUF_SIZE);
|
|
||||||
const char *expectcontinue;
|
|
||||||
const char *transfer_encoding;
|
|
||||||
int status_to_send = 0;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
transfer_encoding = httpp_getvar(source->parser, "transfer-encoding");
|
|
||||||
if (transfer_encoding && strcasecmp(transfer_encoding, HTTPP_ENCODING_IDENTITY) != 0) {
|
|
||||||
client->encoding = httpp_encoding_new(transfer_encoding);
|
|
||||||
if (!client->encoding) {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_UNIMPLEMENTED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source->parser && source->parser->req_type == httpp_req_source) {
|
|
||||||
status_to_send = 200;
|
|
||||||
} else {
|
|
||||||
/* For PUT support we check for 100-continue and send back a 100 to stay in spec */
|
|
||||||
expectcontinue = httpp_getvar (source->parser, "expect");
|
|
||||||
|
|
||||||
if (expectcontinue != NULL) {
|
|
||||||
#ifdef HAVE_STRCASESTR
|
|
||||||
if (strcasestr (expectcontinue, "100-continue") != NULL)
|
|
||||||
#else
|
|
||||||
ICECAST_LOG_WARN("OS doesn't support case insensitive substring checks...");
|
|
||||||
if (strstr (expectcontinue, "100-continue") != NULL)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
status_to_send = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client->respcode = 200;
|
|
||||||
if (status_to_send) {
|
|
||||||
ret = util_http_build_header(ok->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status_to_send, NULL, NULL, NULL, NULL, NULL, client);
|
|
||||||
snprintf(ok->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "Content-Length: 0\r\n\r\n");
|
|
||||||
ok->len = strlen(ok->data);
|
|
||||||
} else {
|
|
||||||
ok->len = 0;
|
|
||||||
}
|
|
||||||
refbuf_release(client->refbuf);
|
|
||||||
client->refbuf = ok;
|
|
||||||
fserve_add_client_callback(client, source_client_callback, source);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNT_IN_USE);
|
|
||||||
ICECAST_LOG_WARN("Mountpoint %s in use", client->uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only called for native icecast source clients */
|
|
||||||
static void _handle_source_request(client_t *client)
|
|
||||||
{
|
|
||||||
const char *method = httpp_getvar(client->parser, HTTPP_VAR_REQ_TYPE);
|
|
||||||
|
|
||||||
ICECAST_LOG_INFO("Source logging in at mountpoint \"%s\" using %s%H%s from %s as role %s with acl %s",
|
|
||||||
client->uri,
|
|
||||||
((method) ? "\"" : "<"), ((method) ? method : "unknown"), ((method) ? "\"" : ">"),
|
|
||||||
client->con->ip, client->role, acl_get_name(client->acl));
|
|
||||||
|
|
||||||
if (client->parser && client->parser->req_type == httpp_req_source) {
|
|
||||||
ICECAST_LOG_DEBUG("Source at mountpoint \"%s\" connected using deprecated SOURCE method.", client->uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->uri[0] != '/') {
|
|
||||||
ICECAST_LOG_WARN("source mountpoint not starting with /");
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
source_startup(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _handle_stats_request(client_t *client)
|
|
||||||
{
|
|
||||||
stats_event_inc(NULL, "stats_connections");
|
|
||||||
|
|
||||||
client->respcode = 200;
|
|
||||||
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
|
|
||||||
"HTTP/1.0 200 OK\r\n\r\n");
|
|
||||||
client->refbuf->len = strlen(client->refbuf->data);
|
|
||||||
fserve_add_client_callback(client, stats_callback, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if 0 is returned then the client should not be touched, however if -1
|
|
||||||
* is returned then the caller is responsible for handling the client
|
|
||||||
*/
|
|
||||||
static void __add_listener_to_source(source_t *source, client_t *client)
|
|
||||||
{
|
|
||||||
size_t loop = 10;
|
|
||||||
|
|
||||||
do {
|
|
||||||
ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
|
|
||||||
source->max_listeners, source->listeners);
|
|
||||||
if (source->max_listeners == -1)
|
|
||||||
break;
|
|
||||||
if (source->listeners < (unsigned long)source->max_listeners)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (loop && source->fallback_when_full && source->fallback_mount) {
|
|
||||||
source_t *next = source_find_mount (source->fallback_mount);
|
|
||||||
if (!next) {
|
|
||||||
ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found",
|
|
||||||
source->mount, source->fallback_mount);
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_SOURCE_MAX_LISTENERS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ICECAST_LOG_INFO("stream full, trying %s", next->mount);
|
|
||||||
source = next;
|
|
||||||
navigation_history_navigate_to(&(client->history), source->identifier, NAVIGATION_DIRECTION_DOWN);
|
|
||||||
loop--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* now we fail the client */
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_SOURCE_MAX_LISTENERS);
|
|
||||||
return;
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
client->write_to_client = format_generic_write_to_client;
|
|
||||||
client->check_buffer = format_check_http_buffer;
|
|
||||||
client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
|
|
||||||
memset(client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
|
|
||||||
|
|
||||||
/* lets add the client to the active list */
|
|
||||||
avl_tree_wlock(source->pending_tree);
|
|
||||||
avl_insert(source->pending_tree, client);
|
|
||||||
avl_tree_unlock(source->pending_tree);
|
|
||||||
|
|
||||||
if (source->running == 0 && source->on_demand) {
|
|
||||||
/* enable on-demand relay to start, wake up the slave thread */
|
|
||||||
ICECAST_LOG_DEBUG("kicking off on-demand relay");
|
|
||||||
source->on_demand_req = 1;
|
|
||||||
}
|
|
||||||
ICECAST_LOG_DEBUG("Added client to %s", source->mount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* count the number of clients on a mount with same username and same role as the given one */
|
|
||||||
static inline ssize_t __count_user_role_on_mount (source_t *source, client_t *client) {
|
|
||||||
ssize_t ret = 0;
|
|
||||||
avl_node *node;
|
|
||||||
|
|
||||||
avl_tree_rlock(source->client_tree);
|
|
||||||
node = avl_get_first(source->client_tree);
|
|
||||||
while (node) {
|
|
||||||
client_t *existing_client = (client_t *)node->key;
|
|
||||||
if (existing_client->username && client->username &&
|
|
||||||
strcmp(existing_client->username, client->username) == 0 &&
|
|
||||||
existing_client->role && client->role &&
|
|
||||||
strcmp(existing_client->role, client->role) == 0) {
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
node = avl_get_next(node);
|
|
||||||
}
|
|
||||||
avl_tree_unlock(source->client_tree);
|
|
||||||
|
|
||||||
avl_tree_rlock(source->pending_tree);
|
|
||||||
node = avl_get_first(source->pending_tree);
|
|
||||||
while (node) {
|
|
||||||
client_t *existing_client = (client_t *)node->key;
|
|
||||||
if (existing_client->username && client->username &&
|
|
||||||
strcmp(existing_client->username, client->username) == 0 &&
|
|
||||||
existing_client->role && client->role &&
|
|
||||||
strcmp(existing_client->role, client->role) == 0){
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
node = avl_get_next(node);
|
|
||||||
}
|
|
||||||
avl_tree_unlock(source->pending_tree);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_get_request(client_t *client) {
|
|
||||||
source_t *source = NULL;
|
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("Got client %p with URI %H", client, client->uri);
|
|
||||||
|
|
||||||
/* there are several types of HTTP GET clients
|
|
||||||
* media clients, which are looking for a source (eg, URI = /stream.ogg),
|
|
||||||
* stats clients, which are looking for /admin/stats.xml and
|
|
||||||
* fserve clients, which are looking for static files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
stats_event_inc(NULL, "client_connections");
|
|
||||||
|
|
||||||
/* 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 :( */
|
|
||||||
auth_reject_client_on_deny(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->parser->req_type == httpp_req_options) {
|
|
||||||
client_send_204(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (util_check_valid_extension(client->uri) == XSLT_CONTENT) {
|
|
||||||
/* If the file exists, then transform it, otherwise, write a 404 */
|
|
||||||
ICECAST_LOG_DEBUG("Stats request, sending XSL transformed stats");
|
|
||||||
stats_transform_xslt(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
avl_tree_rlock(global.source_tree);
|
|
||||||
/* let's see if this is a source or just a random fserve file */
|
|
||||||
source = source_find_mount_with_history(client->uri, &(client->history));
|
|
||||||
if (source) {
|
|
||||||
/* true mount */
|
|
||||||
do {
|
|
||||||
ssize_t max_connections_per_user = acl_get_max_connections_per_user(client->acl);
|
|
||||||
/* check for duplicate_logins */
|
|
||||||
if (max_connections_per_user > 0) { /* -1 = not set (-> default=unlimited), 0 = unlimited */
|
|
||||||
if (max_connections_per_user <= __count_user_role_on_mount(source, client)) {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!source->allow_direct_access) {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set max listening duration in case not already set. */
|
|
||||||
if (client->con->discon_time == 0) {
|
|
||||||
time_t connection_duration = acl_get_max_connection_duration(client->acl);
|
|
||||||
if (connection_duration == -1) {
|
|
||||||
ice_config_t *config = config_get_config();
|
|
||||||
mount_proxy *mount = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
|
|
||||||
if (mount && mount->max_listener_duration)
|
|
||||||
connection_duration = mount->max_listener_duration;
|
|
||||||
config_release_config();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection_duration > 0) /* -1 = not set (-> default=unlimited), 0 = unlimited */
|
|
||||||
client->con->discon_time = connection_duration + time(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
__add_listener_to_source(source, client);
|
|
||||||
} while (0);
|
|
||||||
avl_tree_unlock(global.source_tree);
|
|
||||||
} else {
|
|
||||||
/* file */
|
|
||||||
avl_tree_unlock(global.source_tree);
|
|
||||||
fserve_client_create(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_delete_request(client_t *client) {
|
|
||||||
source_t *source;
|
|
||||||
|
|
||||||
avl_tree_wlock(global.source_tree);
|
|
||||||
source = source_find_mount_raw(client->uri);
|
|
||||||
if (source) {
|
|
||||||
source->running = 0;
|
|
||||||
avl_tree_unlock(global.source_tree);
|
|
||||||
client_send_204(client);
|
|
||||||
} else {
|
|
||||||
avl_tree_unlock(global.source_tree);
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_UNKNOWN_REQUEST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_shoutcast_compatible(client_queue_t *node)
|
static void _handle_shoutcast_compatible(client_queue_t *node)
|
||||||
{
|
{
|
||||||
char *http_compliant;
|
char *http_compliant;
|
||||||
@ -1190,353 +901,6 @@ static void _handle_shoutcast_compatible(client_queue_t *node)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle <resource> lookups here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _handle_resources(client_t *client, char **uri)
|
|
||||||
{
|
|
||||||
const char *http_host = httpp_getvar(client->parser, "host");
|
|
||||||
char *serverhost = NULL;
|
|
||||||
int serverport = 0;
|
|
||||||
char *vhost = NULL;
|
|
||||||
char *vhost_colon;
|
|
||||||
char *new_uri = NULL;
|
|
||||||
ice_config_t *config;
|
|
||||||
const listener_t *listen_sock;
|
|
||||||
resource_t *resource;
|
|
||||||
|
|
||||||
if (http_host) {
|
|
||||||
vhost = strdup(http_host);
|
|
||||||
if (vhost) {
|
|
||||||
vhost_colon = strstr(vhost, ":");
|
|
||||||
if (vhost_colon)
|
|
||||||
*vhost_colon = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config = config_get_config();
|
|
||||||
listen_sock = listensocket_get_listener(client->con->listensocket_effective);
|
|
||||||
if (listen_sock) {
|
|
||||||
serverhost = listen_sock->bind_address;
|
|
||||||
serverport = listen_sock->port;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
if (resource->listen_socket != NULL && (listen_sock->id == NULL || strcmp(resource->listen_socket, listen_sock->id) != 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;
|
|
||||||
|
|
||||||
if (resource->module) {
|
|
||||||
module_t *module = module_container_get_module(global.modulecontainer, resource->module);
|
|
||||||
|
|
||||||
if (module != NULL) {
|
|
||||||
refobject_unref(client->handler_module);
|
|
||||||
client->handler_module = module;
|
|
||||||
} else {
|
|
||||||
ICECAST_LOG_ERROR("Module used in alias not found: %s", resource->module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource->handler) {
|
|
||||||
char *func = strdup(resource->handler);
|
|
||||||
if (func) {
|
|
||||||
free(client->handler_function);
|
|
||||||
client->handler_function = func;
|
|
||||||
} else {
|
|
||||||
ICECAST_LOG_ERROR("Can not allocate memory.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("resource has made %s into %s", *uri, new_uri);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
listensocket_release_listener(client->con->listensocket_effective);
|
|
||||||
config_release_config();
|
|
||||||
|
|
||||||
if (new_uri) {
|
|
||||||
free(*uri);
|
|
||||||
*uri = new_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vhost)
|
|
||||||
free(vhost);
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
admin_handle_request(client, adminuri);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle any client that passed the authing process.
|
|
||||||
*/
|
|
||||||
static void _handle_authed_client(client_t *client, void *userdata, auth_result result)
|
|
||||||
{
|
|
||||||
auth_stack_release(client->authstack);
|
|
||||||
client->authstack = NULL;
|
|
||||||
|
|
||||||
/* Update admin parameters just in case auth changed our URI */
|
|
||||||
if (_update_admin_command(client) == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fastevent_emit(FASTEVENT_TYPE_CLIENT_AUTHED, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
|
|
||||||
|
|
||||||
if (result != AUTH_OK) {
|
|
||||||
auth_reject_client_on_fail(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acl_test_method(client->acl, client->parser->req_type) != ACL_POLICY_ALLOW) {
|
|
||||||
ICECAST_LOG_ERROR("Client (role=%s, acl=%s, username=%s) not allowed to use this request method on %H", client->role, acl_get_name(client->acl), client->username, client->uri);
|
|
||||||
auth_reject_client_on_deny(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dispatch legacy admin.cgi requests */
|
|
||||||
if (strcmp(client->uri, "/admin.cgi") == 0) {
|
|
||||||
_handle_admin_request(client, client->uri + 1);
|
|
||||||
return;
|
|
||||||
} /* Dispatch all admin requests */
|
|
||||||
else if (strncmp(client->uri, "/admin/", 7) == 0) {
|
|
||||||
_handle_admin_request(client, client->uri + 7);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->handler_module && client->handler_function) {
|
|
||||||
const module_client_handler_t *handler = module_get_client_handler(client->handler_module, client->handler_function);
|
|
||||||
if (handler) {
|
|
||||||
handler->cb(client->handler_module, client);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
ICECAST_LOG_ERROR("No such handler function in module: %s", client->handler_function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (client->parser->req_type) {
|
|
||||||
case httpp_req_source:
|
|
||||||
case httpp_req_put:
|
|
||||||
_handle_source_request(client);
|
|
||||||
break;
|
|
||||||
case httpp_req_stats:
|
|
||||||
_handle_stats_request(client);
|
|
||||||
break;
|
|
||||||
case httpp_req_get:
|
|
||||||
case httpp_req_post:
|
|
||||||
case httpp_req_options:
|
|
||||||
_handle_get_request(client);
|
|
||||||
break;
|
|
||||||
case httpp_req_delete:
|
|
||||||
_handle_delete_request(client);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ICECAST_LOG_ERROR("Wrong request type from client");
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_UNKNOWN_REQUEST);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle clients that still need to authenticate.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void _handle_authentication_global(client_t *client, void *userdata, auth_result result)
|
|
||||||
{
|
|
||||||
ice_config_t *config;
|
|
||||||
auth_stack_t *authstack;
|
|
||||||
|
|
||||||
auth_stack_release(client->authstack);
|
|
||||||
client->authstack = NULL;
|
|
||||||
|
|
||||||
if (result != AUTH_NOMATCH &&
|
|
||||||
/* Allow global admins access to all mount points */
|
|
||||||
!(result == AUTH_OK && client->admin_command != ADMIN_COMMAND_ERROR && acl_test_admin(client->acl, client->admin_command) == ACL_POLICY_DENY)) {
|
|
||||||
_handle_authed_client(client, userdata, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("Trying global authenticators for client %p.", client);
|
|
||||||
config = config_get_config();
|
|
||||||
authstack = config->authstack;
|
|
||||||
auth_stack_addref(authstack);
|
|
||||||
config_release_config();
|
|
||||||
auth_stack_add_client(authstack, client, _handle_authed_client, userdata);
|
|
||||||
auth_stack_release(authstack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline mount_proxy * __find_non_admin_mount(ice_config_t *config, const char *name, mount_type type)
|
|
||||||
{
|
|
||||||
if (strcmp(name, "/admin.cgi") == 0 || strncmp(name, "/admin/", 7) == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return config_find_mount(config, name, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_authentication_mount_generic(client_t *client, void *userdata, mount_type type, void (*callback)(client_t*, void*, auth_result))
|
|
||||||
{
|
|
||||||
ice_config_t *config;
|
|
||||||
mount_proxy *mountproxy;
|
|
||||||
auth_stack_t *stack = NULL;
|
|
||||||
|
|
||||||
config = config_get_config();
|
|
||||||
mountproxy = __find_non_admin_mount(config, client->uri, type);
|
|
||||||
if (!mountproxy) {
|
|
||||||
int command_type = admin_get_command_type(client->admin_command);
|
|
||||||
if (command_type == ADMINTYPE_MOUNT || command_type == ADMINTYPE_HYBRID) {
|
|
||||||
const char *mount = httpp_get_param(client->parser, "mount");
|
|
||||||
if (mount)
|
|
||||||
mountproxy = __find_non_admin_mount(config, mount, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mountproxy && mountproxy->mounttype == type)
|
|
||||||
stack = mountproxy->authstack;
|
|
||||||
auth_stack_addref(stack);
|
|
||||||
config_release_config();
|
|
||||||
|
|
||||||
if (stack) {
|
|
||||||
auth_stack_add_client(stack, client, callback, userdata);
|
|
||||||
auth_stack_release(stack);
|
|
||||||
} else {
|
|
||||||
callback(client, userdata, AUTH_NOMATCH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_authentication_mount_default(client_t *client, void *userdata, auth_result result)
|
|
||||||
{
|
|
||||||
auth_stack_release(client->authstack);
|
|
||||||
client->authstack = NULL;
|
|
||||||
|
|
||||||
if (result != AUTH_NOMATCH &&
|
|
||||||
/* Allow global admins access to all mount points */
|
|
||||||
!(result == AUTH_OK && client->admin_command != ADMIN_COMMAND_ERROR && acl_test_admin(client->acl, client->admin_command) == ACL_POLICY_DENY)) {
|
|
||||||
_handle_authed_client(client, userdata, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("Trying <mount type=\"default\"> specific authenticators for client %p.", client);
|
|
||||||
_handle_authentication_mount_generic(client, userdata, MOUNT_TYPE_DEFAULT, _handle_authentication_global);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_authentication_mount_normal(client_t *client, void *userdata, auth_result result)
|
|
||||||
{
|
|
||||||
auth_stack_release(client->authstack);
|
|
||||||
client->authstack = NULL;
|
|
||||||
|
|
||||||
if (result != AUTH_NOMATCH) {
|
|
||||||
_handle_authed_client(client, userdata, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("Trying <mount type=\"normal\"> specific authenticators for client %p.", client);
|
|
||||||
_handle_authentication_mount_generic(client, userdata, MOUNT_TYPE_NORMAL, _handle_authentication_mount_default);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_authentication_listen_socket(client_t *client)
|
|
||||||
{
|
|
||||||
auth_stack_t *stack = NULL;
|
|
||||||
const listener_t *listener;
|
|
||||||
|
|
||||||
listener = listensocket_get_listener(client->con->listensocket_effective);
|
|
||||||
if (listener) {
|
|
||||||
if (listener->authstack) {
|
|
||||||
auth_stack_addref(stack = listener->authstack);
|
|
||||||
}
|
|
||||||
listensocket_release_listener(client->con->listensocket_effective);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack) {
|
|
||||||
auth_stack_add_client(stack, client, _handle_authentication_mount_normal, NULL);
|
|
||||||
auth_stack_release(stack);
|
|
||||||
} else {
|
|
||||||
_handle_authentication_mount_normal(client, NULL, AUTH_NOMATCH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handle_authentication(client_t *client)
|
|
||||||
{
|
|
||||||
fastevent_emit(FASTEVENT_TYPE_CLIENT_READY_FOR_AUTH, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
|
|
||||||
_handle_authentication_listen_socket(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __prepare_shoutcast_admin_cgi_request(client_t *client)
|
|
||||||
{
|
|
||||||
ice_config_t *config;
|
|
||||||
const char *sc_mount;
|
|
||||||
const char *pass = httpp_get_query_param(client->parser, "pass");
|
|
||||||
const listener_t *listener;
|
|
||||||
|
|
||||||
if (pass == NULL) {
|
|
||||||
ICECAST_LOG_ERROR("missing pass parameter");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->password) {
|
|
||||||
ICECAST_LOG_INFO("Client already has password set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Why do we acquire a global lock here? -- ph3-der-loewe, 2018-05-11 */
|
|
||||||
global_lock();
|
|
||||||
config = config_get_config();
|
|
||||||
sc_mount = config->shoutcast_mount;
|
|
||||||
|
|
||||||
listener = listensocket_get_listener(client->con->listensocket_effective);
|
|
||||||
if (listener && listener->shoutcast_mount)
|
|
||||||
sc_mount = listener->shoutcast_mount;
|
|
||||||
|
|
||||||
httpp_set_query_param(client->parser, "mount", sc_mount);
|
|
||||||
listensocket_release_listener(client->con->listensocket_effective);
|
|
||||||
|
|
||||||
httpp_setvar(client->parser, HTTPP_VAR_PROTOCOL, "ICY");
|
|
||||||
client->password = strdup(pass);
|
|
||||||
config_release_config();
|
|
||||||
global_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we need body of client */
|
/* Check if we need body of client */
|
||||||
static int _need_body(client_queue_t *node)
|
static int _need_body(client_queue_t *node)
|
||||||
{
|
{
|
||||||
@ -1562,23 +926,6 @@ static int _need_body(client_queue_t *node)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updates client's admin_command */
|
|
||||||
static int _update_admin_command(client_t *client)
|
|
||||||
{
|
|
||||||
if (strcmp(client->uri, "/admin.cgi") == 0) {
|
|
||||||
client->admin_command = admin_get_command(client->uri + 1);
|
|
||||||
__prepare_shoutcast_admin_cgi_request(client);
|
|
||||||
if (!client->password) {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_MISSING_PASS_PARAMETER);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (strncmp(client->uri, "/admin/", 7) == 0) {
|
|
||||||
client->admin_command = admin_get_command(client->uri + 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connection thread. Here we take clients off the connection queue and check
|
/* Connection thread. Here we take clients off the connection queue and check
|
||||||
* the contents provided. We set up the parser then hand off to the specific
|
* the contents provided. We set up the parser then hand off to the specific
|
||||||
* request handler.
|
* request handler.
|
||||||
@ -1612,9 +959,6 @@ static void _handle_connection(void)
|
|||||||
client->parser = parser;
|
client->parser = parser;
|
||||||
}
|
}
|
||||||
if (already_parsed || httpp_parse (parser, client->refbuf->data, node->offset)) {
|
if (already_parsed || httpp_parse (parser, client->refbuf->data, node->offset)) {
|
||||||
char *uri;
|
|
||||||
const char *upgrade, *connection;
|
|
||||||
|
|
||||||
client->refbuf->len = 0;
|
client->refbuf->len = 0;
|
||||||
|
|
||||||
/* early check if we need more data */
|
/* early check if we need more data */
|
||||||
@ -1653,54 +997,7 @@ static void _handle_connection(void)
|
|||||||
free (node->shoutcast_mount);
|
free (node->shoutcast_mount);
|
||||||
free (node);
|
free (node);
|
||||||
|
|
||||||
if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
|
connection_handle_client(client);
|
||||||
strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
|
|
||||||
ICECAST_LOG_ERROR("Bad HTTP protocol detected");
|
|
||||||
client_destroy (client);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
upgrade = httpp_getvar(parser, "upgrade");
|
|
||||||
connection = httpp_getvar(parser, "connection");
|
|
||||||
if (upgrade && connection && strcasecmp(connection, "upgrade") == 0) {
|
|
||||||
if (client->con->tlsmode == ICECAST_TLSMODE_DISABLED || client->con->tls || strstr(upgrade, "TLS/1.0") == NULL) {
|
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_CON_UPGRADE_ERROR);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
client_send_101(client, ICECAST_REUSE_UPGRADETLS);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (client->con->tlsmode != ICECAST_TLSMODE_DISABLED && client->con->tlsmode != ICECAST_TLSMODE_AUTO && !client->con->tls) {
|
|
||||||
client_send_426(client, ICECAST_REUSE_UPGRADETLS);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser->req_type == httpp_req_options && strcmp(rawuri, "*") == 0) {
|
|
||||||
client->uri = strdup("*");
|
|
||||||
client_send_204(client);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = util_normalise_uri(rawuri);
|
|
||||||
|
|
||||||
if (!uri) {
|
|
||||||
client_destroy (client);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->mode = config_str_to_omode(NULL, NULL, httpp_get_param(client->parser, "omode"));
|
|
||||||
|
|
||||||
if (_handle_resources(client, &uri) != 0) {
|
|
||||||
client_destroy (client);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->uri = uri;
|
|
||||||
|
|
||||||
if (_update_admin_command(client) == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_handle_authentication(client);
|
|
||||||
} else {
|
} else {
|
||||||
free (node);
|
free (node);
|
||||||
ICECAST_LOG_ERROR("HTTP request parsing failed");
|
ICECAST_LOG_ERROR("HTTP request parsing failed");
|
||||||
|
747
src/connection_handle.c
Normal file
747
src/connection_handle.c
Normal file
@ -0,0 +1,747 @@
|
|||||||
|
/* 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 <jack@xiph.org,
|
||||||
|
* Michael Smith <msmith@xiph.org>,
|
||||||
|
* oddsock <oddsock@xiph.org>,
|
||||||
|
* Karl Heyes <karl@xiph.org>
|
||||||
|
* and others (see AUTHORS for details).
|
||||||
|
* Copyright 2011, Dave 'justdave' Miller <justdave@mozilla.com>,
|
||||||
|
* Copyright 2011-2022, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "connection_handle.h"
|
||||||
|
#include "auth.h"
|
||||||
|
#include "acl.h"
|
||||||
|
#include "admin.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "fastevent.h"
|
||||||
|
#include "listensocket.h"
|
||||||
|
#include "source.h"
|
||||||
|
#include "errors.h"
|
||||||
|
#include "stats.h"
|
||||||
|
#include "fserve.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#define CATMODULE "connection-handle"
|
||||||
|
|
||||||
|
/* Handle <resource> lookups here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool _handle_resources(client_t *client, char **uri)
|
||||||
|
{
|
||||||
|
const char *http_host = httpp_getvar(client->parser, "host");
|
||||||
|
char *serverhost = NULL;
|
||||||
|
int serverport = 0;
|
||||||
|
char *vhost = NULL;
|
||||||
|
char *vhost_colon;
|
||||||
|
char *new_uri = NULL;
|
||||||
|
ice_config_t *config;
|
||||||
|
const listener_t *listen_sock;
|
||||||
|
resource_t *resource;
|
||||||
|
|
||||||
|
if (http_host) {
|
||||||
|
vhost = strdup(http_host);
|
||||||
|
if (vhost) {
|
||||||
|
vhost_colon = strstr(vhost, ":");
|
||||||
|
if (vhost_colon)
|
||||||
|
*vhost_colon = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config = config_get_config();
|
||||||
|
listen_sock = listensocket_get_listener(client->con->listensocket_effective);
|
||||||
|
if (listen_sock) {
|
||||||
|
serverhost = listen_sock->bind_address;
|
||||||
|
serverport = listen_sock->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
if (resource->listen_socket != NULL && (listen_sock->id == NULL || strcmp(resource->listen_socket, listen_sock->id) != 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;
|
||||||
|
|
||||||
|
if (resource->module) {
|
||||||
|
module_t *module = module_container_get_module(global.modulecontainer, resource->module);
|
||||||
|
|
||||||
|
if (module != NULL) {
|
||||||
|
refobject_unref(client->handler_module);
|
||||||
|
client->handler_module = module;
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Module used in alias not found: %s", resource->module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource->handler) {
|
||||||
|
char *func = strdup(resource->handler);
|
||||||
|
if (func) {
|
||||||
|
free(client->handler_function);
|
||||||
|
client->handler_function = func;
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Can not allocate memory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ICECAST_LOG_DEBUG("resource has made %s into %s", *uri, new_uri);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
listensocket_release_listener(client->con->listensocket_effective);
|
||||||
|
config_release_config();
|
||||||
|
|
||||||
|
if (new_uri) {
|
||||||
|
free(*uri);
|
||||||
|
*uri = new_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vhost)
|
||||||
|
free(vhost);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void __prepare_shoutcast_admin_cgi_request(client_t *client)
|
||||||
|
{
|
||||||
|
ice_config_t *config;
|
||||||
|
const char *sc_mount;
|
||||||
|
const char *pass = httpp_get_query_param(client->parser, "pass");
|
||||||
|
const listener_t *listener;
|
||||||
|
|
||||||
|
if (pass == NULL) {
|
||||||
|
ICECAST_LOG_ERROR("missing pass parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->password) {
|
||||||
|
ICECAST_LOG_INFO("Client already has password set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Why do we acquire a global lock here? -- ph3-der-loewe, 2018-05-11 */
|
||||||
|
global_lock();
|
||||||
|
config = config_get_config();
|
||||||
|
sc_mount = config->shoutcast_mount;
|
||||||
|
|
||||||
|
listener = listensocket_get_listener(client->con->listensocket_effective);
|
||||||
|
if (listener && listener->shoutcast_mount)
|
||||||
|
sc_mount = listener->shoutcast_mount;
|
||||||
|
|
||||||
|
httpp_set_query_param(client->parser, "mount", sc_mount);
|
||||||
|
listensocket_release_listener(client->con->listensocket_effective);
|
||||||
|
|
||||||
|
httpp_setvar(client->parser, HTTPP_VAR_PROTOCOL, "ICY");
|
||||||
|
client->password = strdup(pass);
|
||||||
|
config_release_config();
|
||||||
|
global_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Updates client's admin_command */
|
||||||
|
static bool _update_admin_command(client_t *client)
|
||||||
|
{
|
||||||
|
if (strcmp(client->uri, "/admin.cgi") == 0) {
|
||||||
|
client->admin_command = admin_get_command(client->uri + 1);
|
||||||
|
__prepare_shoutcast_admin_cgi_request(client);
|
||||||
|
if (!client->password) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_MISSING_PASS_PARAMETER);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (strncmp(client->uri, "/admin/", 7) == 0) {
|
||||||
|
client->admin_command = admin_get_command(client->uri + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void source_startup(client_t *client)
|
||||||
|
{
|
||||||
|
source_t *source;
|
||||||
|
source = source_reserve(client->uri);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
source->client = client;
|
||||||
|
source->parser = client->parser;
|
||||||
|
source->con = client->con;
|
||||||
|
if (connection_complete_source(source, 1) < 0) {
|
||||||
|
source_clear_source(source);
|
||||||
|
source_free_source(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client->respcode = 200;
|
||||||
|
if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
|
||||||
|
client->respcode = 200;
|
||||||
|
/* send this non-blocking but if there is only a partial write
|
||||||
|
* then leave to header timeout */
|
||||||
|
client_send_bytes(client, "OK2\r\nicy-caps:11\r\n\r\n", 20); /* TODO: Replace Magic Number! */
|
||||||
|
source->shoutcast_compat = 1;
|
||||||
|
source_client_callback(client, source);
|
||||||
|
} else {
|
||||||
|
refbuf_t *ok = refbuf_new(PER_CLIENT_REFBUF_SIZE);
|
||||||
|
const char *expectcontinue;
|
||||||
|
const char *transfer_encoding;
|
||||||
|
int status_to_send = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
transfer_encoding = httpp_getvar(source->parser, "transfer-encoding");
|
||||||
|
if (transfer_encoding && strcasecmp(transfer_encoding, HTTPP_ENCODING_IDENTITY) != 0) {
|
||||||
|
client->encoding = httpp_encoding_new(transfer_encoding);
|
||||||
|
if (!client->encoding) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_UNIMPLEMENTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source->parser && source->parser->req_type == httpp_req_source) {
|
||||||
|
status_to_send = 200;
|
||||||
|
} else {
|
||||||
|
/* For PUT support we check for 100-continue and send back a 100 to stay in spec */
|
||||||
|
expectcontinue = httpp_getvar (source->parser, "expect");
|
||||||
|
|
||||||
|
if (expectcontinue != NULL) {
|
||||||
|
#ifdef HAVE_STRCASESTR
|
||||||
|
if (strcasestr (expectcontinue, "100-continue") != NULL)
|
||||||
|
#else
|
||||||
|
ICECAST_LOG_WARN("OS doesn't support case insensitive substring checks...");
|
||||||
|
if (strstr (expectcontinue, "100-continue") != NULL)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
status_to_send = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client->respcode = 200;
|
||||||
|
if (status_to_send) {
|
||||||
|
ret = util_http_build_header(ok->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status_to_send, NULL, NULL, NULL, NULL, NULL, client);
|
||||||
|
snprintf(ok->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "Content-Length: 0\r\n\r\n");
|
||||||
|
ok->len = strlen(ok->data);
|
||||||
|
} else {
|
||||||
|
ok->len = 0;
|
||||||
|
}
|
||||||
|
refbuf_release(client->refbuf);
|
||||||
|
client->refbuf = ok;
|
||||||
|
fserve_add_client_callback(client, source_client_callback, source);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNT_IN_USE);
|
||||||
|
ICECAST_LOG_WARN("Mountpoint %s in use", client->uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* only called for native icecast source clients */
|
||||||
|
static void _handle_source_request(client_t *client)
|
||||||
|
{
|
||||||
|
const char *method = httpp_getvar(client->parser, HTTPP_VAR_REQ_TYPE);
|
||||||
|
|
||||||
|
ICECAST_LOG_INFO("Source logging in at mountpoint \"%s\" using %s%H%s from %s as role %s with acl %s",
|
||||||
|
client->uri,
|
||||||
|
((method) ? "\"" : "<"), ((method) ? method : "unknown"), ((method) ? "\"" : ">"),
|
||||||
|
client->con->ip, client->role, acl_get_name(client->acl));
|
||||||
|
|
||||||
|
if (client->parser && client->parser->req_type == httpp_req_source) {
|
||||||
|
ICECAST_LOG_DEBUG("Source at mountpoint \"%s\" connected using deprecated SOURCE method.", client->uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->uri[0] != '/') {
|
||||||
|
ICECAST_LOG_WARN("source mountpoint not starting with /");
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
source_startup(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _handle_stats_request(client_t *client)
|
||||||
|
{
|
||||||
|
stats_event_inc(NULL, "stats_connections");
|
||||||
|
|
||||||
|
client->respcode = 200;
|
||||||
|
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
|
||||||
|
"HTTP/1.0 200 OK\r\n\r\n");
|
||||||
|
client->refbuf->len = strlen(client->refbuf->data);
|
||||||
|
fserve_add_client_callback(client, stats_callback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if 0 is returned then the client should not be touched, however if -1
|
||||||
|
* is returned then the caller is responsible for handling the client
|
||||||
|
*/
|
||||||
|
static void __add_listener_to_source(source_t *source, client_t *client)
|
||||||
|
{
|
||||||
|
size_t loop = 10;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
|
||||||
|
source->max_listeners, source->listeners);
|
||||||
|
if (source->max_listeners == -1)
|
||||||
|
break;
|
||||||
|
if (source->listeners < (unsigned long)source->max_listeners)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (loop && source->fallback_when_full && source->fallback_mount) {
|
||||||
|
source_t *next = source_find_mount (source->fallback_mount);
|
||||||
|
if (!next) {
|
||||||
|
ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found",
|
||||||
|
source->mount, source->fallback_mount);
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_SOURCE_MAX_LISTENERS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ICECAST_LOG_INFO("stream full, trying %s", next->mount);
|
||||||
|
source = next;
|
||||||
|
navigation_history_navigate_to(&(client->history), source->identifier, NAVIGATION_DIRECTION_DOWN);
|
||||||
|
loop--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* now we fail the client */
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_SOURCE_MAX_LISTENERS);
|
||||||
|
return;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
client->write_to_client = format_generic_write_to_client;
|
||||||
|
client->check_buffer = format_check_http_buffer;
|
||||||
|
client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
|
||||||
|
memset(client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
|
||||||
|
|
||||||
|
/* lets add the client to the active list */
|
||||||
|
avl_tree_wlock(source->pending_tree);
|
||||||
|
avl_insert(source->pending_tree, client);
|
||||||
|
avl_tree_unlock(source->pending_tree);
|
||||||
|
|
||||||
|
if (source->running == 0 && source->on_demand) {
|
||||||
|
/* enable on-demand relay to start, wake up the slave thread */
|
||||||
|
ICECAST_LOG_DEBUG("kicking off on-demand relay");
|
||||||
|
source->on_demand_req = 1;
|
||||||
|
}
|
||||||
|
ICECAST_LOG_DEBUG("Added client to %s", source->mount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* count the number of clients on a mount with same username and same role as the given one */
|
||||||
|
static inline ssize_t __count_user_role_on_mount (source_t *source, client_t *client) {
|
||||||
|
ssize_t ret = 0;
|
||||||
|
avl_node *node;
|
||||||
|
|
||||||
|
avl_tree_rlock(source->client_tree);
|
||||||
|
node = avl_get_first(source->client_tree);
|
||||||
|
while (node) {
|
||||||
|
client_t *existing_client = (client_t *)node->key;
|
||||||
|
if (existing_client->username && client->username &&
|
||||||
|
strcmp(existing_client->username, client->username) == 0 &&
|
||||||
|
existing_client->role && client->role &&
|
||||||
|
strcmp(existing_client->role, client->role) == 0) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
node = avl_get_next(node);
|
||||||
|
}
|
||||||
|
avl_tree_unlock(source->client_tree);
|
||||||
|
|
||||||
|
avl_tree_rlock(source->pending_tree);
|
||||||
|
node = avl_get_first(source->pending_tree);
|
||||||
|
while (node) {
|
||||||
|
client_t *existing_client = (client_t *)node->key;
|
||||||
|
if (existing_client->username && client->username &&
|
||||||
|
strcmp(existing_client->username, client->username) == 0 &&
|
||||||
|
existing_client->role && client->role &&
|
||||||
|
strcmp(existing_client->role, client->role) == 0){
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
node = avl_get_next(node);
|
||||||
|
}
|
||||||
|
avl_tree_unlock(source->pending_tree);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_get_request(client_t *client) {
|
||||||
|
source_t *source = NULL;
|
||||||
|
|
||||||
|
ICECAST_LOG_DEBUG("Got client %p with URI %H", client, client->uri);
|
||||||
|
|
||||||
|
/* there are several types of HTTP GET clients
|
||||||
|
* media clients, which are looking for a source (eg, URI = /stream.ogg),
|
||||||
|
* stats clients, which are looking for /admin/stats.xml and
|
||||||
|
* fserve clients, which are looking for static files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
stats_event_inc(NULL, "client_connections");
|
||||||
|
|
||||||
|
/* 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 :( */
|
||||||
|
auth_reject_client_on_deny(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->parser->req_type == httpp_req_options) {
|
||||||
|
client_send_204(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (util_check_valid_extension(client->uri) == XSLT_CONTENT) {
|
||||||
|
/* If the file exists, then transform it, otherwise, write a 404 */
|
||||||
|
ICECAST_LOG_DEBUG("Stats request, sending XSL transformed stats");
|
||||||
|
stats_transform_xslt(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avl_tree_rlock(global.source_tree);
|
||||||
|
/* let's see if this is a source or just a random fserve file */
|
||||||
|
source = source_find_mount_with_history(client->uri, &(client->history));
|
||||||
|
if (source) {
|
||||||
|
/* true mount */
|
||||||
|
do {
|
||||||
|
ssize_t max_connections_per_user = acl_get_max_connections_per_user(client->acl);
|
||||||
|
/* check for duplicate_logins */
|
||||||
|
if (max_connections_per_user > 0) { /* -1 = not set (-> default=unlimited), 0 = unlimited */
|
||||||
|
if (max_connections_per_user <= __count_user_role_on_mount(source, client)) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!source->allow_direct_access) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set max listening duration in case not already set. */
|
||||||
|
if (client->con->discon_time == 0) {
|
||||||
|
time_t connection_duration = acl_get_max_connection_duration(client->acl);
|
||||||
|
if (connection_duration == -1) {
|
||||||
|
ice_config_t *config = config_get_config();
|
||||||
|
mount_proxy *mount = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
|
||||||
|
if (mount && mount->max_listener_duration)
|
||||||
|
connection_duration = mount->max_listener_duration;
|
||||||
|
config_release_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection_duration > 0) /* -1 = not set (-> default=unlimited), 0 = unlimited */
|
||||||
|
client->con->discon_time = connection_duration + time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__add_listener_to_source(source, client);
|
||||||
|
} while (0);
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
} else {
|
||||||
|
/* file */
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
fserve_client_create(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_delete_request(client_t *client) {
|
||||||
|
source_t *source;
|
||||||
|
|
||||||
|
avl_tree_wlock(global.source_tree);
|
||||||
|
source = source_find_mount_raw(client->uri);
|
||||||
|
if (source) {
|
||||||
|
source->running = 0;
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
client_send_204(client);
|
||||||
|
} else {
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_UNKNOWN_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
admin_handle_request(client, adminuri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle any client that passed the authing process.
|
||||||
|
*/
|
||||||
|
static void _handle_authed_client(client_t *client, void *userdata, auth_result result)
|
||||||
|
{
|
||||||
|
auth_stack_release(client->authstack);
|
||||||
|
client->authstack = NULL;
|
||||||
|
|
||||||
|
/* Update admin parameters just in case auth changed our URI */
|
||||||
|
if (!_update_admin_command(client))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fastevent_emit(FASTEVENT_TYPE_CLIENT_AUTHED, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
|
||||||
|
|
||||||
|
if (result != AUTH_OK) {
|
||||||
|
auth_reject_client_on_fail(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acl_test_method(client->acl, client->parser->req_type) != ACL_POLICY_ALLOW) {
|
||||||
|
ICECAST_LOG_ERROR("Client (role=%s, acl=%s, username=%s) not allowed to use this request method on %H", client->role, acl_get_name(client->acl), client->username, client->uri);
|
||||||
|
auth_reject_client_on_deny(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dispatch legacy admin.cgi requests */
|
||||||
|
if (strcmp(client->uri, "/admin.cgi") == 0) {
|
||||||
|
_handle_admin_request(client, client->uri + 1);
|
||||||
|
return;
|
||||||
|
} /* Dispatch all admin requests */
|
||||||
|
else if (strncmp(client->uri, "/admin/", 7) == 0) {
|
||||||
|
_handle_admin_request(client, client->uri + 7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->handler_module && client->handler_function) {
|
||||||
|
const module_client_handler_t *handler = module_get_client_handler(client->handler_module, client->handler_function);
|
||||||
|
if (handler) {
|
||||||
|
handler->cb(client->handler_module, client);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("No such handler function in module: %s", client->handler_function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (client->parser->req_type) {
|
||||||
|
case httpp_req_source:
|
||||||
|
case httpp_req_put:
|
||||||
|
_handle_source_request(client);
|
||||||
|
break;
|
||||||
|
case httpp_req_stats:
|
||||||
|
_handle_stats_request(client);
|
||||||
|
break;
|
||||||
|
case httpp_req_get:
|
||||||
|
case httpp_req_post:
|
||||||
|
case httpp_req_options:
|
||||||
|
_handle_get_request(client);
|
||||||
|
break;
|
||||||
|
case httpp_req_delete:
|
||||||
|
_handle_delete_request(client);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ICECAST_LOG_ERROR("Wrong request type from client");
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_UNKNOWN_REQUEST);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle clients that still need to authenticate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void _handle_authentication_global(client_t *client, void *userdata, auth_result result)
|
||||||
|
{
|
||||||
|
ice_config_t *config;
|
||||||
|
auth_stack_t *authstack;
|
||||||
|
|
||||||
|
auth_stack_release(client->authstack);
|
||||||
|
client->authstack = NULL;
|
||||||
|
|
||||||
|
if (result != AUTH_NOMATCH &&
|
||||||
|
/* Allow global admins access to all mount points */
|
||||||
|
!(result == AUTH_OK && client->admin_command != ADMIN_COMMAND_ERROR && acl_test_admin(client->acl, client->admin_command) == ACL_POLICY_DENY)) {
|
||||||
|
_handle_authed_client(client, userdata, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICECAST_LOG_DEBUG("Trying global authenticators for client %p.", client);
|
||||||
|
config = config_get_config();
|
||||||
|
authstack = config->authstack;
|
||||||
|
auth_stack_addref(authstack);
|
||||||
|
config_release_config();
|
||||||
|
auth_stack_add_client(authstack, client, _handle_authed_client, userdata);
|
||||||
|
auth_stack_release(authstack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline mount_proxy * __find_non_admin_mount(ice_config_t *config, const char *name, mount_type type)
|
||||||
|
{
|
||||||
|
if (strcmp(name, "/admin.cgi") == 0 || strncmp(name, "/admin/", 7) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return config_find_mount(config, name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_authentication_mount_generic(client_t *client, void *userdata, mount_type type, void (*callback)(client_t*, void*, auth_result))
|
||||||
|
{
|
||||||
|
ice_config_t *config;
|
||||||
|
mount_proxy *mountproxy;
|
||||||
|
auth_stack_t *stack = NULL;
|
||||||
|
|
||||||
|
config = config_get_config();
|
||||||
|
mountproxy = __find_non_admin_mount(config, client->uri, type);
|
||||||
|
if (!mountproxy) {
|
||||||
|
int command_type = admin_get_command_type(client->admin_command);
|
||||||
|
if (command_type == ADMINTYPE_MOUNT || command_type == ADMINTYPE_HYBRID) {
|
||||||
|
const char *mount = httpp_get_param(client->parser, "mount");
|
||||||
|
if (mount)
|
||||||
|
mountproxy = __find_non_admin_mount(config, mount, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mountproxy && mountproxy->mounttype == type)
|
||||||
|
stack = mountproxy->authstack;
|
||||||
|
auth_stack_addref(stack);
|
||||||
|
config_release_config();
|
||||||
|
|
||||||
|
if (stack) {
|
||||||
|
auth_stack_add_client(stack, client, callback, userdata);
|
||||||
|
auth_stack_release(stack);
|
||||||
|
} else {
|
||||||
|
callback(client, userdata, AUTH_NOMATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_authentication_mount_default(client_t *client, void *userdata, auth_result result)
|
||||||
|
{
|
||||||
|
auth_stack_release(client->authstack);
|
||||||
|
client->authstack = NULL;
|
||||||
|
|
||||||
|
if (result != AUTH_NOMATCH &&
|
||||||
|
/* Allow global admins access to all mount points */
|
||||||
|
!(result == AUTH_OK && client->admin_command != ADMIN_COMMAND_ERROR && acl_test_admin(client->acl, client->admin_command) == ACL_POLICY_DENY)) {
|
||||||
|
_handle_authed_client(client, userdata, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICECAST_LOG_DEBUG("Trying <mount type=\"default\"> specific authenticators for client %p.", client);
|
||||||
|
_handle_authentication_mount_generic(client, userdata, MOUNT_TYPE_DEFAULT, _handle_authentication_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_authentication_mount_normal(client_t *client, void *userdata, auth_result result)
|
||||||
|
{
|
||||||
|
auth_stack_release(client->authstack);
|
||||||
|
client->authstack = NULL;
|
||||||
|
|
||||||
|
if (result != AUTH_NOMATCH) {
|
||||||
|
_handle_authed_client(client, userdata, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICECAST_LOG_DEBUG("Trying <mount type=\"normal\"> specific authenticators for client %p.", client);
|
||||||
|
_handle_authentication_mount_generic(client, userdata, MOUNT_TYPE_NORMAL, _handle_authentication_mount_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_authentication_listen_socket(client_t *client)
|
||||||
|
{
|
||||||
|
auth_stack_t *stack = NULL;
|
||||||
|
const listener_t *listener;
|
||||||
|
|
||||||
|
listener = listensocket_get_listener(client->con->listensocket_effective);
|
||||||
|
if (listener) {
|
||||||
|
if (listener->authstack) {
|
||||||
|
auth_stack_addref(stack = listener->authstack);
|
||||||
|
}
|
||||||
|
listensocket_release_listener(client->con->listensocket_effective);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack) {
|
||||||
|
auth_stack_add_client(stack, client, _handle_authentication_mount_normal, NULL);
|
||||||
|
auth_stack_release(stack);
|
||||||
|
} else {
|
||||||
|
_handle_authentication_mount_normal(client, NULL, AUTH_NOMATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_authentication(client_t *client)
|
||||||
|
{
|
||||||
|
fastevent_emit(FASTEVENT_TYPE_CLIENT_READY_FOR_AUTH, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
|
||||||
|
_handle_authentication_listen_socket(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_handle_client(client_t *client)
|
||||||
|
{
|
||||||
|
http_parser_t *parser = client->parser;
|
||||||
|
const char *rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
|
||||||
|
const char *upgrade, *connection;
|
||||||
|
char *uri;
|
||||||
|
|
||||||
|
if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
|
||||||
|
strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
|
||||||
|
ICECAST_LOG_ERROR("Bad HTTP protocol detected");
|
||||||
|
client_destroy(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upgrade = httpp_getvar(parser, "upgrade");
|
||||||
|
connection = httpp_getvar(parser, "connection");
|
||||||
|
if (upgrade && connection && strcasecmp(connection, "upgrade") == 0) {
|
||||||
|
if (client->con->tlsmode == ICECAST_TLSMODE_DISABLED || client->con->tls || strstr(upgrade, "TLS/1.0") == NULL) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_CON_UPGRADE_ERROR);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
client_send_101(client, ICECAST_REUSE_UPGRADETLS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (client->con->tlsmode != ICECAST_TLSMODE_DISABLED && client->con->tlsmode != ICECAST_TLSMODE_AUTO && !client->con->tls) {
|
||||||
|
client_send_426(client, ICECAST_REUSE_UPGRADETLS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->req_type == httpp_req_options && strcmp(rawuri, "*") == 0) {
|
||||||
|
client->uri = strdup("*");
|
||||||
|
client_send_204(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = util_normalise_uri(rawuri);
|
||||||
|
|
||||||
|
if (!uri) {
|
||||||
|
client_destroy(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->mode = config_str_to_omode(NULL, NULL, httpp_get_param(client->parser, "omode"));
|
||||||
|
|
||||||
|
if (!_handle_resources(client, &uri)) {
|
||||||
|
client_destroy(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->uri = uri;
|
||||||
|
|
||||||
|
if (!_update_admin_command(client))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_handle_authentication(client);
|
||||||
|
}
|
16
src/connection_handle.h
Normal file
16
src/connection_handle.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2022-2022, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CONNECTION_HANDLE_H__
|
||||||
|
#define __CONNECTION_HANDLE_H__
|
||||||
|
|
||||||
|
#include "icecasttypes.h"
|
||||||
|
|
||||||
|
void connection_handle_client(client_t *client);
|
||||||
|
|
||||||
|
#endif /* __CONNECTION_HANDLE_H__ */
|
Loading…
Reference in New Issue
Block a user