1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-23 06:25:24 +00:00
icecast-server/src/connection.c

1272 lines
38 KiB
C
Raw Permalink Normal View History

/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
2015-01-10 18:53:44 +00:00
* 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).
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
* Copyright 2011, Dave 'justdave' Miller <justdave@mozilla.com>,
2022-03-01 11:17:28 +00:00
* 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 <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_POLL
#include <poll.h>
#endif
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#endif
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/net/sock.h"
#include "common/httpp/httpp.h"
#include "compat.h"
#include "connection.h"
#include "connection_handle.h"
#include "cfgfile.h"
#include "global.h"
#include "util.h"
#include "refobject.h"
#include "refbuf.h"
#include "client.h"
#include "errors.h"
#include "stats.h"
#include "logging.h"
#include "fserve.h"
#include "slave.h"
#include "source.h"
#include "admin.h"
#include "auth.h"
#include "matchfile.h"
#include "tls.h"
#include "acl.h"
#include "refobject.h"
#include "listensocket.h"
#include "fastevent.h"
#include "navigation.h"
#define CATMODULE "connection"
/* Two different major types of source authentication.
Shoutcast style is used only by the Shoutcast DSP
and is a crazy version of HTTP. It looks like :
Source Client -> Connects to port + 1
Source Client -> sends encoder password (plaintext)\r\n
Icecast -> reads encoder password, if ok, sends OK2\r\n, else disconnects
Source Client -> reads OK2\r\n, then sends http-type request headers
that contain the stream details (icy-name, etc..)
Icecast -> reads headers, stores them
Source Client -> starts sending MP3 data
Source Client -> periodically updates metadata via admin.cgi call
Icecast auth style uses HTTP and Basic Authorization.
*/
typedef struct client_queue_tag {
client_t *client;
int offset;
int shoutcast;
char *shoutcast_mount;
2018-06-18 21:40:47 +00:00
char *bodybuffer;
size_t bodybufferlen;
int tried_body;
bool ready;
struct client_queue_tag *next;
} client_queue_entry_t;
2022-03-19 19:05:53 +00:00
typedef struct {
client_queue_entry_t *head;
client_queue_entry_t **tail;
mutex_t mutex;
cond_t cond;
thread_type *thread;
bool running;
#ifdef HAVE_POLL
struct pollfd *pollfds;
size_t pollfds_len;
#endif
2022-03-19 19:05:53 +00:00
} client_queue_t;
#define QUEUE_READY_TIMEOUT 50
2022-03-19 19:05:53 +00:00
static spin_t _connection_lock; // protects _current_id
static volatile connection_id_t _current_id = 0;
static int _initialized = 0;
2022-03-19 19:05:53 +00:00
static client_queue_t _request_queue;
static client_queue_t _connection_queue;
static client_queue_t _body_queue;
static client_queue_t _handle_queue;
static bool tls_ok = false;
static tls_ctx_t *tls_ctx;
/* filtering client connection based on IP */
static matchfile_t *banned_ip, *allowed_ip;
rwlock_t _source_shutdown_rwlock;
static void get_tls_certificate(ice_config_t *config);
static void free_client_node(client_queue_entry_t *node);
static void * _handle_connection(client_queue_t *queue);
static void * process_request_queue (client_queue_t *queue);
static void * process_request_body_queue (client_queue_t *queue);
static void * handle_client_worker(client_queue_t *queue);
2022-03-19 19:05:53 +00:00
static void client_queue_init(client_queue_t *queue)
{
memset(queue, 0, sizeof(*queue));
queue->tail = &(queue->head);
thread_mutex_create(&(queue->mutex));
thread_cond_create(&(queue->cond));
2022-03-19 19:05:53 +00:00
}
static void client_queue_destroy(client_queue_t *queue)
{
if (queue->thread) {
queue->running = false;
thread_cond_broadcast(&(queue->cond));
thread_join(queue->thread);
}
thread_cond_destroy(&(queue->cond));
2022-03-19 19:05:53 +00:00
thread_mutex_destroy(&(queue->mutex));
#ifdef HAVE_POLL
free(queue->pollfds);
#endif
2022-03-19 19:05:53 +00:00
}
2022-03-20 13:17:16 +00:00
static void client_queue_start_thread(client_queue_t *queue, const char *name, void *(*func)(client_queue_t *))
{
if (queue->thread)
return;
queue->running = true;
2022-03-20 13:17:16 +00:00
queue->thread = thread_create(name, (void*(*)(void*))func, queue, THREAD_ATTACHED);
}
static inline bool client_queue_running(client_queue_t *queue)
{
return queue->running;
}
2022-03-19 19:05:53 +00:00
static void client_queue_add(client_queue_t *queue, client_queue_entry_t *entry)
{
thread_mutex_lock(&(queue->mutex));
*(queue->tail) = entry;
queue->tail = &(entry->next);
thread_mutex_unlock(&(queue->mutex));
thread_cond_broadcast(&(queue->cond));
}
static void client_queue_wait(client_queue_t *queue)
{
thread_cond_wait(&(queue->cond));
2022-03-19 19:05:53 +00:00
}
static client_queue_entry_t * client_queue_shift(client_queue_t *queue, client_queue_entry_t *stop)
{
client_queue_entry_t *ret;
thread_mutex_lock(&(queue->mutex));
ret = queue->head;
if (ret) {
if (ret == stop) {
ret = NULL;
} else {
queue->head = ret->next;
if (!queue->head) {
queue->tail = &(queue->head);
}
ret->next = NULL;
}
}
thread_mutex_unlock(&(queue->mutex));
return ret;
}
static bool client_queue_check_ready(client_queue_t *queue, int timeout, time_t connection_timeout)
{
if (!queue->head)
return false;
#ifdef HAVE_POLL
if (true) {
bool had_timeout = false;
size_t count = 0;
size_t i;
client_queue_entry_t *cur;
thread_mutex_lock(&(queue->mutex));
for (cur = queue->head; cur; cur = cur->next) {
count++;
if (cur->client->con->con_time <= connection_timeout) {
cur->ready = true;
had_timeout = true;
} else {
cur->ready = false;
}
}
if (queue->pollfds_len < count) {
free(queue->pollfds);
queue->pollfds = calloc(count, sizeof(*queue->pollfds));
if (queue->pollfds) {
queue->pollfds_len = count;
} else {
ICECAST_LOG_ERROR("Allocation of queue->pollfds failed. BAD.");
queue->pollfds_len = 0;
thread_mutex_unlock(&(queue->mutex));
return false;
}
} else {
memset(queue->pollfds, 0, sizeof(*queue->pollfds)*count);
}
for (cur = queue->head, i = 0; cur && i < count; cur = cur->next, i++) {
queue->pollfds[i].fd = cur->client->con->sock;
queue->pollfds[i].events = POLLIN;
}
thread_mutex_unlock(&(queue->mutex));
if (had_timeout)
return true;
if (poll(queue->pollfds, count, timeout) < 1)
return false;
thread_mutex_lock(&(queue->mutex));
for (cur = queue->head; cur; cur = cur->next) {
for (i = 0; i < count; i++) {
if (queue->pollfds[i].fd == cur->client->con->sock) {
if (queue->pollfds[i].revents) {
cur->ready = true;
}
}
}
}
thread_mutex_unlock(&(queue->mutex));
}
#endif
return true;
}
static bool client_queue_check_ready_wait(client_queue_t *queue, int timeout, time_t connection_timeout)
{
while (queue->running) {
if (client_queue_check_ready(queue, timeout, connection_timeout))
return true;
if (!queue->head)
thread_cond_wait(&(queue->cond));
}
return false;
}
static client_queue_entry_t * client_queue_shift_ready(client_queue_t *queue, client_queue_entry_t *stop)
{
#ifdef HAVE_POLL
client_queue_entry_t *cur;
client_queue_entry_t *last = NULL;
if (!queue->head)
return NULL;
thread_mutex_lock(&(queue->mutex));
for (cur = queue->head; cur && cur != stop; cur = cur->next) {
if (cur->ready) {
// use this one.
if (last == NULL) {
/* we are the head */
queue->head = cur->next;
if (!queue->head) {
queue->tail = &(queue->head);
}
} else {
last->next = cur->next;
if (queue->tail == &(cur->next)) {
queue->tail = &(last->next);
}
}
cur->next = NULL;
thread_mutex_unlock(&(queue->mutex));
return cur;
}
last = cur;
}
thread_mutex_unlock(&(queue->mutex));
return NULL;
#else
/* just return any */
return client_queue_shift(queue, stop);
#endif
}
void connection_initialize(void)
{
if (_initialized)
return;
2015-01-10 18:53:44 +00:00
thread_spin_create (&_connection_lock);
thread_mutex_create(&move_clients_mutex);
thread_rwlock_create(&_source_shutdown_rwlock);
2022-03-19 19:05:53 +00:00
client_queue_init(&_request_queue);
client_queue_init(&_connection_queue);
client_queue_init(&_body_queue);
client_queue_init(&_handle_queue);
2022-03-20 16:10:58 +00:00
client_queue_start_thread(&_request_queue, "Request Queue", process_request_queue);
client_queue_start_thread(&_connection_queue, "Con Queue", _handle_connection);
2022-03-20 16:10:58 +00:00
client_queue_start_thread(&_body_queue, "Body Queue", process_request_body_queue);
client_queue_start_thread(&_handle_queue, "Client Handler", handle_client_worker);
_initialized = 1;
}
void connection_shutdown(void)
{
if (!_initialized)
return;
2015-01-10 18:53:44 +00:00
tls_ctx_unref(tls_ctx);
matchfile_release(banned_ip);
matchfile_release(allowed_ip);
thread_rwlock_destroy(&_source_shutdown_rwlock);
thread_spin_destroy (&_connection_lock);
thread_mutex_destroy(&move_clients_mutex);
2022-03-19 19:05:53 +00:00
client_queue_destroy(&_request_queue);
client_queue_destroy(&_connection_queue);
client_queue_destroy(&_body_queue);
client_queue_destroy(&_handle_queue);
_initialized = 0;
}
void connection_reread_config(ice_config_t *config)
{
get_tls_certificate(config);
listensocket_container_configure_and_setup(global.listensockets, config);
}
static connection_id_t _next_connection_id(void)
{
connection_id_t id;
thread_spin_lock(&_connection_lock);
id = _current_id++;
thread_spin_unlock(&_connection_lock);
return id;
}
#ifdef ICECAST_CAP_TLS
static void get_tls_certificate(ice_config_t *config)
{
const char *keyfile;
tls_ok = false;
keyfile = config->tls_context.key_file;
if (!keyfile)
keyfile = config->tls_context.cert_file;
tls_ctx_unref(tls_ctx);
tls_ctx = tls_ctx_new(config->tls_context.cert_file, keyfile, config->tls_context.cipher_list);
if (!tls_ctx) {
ICECAST_LOG_INFO("No TLS capability on any configured ports");
return;
}
tls_ok = true;
}
/* handlers for reading and writing a connection_t when there is TLS
* configured on the listening port
*/
static int connection_read_tls(connection_t *con, void *buf, size_t len)
{
ssize_t bytes = tls_read(con->tls, buf, len);
if (bytes <= 0) {
if (tls_want_io(con->tls) > 0)
return -1;
con->error = 1;
}
return bytes;
}
static int connection_send_tls(connection_t *con, const void *buf, size_t len)
{
ssize_t bytes = tls_write(con->tls, buf, len);
if (bytes < 0) {
con->error = 1;
} else {
con->sent_bytes += bytes;
}
return bytes;
}
#else
/* TLS not compiled in, so at least log it */
static void get_tls_certificate(ice_config_t *config)
{
tls_ok = false;
ICECAST_LOG_INFO("No TLS capability. "
2016-11-06 10:49:47 +00:00
"Rebuild Icecast with OpenSSL support to enable this.");
}
#endif /* ICECAST_CAP_TLS */
/* handlers (default) for reading and writing a connection_t, no encrpytion
* used just straight access to the socket
*/
static int connection_read(connection_t *con, void *buf, size_t len)
{
int bytes = sock_read_bytes(con->sock, buf, len);
if (bytes == 0)
con->error = 1;
if (bytes == -1 && !sock_recoverable(sock_error()))
con->error = 1;
return bytes;
}
static int connection_send(connection_t *con, const void *buf, size_t len)
{
int bytes = sock_write_bytes(con->sock, buf, len);
if (bytes < 0) {
if (!sock_recoverable(sock_error()))
con->error = 1;
} else {
con->sent_bytes += bytes;
}
return bytes;
}
connection_t *connection_create(sock_t sock, listensocket_t *listensocket_real, listensocket_t* listensocket_effective, char *ip)
{
connection_t *con;
if (!matchfile_match_allow_deny(allowed_ip, banned_ip, ip))
return NULL;
con = (connection_t *)calloc(1, sizeof(connection_t));
if (con) {
refobject_ref(listensocket_real);
refobject_ref(listensocket_effective);
con->sock = sock;
con->listensocket_real = listensocket_real;
con->listensocket_effective = listensocket_effective;
con->con_time = time(NULL);
con->id = _next_connection_id();
con->ip = ip;
con->tlsmode = ICECAST_TLSMODE_AUTO;
con->read = connection_read;
con->send = connection_send;
}
fastevent_emit(FASTEVENT_TYPE_CONNECTION_CREATE, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CONNECTION, con);
return con;
}
/* prepare connection for interacting over a TLS connection
*/
void connection_uses_tls(connection_t *con)
{
#ifdef ICECAST_CAP_TLS
if (con->tls)
return;
if (con->readbufferlen) {
ICECAST_LOG_ERROR("Connection is now using TLS but has data put back. BAD. Discarding putback data.");
free(con->readbuffer);
con->readbufferlen = 0;
}
con->tlsmode = ICECAST_TLSMODE_RFC2818;
con->read = connection_read_tls;
con->send = connection_send_tls;
con->tls = tls_new(tls_ctx);
tls_set_incoming(con->tls);
tls_set_socket(con->tls, con->sock);
#endif
}
ssize_t connection_send_bytes(connection_t *con, const void *buf, size_t len)
{
ssize_t ret = con->send(con, buf, len);
fastevent_emit(FASTEVENT_TYPE_CONNECTION_WRITE, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_OBRD, con, buf, len, ret);
return ret;
}
static inline ssize_t connection_read_bytes_real(connection_t *con, void *buf, size_t len)
{
ssize_t done = 0;
ssize_t ret;
if (con->readbufferlen) {
ICECAST_LOG_DEBUG("On connection %p we read from putback buffer, filled with %zu bytes, requested are %zu bytes", con, con->readbufferlen, len);
if (len >= con->readbufferlen) {
memcpy(buf, con->readbuffer, con->readbufferlen);
free(con->readbuffer);
con->readbuffer = NULL;
ICECAST_LOG_DEBUG("New fill in buffer=<empty>");
if (len == con->readbufferlen) {
con->readbufferlen = 0;
return len;
} else {
len -= con->readbufferlen;
buf += con->readbufferlen;
done = con->readbufferlen;
con->readbufferlen = 0;
}
} else {
memcpy(buf, con->readbuffer, len);
memmove(con->readbuffer, con->readbuffer+len, con->readbufferlen-len);
con->readbufferlen -= len;
return len;
}
}
ret = con->read(con, buf, len);
if (ret < 0) {
if (done == 0) {
return ret;
} else {
return done;
}
}
return done + ret;
}
ssize_t connection_read_bytes(connection_t *con, void *buf, size_t len)
{
ssize_t ret = connection_read_bytes_real(con, buf, len);
fastevent_emit(FASTEVENT_TYPE_CONNECTION_READ, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_OBRD, con, buf, len, ret);
return ret;
}
int connection_read_put_back(connection_t *con, const void *buf, size_t len)
{
void *n;
fastevent_emit(FASTEVENT_TYPE_CONNECTION_PUTBACK, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_OBR, con, buf, len);
if (con->readbufferlen) {
n = realloc(con->readbuffer, con->readbufferlen + len);
if (!n)
return -1;
memcpy(n + con->readbufferlen, buf, len);
con->readbuffer = n;
con->readbufferlen += len;
ICECAST_LOG_DEBUG("On connection %p %zu bytes have been put back.", con, len);
return 0;
} else {
n = malloc(len);
if (!n)
return -1;
memcpy(n, buf, len);
con->readbuffer = n;
con->readbufferlen = len;
ICECAST_LOG_DEBUG("On connection %p %zu bytes have been put back.", con, len);
return 0;
}
}
/* run along queue checking for any data that has come in or a timeout */
static bool process_request_queue_one (client_queue_entry_t *node, time_t timeout)
{
2022-03-19 19:05:53 +00:00
client_t *client = node->client;
int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
char *buf = client->refbuf->data + node->offset;
2022-03-19 19:05:53 +00:00
ICECAST_LOG_DDEBUG("Checking on client %p", client);
2019-09-17 17:32:00 +00:00
2022-03-19 19:05:53 +00:00
if (client->con->tlsmode == ICECAST_TLSMODE_AUTO || client->con->tlsmode == ICECAST_TLSMODE_AUTO_NO_PLAIN) {
char peak;
if (recv(client->con->sock, &peak, 1, MSG_PEEK) == 1) {
if (peak == 0x16) { /* TLS Record Protocol Content type 0x16 == Handshake */
connection_uses_tls(client->con);
}
}
2022-03-19 19:05:53 +00:00
}
2022-03-19 19:05:53 +00:00
if (len > 0) {
if (client->con->con_time <= timeout) {
2022-03-19 19:05:53 +00:00
len = 0;
} else {
len = client_read_bytes(client, buf, len);
}
2022-03-19 19:05:53 +00:00
}
2022-03-19 19:05:53 +00:00
if (len > 0 || node->shoutcast > 1) {
ssize_t stream_offset = -1;
int pass_it = 1;
char *ptr;
if (len < 0 && node->shoutcast > 1)
len = 0;
/* handle \n, \r\n and nsvcap which for some strange reason has
* EOL as \r\r\n */
node->offset += len;
client->refbuf->data[node->offset] = '\000';
do {
if (node->shoutcast == 1) {
/* password line */
if (strstr (client->refbuf->data, "\r\r\n") != NULL)
break;
2022-03-19 19:05:53 +00:00
if (strstr (client->refbuf->data, "\r\n") != NULL)
break;
2022-03-19 19:05:53 +00:00
if (strstr (client->refbuf->data, "\n") != NULL)
break;
2022-03-19 19:05:53 +00:00
}
/* stream_offset refers to the start of any data sent after the
* http style headers, we don't want to lose those */
ptr = strstr(client->refbuf->data, "\r\r\n\r\r\n");
if (ptr) {
stream_offset = (ptr+6) - client->refbuf->data;
break;
}
ptr = strstr(client->refbuf->data, "\r\n\r\n");
if (ptr) {
stream_offset = (ptr+4) - client->refbuf->data;
break;
}
ptr = strstr(client->refbuf->data, "\n\n");
if (ptr) {
stream_offset = (ptr+2) - client->refbuf->data;
break;
}
pass_it = 0;
} while (0);
2022-03-19 19:05:53 +00:00
ICECAST_LOG_DDEBUG("pass_it=%i, len=%i", pass_it, (int)len);
ICECAST_LOG_DDEBUG("Client %p has buffer: %H", client, client->refbuf->data);
2019-09-17 17:32:00 +00:00
2022-03-19 19:05:53 +00:00
if (pass_it) {
if (stream_offset != -1) {
connection_read_put_back(client->con, client->refbuf->data + stream_offset, node->offset - stream_offset);
node->offset = stream_offset;
}
2022-03-19 19:05:53 +00:00
client_queue_add(&_connection_queue, node);
return true;
}
} else {
if (len == 0 || client->con->error) {
client_destroy(client);
free_client_node(node);
2022-03-19 19:05:53 +00:00
return true;
}
}
2022-03-19 19:05:53 +00:00
return false;
}
static void * process_request_queue (client_queue_t *queue)
{
while (client_queue_running(queue)) {
client_queue_entry_t *stop = NULL;
client_queue_entry_t *node;
ice_config_t *config;
time_t timeout;
config = config_get_config();
timeout = time(NULL) - config->header_timeout;
config_release_config();
2022-03-19 19:05:53 +00:00
client_queue_check_ready_wait(queue, QUEUE_READY_TIMEOUT, timeout);
while ((node = client_queue_shift_ready(queue, stop))) {
if (!process_request_queue_one(node, timeout)) {
client_queue_add(queue, node);
if (!stop)
stop = node;
}
2022-03-19 19:05:53 +00:00
}
}
return NULL;
}
static client_slurp_result_t process_request_body_queue_one(client_queue_entry_t *node, time_t timeout, size_t body_size_limit)
{
client_t *client = node->client;
client_slurp_result_t res;
if (client->parser->req_type == httpp_req_post) {
if (node->bodybuffer == NULL && client->request_body_read == 0) {
if (client->request_body_length < 0) {
node->bodybufferlen = body_size_limit;
node->bodybuffer = malloc(node->bodybufferlen);
} else if (client->request_body_length <= (ssize_t)body_size_limit) {
node->bodybufferlen = client->request_body_length;
node->bodybuffer = malloc(node->bodybufferlen);
}
}
}
if (node->bodybuffer) {
res = client_body_slurp(client, node->bodybuffer, &(node->bodybufferlen));
if (res == CLIENT_SLURP_SUCCESS) {
httpp_parse_postdata(client->parser, node->bodybuffer, node->bodybufferlen);
free(node->bodybuffer);
node->bodybuffer = NULL;
}
} else {
res = client_body_skip(client);
}
if (res != CLIENT_SLURP_SUCCESS) {
if (client->con->con_time <= timeout || client->request_body_read >= body_size_limit) {
return CLIENT_SLURP_ERROR;
}
}
return res;
}
/* This queue reads data from the body of clients. */
static void * process_request_body_queue (client_queue_t *queue)
{
while (client_queue_running(queue)) {
client_queue_entry_t *stop = NULL;
client_queue_entry_t *node;
ice_config_t *config;
time_t timeout;
size_t body_size_limit;
ICECAST_LOG_DDEBUG("Processing body queue.");
config = config_get_config();
timeout = time(NULL) - config->body_timeout;
body_size_limit = config->body_size_limit;
config_release_config();
client_queue_check_ready_wait(queue, QUEUE_READY_TIMEOUT, timeout);
while ((node = client_queue_shift(queue, stop))) {
client_t *client = node->client;
client_slurp_result_t res;
node->tried_body = 1;
ICECAST_LOG_DEBUG("Got client %p in body queue.", client);
2018-06-18 21:40:47 +00:00
res = process_request_body_queue_one(node, timeout, body_size_limit);
if (res == CLIENT_SLURP_NEEDS_MORE_DATA) {
client_queue_add(queue, node);
if (!stop)
stop = node;
} else {
ICECAST_LOG_DEBUG("Putting client %p back in connection queue.", client);
client_queue_add(&_connection_queue, node);
continue;
}
}
}
return NULL;
}
static client_queue_entry_t *create_client_node(client_t *client)
{
client_queue_entry_t *node = calloc (1, sizeof (client_queue_entry_t));
const listener_t *listener;
if (!node)
return NULL;
node->client = client;
listener = listensocket_get_listener(client->con->listensocket_effective);
if (listener) {
if (listener->shoutcast_compat)
node->shoutcast = 1;
2016-11-06 16:02:11 +00:00
client->con->tlsmode = listener->tls;
if (listener->tls == ICECAST_TLSMODE_RFC2818 && tls_ok)
connection_uses_tls(client->con);
if (listener->shoutcast_mount)
node->shoutcast_mount = strdup(listener->shoutcast_mount);
}
listensocket_release_listener(client->con->listensocket_effective);
return node;
}
static void free_client_node(client_queue_entry_t *node)
{
free(node->shoutcast_mount);
free(node->bodybuffer);
free(node);
}
void connection_queue(connection_t *con)
{
client_queue_entry_t *node;
client_t *client = NULL;
global_lock();
if (client_create(&client, con, NULL) < 0) {
global_unlock();
client_send_error_by_id(client, ICECAST_ERROR_GEN_CLIENT_LIMIT);
/* don't be too eager as this is an imposed hard limit */
thread_sleep(400000);
return;
}
/* setup client for reading incoming http */
client->refbuf->data[PER_CLIENT_REFBUF_SIZE-1] = '\000';
if (sock_set_blocking(client->con->sock, 0) || sock_set_nodelay(client->con->sock)) {
global_unlock();
ICECAST_LOG_WARN("Failed to set tcp options on client connection, dropping");
client_destroy(client);
return;
}
node = create_client_node(client);
global_unlock();
if (node == NULL) {
client_destroy(client);
return;
}
2022-03-19 19:05:53 +00:00
client_queue_add(&_request_queue, node);
stats_event_inc(NULL, "connections");
}
void connection_accept_loop(void)
{
ice_config_t *config;
config = config_get_config();
get_tls_certificate(config);
config_release_config();
while (global.running == ICECAST_RUNNING) {
connection_t *con = listensocket_container_accept(global.listensockets, 800);
if (con) {
connection_queue(con);
}
}
/* Give all the other threads notification to shut down */
/* wait for all the sources to shutdown */
thread_rwlock_wlock(&_source_shutdown_rwlock);
thread_rwlock_unlock(&_source_shutdown_rwlock);
}
/* Called when activating a source. Verifies that the source count is not
* exceeded and applies any initial parameters.
*/
int connection_complete_source(source_t *source, int response)
{
ice_config_t *config;
global_lock();
ICECAST_LOG_DEBUG("sources count is %d", global.sources);
config = config_get_config();
if (global.sources < config->source_limit) {
const char *contenttype;
mount_proxy *mountinfo;
format_type_t format_type;
/* setup format handler */
contenttype = httpp_getvar (source->parser, "content-type");
if (contenttype != NULL) {
format_type = format_get_type(contenttype);
if (format_type == FORMAT_ERROR) {
config_release_config();
global_unlock();
if (response) {
client_send_error_by_id(source->client, ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS);
source->client = NULL;
}
ICECAST_LOG_WARN("Content-type \"%s\" not supported, dropping source", contenttype);
return -1;
}
} else if (source->parser->req_type == httpp_req_put) {
config_release_config();
global_unlock();
if (response) {
client_send_error_by_id(source->client, ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN);
source->client = NULL;
}
ICECAST_LOG_ERROR("Content-type not given in PUT request, dropping source");
return -1;
} else {
ICECAST_LOG_ERROR("No content-type header, falling back to backwards compatibility mode "
"for icecast 1.x relays. Assuming content is mp3. This behaviour is deprecated "
"and the source client will NOT work with future Icecast versions!");
format_type = FORMAT_TYPE_GENERIC;
}
if (format_get_plugin (format_type, source) < 0) {
global_unlock();
config_release_config();
if (response) {
client_send_error_by_id(source->client, ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR);
source->client = NULL;
}
ICECAST_LOG_WARN("plugin format failed for \"%s\"", source->mount);
return -1;
}
global.sources++;
stats_event_args(NULL, "sources", "%d", global.sources);
global_unlock();
source->running = 1;
mountinfo = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
source_update_settings(config, source, mountinfo);
config_release_config();
slave_rebuild_mounts();
source->shutdown_rwlock = &_source_shutdown_rwlock;
ICECAST_LOG_DEBUG("source is ready to start");
return 0;
}
ICECAST_LOG_WARN("Request to add source when maximum source limit "
"reached %d", global.sources);
global_unlock();
config_release_config();
if (response) {
client_send_error_by_id(source->client, ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT);
source->client = NULL;
}
return -1;
}
static void _handle_shoutcast_compatible(client_queue_entry_t *node)
{
char *http_compliant;
int http_compliant_len = 0;
http_parser_t *parser;
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
const char *shoutcast_mount;
client_t *client = node->client;
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
ice_config_t *config;
2019-09-17 17:32:00 +00:00
ICECAST_LOG_DDEBUG("Client %p is a shoutcast client of stage %i", client, (int)node->shoutcast);
if (node->shoutcast == 1)
{
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
char *ptr, *headers;
2019-09-17 17:32:00 +00:00
ICECAST_LOG_DDEBUG("Client %p has buffer: %H", client, client->refbuf->data);
/* Get rid of trailing \r\n or \n after password */
ptr = strstr(client->refbuf->data, "\r\r\n");
if (ptr) {
headers = ptr+3;
} else {
ptr = strstr(client->refbuf->data, "\r\n");
if (ptr) {
headers = ptr+2;
} else {
ptr = strstr(client->refbuf->data, "\n");
if (ptr)
headers = ptr+1;
}
}
if (ptr == NULL){
client_destroy(client);
free_client_node(node);
return;
}
*ptr = '\0';
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
client->password = strdup(client->refbuf->data);
config = config_get_config();
client->username = strdup(config->shoutcast_user);
config_release_config();
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
node->offset -= (headers - client->refbuf->data);
memmove(client->refbuf->data, headers, node->offset+1);
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
node->shoutcast = 2;
/* we've checked the password, now send it back for reading headers */
2022-03-19 19:05:53 +00:00
client_queue_add(&_request_queue, node);
2019-09-17 17:32:00 +00:00
ICECAST_LOG_DDEBUG("Client %p re-added to request queue", client);
return;
}
/* actually make a copy as we are dropping the config lock */
/* Here we create a valid HTTP request based of the information
that was passed in via the non-HTTP style protocol above. This
means we can use some of our existing code to handle this case */
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
config = config_get_config();
if (node->shoutcast_mount) {
shoutcast_mount = node->shoutcast_mount;
} else {
shoutcast_mount = config->shoutcast_mount;
}
http_compliant_len = 20 + strlen(shoutcast_mount) + node->offset;
http_compliant = (char *)calloc(1, http_compliant_len);
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
snprintf(http_compliant, http_compliant_len,
"SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
config_release_config();
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, http_compliant, strlen(http_compliant))) {
client->refbuf->len = 0;
client->parser = parser;
Wow. Mega patch! This patch *replaces* the authentication system completly. What is new: - <authentication> in mount section is now a container object. - <authentication> in root and mount section may hold any number of <role>-Tags. - <role> tags: Those tags define a 'role' and it's ACL rules. A role is a instance of an authentication module (see below). <role> takes the following options. All but type are optional. - authentication related: - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd; symbolic constants in auth.h) - name: Name for the role. For later matching. (values: any string; default: (none)) - method: This rule is only active on the given list of HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - ACL related: - allow-method: Allowed HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get) - deny-method: Rejected HTTP methods. (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *) - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u) - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *) - allow-web: Allowed web pages. (values: empty or *; default: *) - deny-web: Rejected web pages. (values: empty or *; default: (empty)) - connections-per-user: maximum number of simultaneous connections per role and username. This is only active on active sources. (values: unlimited or number of connections; default: unlimited) - connection-duration: maximum time of a connection. This is only active on active sources. (values: unlimited or number of secounds; default: unlimited) <role> takes <option> child tags. <option> tags contain a name and a value option. Meaning of <option> tags is up to the authentication module. - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried. - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback. Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time) and rejects all other requests. - New authentication module: anonymous This module matches all requests. No options taken. - New authentication module: static This module matches with a static username and password. It takes two <option>s. One with name="username" and one with name="password" to set username and password. This replaces old style <*-username> and <*-password> tags. - New authentication module: legacy-password This module matches with a statich password. It takes one <option> with name="password" to set password. This replaces old ICE and ICY (shoutcast compat mode) authentication. - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility. - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global <authentication> for 100% backward compatibility. - <alias> is now proccessed very early. This enables them to be used for all kinds of requests. To Do List & What does not yet work: - type="url" auth: mount_add and mount_remove. This should be replaced by an unique feature I would call '<event>'. - Admin commands manageauth and manageauth.xsl are disabled as they need more review: This code needs to be ported to support multiple <role>s per <mount>. - url authentication module can not yet return AUTH_NOMATCH. This needs to be reviewed and discussed on how to handle this case best way. - Default config files needs to be updated to reflect the changes. As this is quite some political act it should be done in dicussion with the whole team and permission of the release manager. - Docs need to be updated to reflect the changes. How does it work: Code has been changed so that authentification is done early for all clients. This allows accessing the ACL data (client->acl) from nearly everywhere in the code. After accept() and initial client setup the request is parsed. In the next step all <alias>es are resolved. After this the client is passed for authentication. After authentication it is passed to the corresponding subsystem depending on kind of request. All authentication instances have a thread running for doing the authentication. This thread works on a queue of clients. Hints for testers: - Test with default config. - Test with diffrent authentication modules in <mount>. - Test shoutcast compatibility mode. - Test with new style <authentication> and any amount of <role> (zero to quite some). - Test <alias> lookup on all kinds of objects. - Test source level credential login into the admin interface. - Test shoucast style meta data updates. - Test playlist generation. Thank you for reading this long commit message. Have fun reading the full patch! svn path=/icecast/trunk/icecast/; revision=19358
2014-11-28 23:46:08 +00:00
client->protocol = ICECAST_PROTOCOL_SHOUTCAST;
node->shoutcast = 0;
return;
} else {
httpp_destroy(parser);
client_destroy(client);
}
free(http_compliant);
free_client_node(node);
return;
}
/* Check if we need body of client */
static int _need_body(client_queue_entry_t *node)
{
client_t *client = node->client;
if (node->tried_body)
return 0;
if (client->parser->req_type == httpp_req_source) {
/* SOURCE connection. */
return 0;
} else if (client->parser->req_type == httpp_req_put) {
/* PUT connection.
* TODO: We may need body for /admin/ but we do not know if it's an admin request yet.
*/
return 0;
} else if (client->request_body_length != -1 && (size_t)client->request_body_length != client->request_body_read) {
return 1;
} else if (client->request_body_length == -1 && client_body_eof(client) == 0) {
return 1;
}
return 0;
}
/* 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
* request handler.
*/
static void * _handle_connection(client_queue_t *queue)
{
while (client_queue_running(queue)) {
client_queue_entry_t *node;
node = client_queue_shift(&_connection_queue, NULL);
if (node) {
client_t *client = node->client;
http_parser_t *parser;
const char *rawuri;
int already_parsed = 0;
/* Check for special shoutcast compatability processing */
if (node->shoutcast) {
_handle_shoutcast_compatible (node);
if (node->shoutcast)
continue;
}
/* process normal HTTP headers */
if (client->parser) {
already_parsed = 1;
parser = client->parser;
} else {
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
client->parser = parser;
}
if (already_parsed || httpp_parse (parser, client->refbuf->data, node->offset)) {
client->refbuf->len = 0;
/* early check if we need more data */
client_complete(client);
if (_need_body(node)) {
/* Just calling client_queue_add(&_body_queue, node) would do the job.
* However, if the client only has a small body this might work without moving it between queues.
* -> much faster.
*/
client_slurp_result_t res;
ice_config_t *config;
time_t timeout;
size_t body_size_limit;
config = config_get_config();
timeout = time(NULL) - config->body_timeout;
body_size_limit = config->body_size_limit;
config_release_config();
res = process_request_body_queue_one(node, timeout, body_size_limit);
if (res != CLIENT_SLURP_SUCCESS) {
ICECAST_LOG_DEBUG("Putting client %p in body queue.", client);
client_queue_add(&_body_queue, node);
continue;
} else {
ICECAST_LOG_DEBUG("Success on fast lane");
}
}
rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
/* assign a port-based shoutcast mountpoint if required */
if (node->shoutcast_mount && strcmp (rawuri, "/admin.cgi") == 0)
httpp_set_query_param (client->parser, "mount", node->shoutcast_mount);
client_queue_add(&_handle_queue, node);
} else {
free_client_node(node);
ICECAST_LOG_ERROR("HTTP request parsing failed");
client_destroy (client);
}
} else {
client_queue_wait(queue);
}
}
return NULL;
}
static void * handle_client_worker(client_queue_t *queue)
{
while (client_queue_running(queue)) {
client_queue_entry_t *node = client_queue_shift(queue, NULL);
if (node) {
client_t *client = node->client;
free_client_node(node);
connection_handle_client(client);
} else {
client_queue_wait(queue);
}
}
return NULL;
}
static void __on_sock_count(size_t count, void *userdata)
{
(void)userdata;
ICECAST_LOG_DEBUG("Listen socket count is now %zu.", count);
if (count == 0 && global.running == ICECAST_RUNNING) {
ICECAST_LOG_INFO("No more listen sockets. Exiting.");
global.running = ICECAST_HALTING;
}
}
/* called when listening thread is not checking for incoming connections */
void connection_setup_sockets (ice_config_t *config)
{
global_lock();
refobject_unref(global.listensockets);
if (config == NULL) {
global_unlock();
return;
}
/* setup the banned/allowed IP filenames from the xml */
if (config->banfile) {
matchfile_release(banned_ip);
banned_ip = matchfile_new(config->banfile);
if (!banned_ip)
ICECAST_LOG_ERROR("Can not create ban object, bad!");
}
if (config->allowfile) {
matchfile_release(allowed_ip);
allowed_ip = matchfile_new(config->allowfile);
}
global.listensockets = refobject_new(listensocket_container_t);
listensocket_container_configure(global.listensockets, config);
global_unlock();
listensocket_container_set_sockcount_cb(global.listensockets, __on_sock_count, NULL);
listensocket_container_setup(global.listensockets);;
}
void connection_close(connection_t *con)
{
if (!con)
return;
ICECAST_LOG_DEBUG("Closing connection %p (connection ID: %llu, sock=%R)", con, (long long unsigned int)con->id, con->sock);
fastevent_emit(FASTEVENT_TYPE_CONNECTION_DESTROY, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CONNECTION, con);
tls_unref(con->tls);
if (con->sock != SOCK_ERROR)
sock_close(con->sock);
if (con->ip)
free(con->ip);
if (con->readbuffer)
free(con->readbuffer);
refobject_unref(con->listensocket_real);
refobject_unref(con->listensocket_effective);
free(con);
}
void connection_queue_client(client_t *client)
{
client_queue_entry_t *node = create_client_node(client);
2022-03-19 19:05:53 +00:00
client_queue_add(&_connection_queue, node);
}