mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-06-30 22:18:19 -04:00
HTTP Basic source login support. The old "ice-password" method is still
available, but is deprecated and turned off by default. svn path=/trunk/icecast/; revision=3837
This commit is contained in:
parent
def074c801
commit
913ec3481a
@ -55,3 +55,12 @@ void client_send_404(client_t *client, char *message) {
|
||||
client_destroy(client);
|
||||
}
|
||||
|
||||
void client_send_401(client_t *client) {
|
||||
int bytes = sock_write(client->con->sock,
|
||||
"HTTP/1.0 401 Authentication Required\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
|
||||
"\r\n"
|
||||
"You need to authenticate\r\n");
|
||||
if(bytes > 0) client->con->sent_bytes = bytes;
|
||||
client_destroy(client);
|
||||
}
|
||||
|
@ -25,5 +25,6 @@ typedef struct _client_tag
|
||||
client_t *client_create(connection_t *con, http_parser_t *parser);
|
||||
void client_destroy(client_t *client);
|
||||
void client_send_404(client_t *client, char *message);
|
||||
void client_send_401(client_t *client);
|
||||
|
||||
#endif /* __CLIENT_H__ */
|
||||
|
@ -14,6 +14,7 @@
|
||||
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
|
||||
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
|
||||
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
|
||||
#define CONFIG_DEFAULT_ICE_LOGIN 0
|
||||
#define CONFIG_DEFAULT_TOUCH_FREQ 5
|
||||
#define CONFIG_DEFAULT_HOSTNAME "localhost"
|
||||
#define CONFIG_DEFAULT_PORT 8888
|
||||
@ -156,6 +157,7 @@ static void _set_defaults(void)
|
||||
_configuration.header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
|
||||
_configuration.source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
|
||||
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
|
||||
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
|
||||
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
|
||||
_configuration.dir_list = NULL;
|
||||
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
|
||||
@ -200,6 +202,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
|
||||
if (_configuration.source_password && _configuration.source_password != CONFIG_DEFAULT_SOURCE_PASSWORD) xmlFree(_configuration.source_password);
|
||||
_configuration.source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||
}
|
||||
} else if (strcmp(node->name, "icelogin") == 0) {
|
||||
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||
_configuration.ice_login = atoi(tmp);
|
||||
if (tmp) xmlFree(tmp);
|
||||
} else if (strcmp(node->name, "hostname") == 0) {
|
||||
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
|
||||
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||
|
@ -24,6 +24,7 @@ typedef struct ice_config_tag
|
||||
int client_timeout;
|
||||
int header_timeout;
|
||||
int source_timeout;
|
||||
int ice_login;
|
||||
|
||||
char *source_password;
|
||||
|
||||
|
@ -286,9 +286,11 @@ static connection_t *_get_connection(void)
|
||||
return con;
|
||||
}
|
||||
|
||||
int connection_create_source(connection_t *con, http_parser_t *parser, char *mount) {
|
||||
int connection_create_source(client_t *client, connection_t *con, http_parser_t *parser, char *mount) {
|
||||
source_t *source;
|
||||
char *contenttype;
|
||||
int bytes;
|
||||
|
||||
/* check to make sure this source wouldn't
|
||||
** be over the limit
|
||||
*/
|
||||
@ -311,12 +313,16 @@ int connection_create_source(connection_t *con, http_parser_t *parser, char *mou
|
||||
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
|
||||
goto fail;
|
||||
} else {
|
||||
source = source_create(con, parser, mount, format);
|
||||
source = source_create(client, con, parser, mount, format);
|
||||
}
|
||||
} else {
|
||||
WARN0("No content-type header, cannot handle source");
|
||||
goto fail;
|
||||
}
|
||||
bytes = sock_write(client->con->sock,
|
||||
"HTTP/1.0 200 OK\r\n\r\n");
|
||||
if(bytes > 0) client->con->sent_bytes = bytes;
|
||||
|
||||
source->shutdown_rwlock = &_source_shutdown_rwlock;
|
||||
sock_set_blocking(con->sock, SOCK_NONBLOCK);
|
||||
thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
|
||||
@ -331,7 +337,46 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _check_source_pass(http_parser_t *parser)
|
||||
static int _check_source_pass_http(http_parser_t *parser)
|
||||
{
|
||||
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
|
||||
char *header = httpp_getvar(parser, "authorization");
|
||||
char *userpass, *tmp;
|
||||
char *username, *password;
|
||||
char *correctpass;
|
||||
|
||||
correctpass = config_get_config()->source_password;
|
||||
if(!correctpass)
|
||||
correctpass = "";
|
||||
|
||||
if(header == NULL)
|
||||
return 0;
|
||||
|
||||
if(strncmp(header, "Basic ", 6))
|
||||
return 0;
|
||||
|
||||
userpass = util_base64_decode(header+6);
|
||||
if(userpass == NULL)
|
||||
return 0;
|
||||
|
||||
tmp = strchr(userpass, ':');
|
||||
if(!tmp) {
|
||||
free(userpass);
|
||||
return 0;
|
||||
}
|
||||
*tmp = 0;
|
||||
username = userpass;
|
||||
password = tmp+1;
|
||||
|
||||
if(strcmp(username, "source") || strcmp(password, correctpass)) {
|
||||
free(userpass);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_source_pass_ice(http_parser_t *parser)
|
||||
{
|
||||
char *password, *correctpass;
|
||||
|
||||
@ -348,16 +393,27 @@ static int _check_source_pass(http_parser_t *parser)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_source_pass(http_parser_t *parser)
|
||||
{
|
||||
if(config_get_config()->ice_login)
|
||||
return _check_source_pass_ice(parser);
|
||||
else
|
||||
return _check_source_pass_http(parser);
|
||||
}
|
||||
|
||||
static void _handle_source_request(connection_t *con,
|
||||
http_parser_t *parser, char *uri)
|
||||
{
|
||||
client_t *client;
|
||||
|
||||
client = client_create(con, parser);
|
||||
|
||||
INFO1("Source logging in at mountpoint \"%s\"", uri);
|
||||
stats_event_inc(NULL, "source_connections");
|
||||
|
||||
if (!_check_source_pass(parser)) {
|
||||
INFO1("Source (%s) attempted to login with bad password", uri);
|
||||
connection_close(con);
|
||||
httpp_destroy(parser);
|
||||
client_send_401(client);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -368,16 +424,14 @@ static void _handle_source_request(connection_t *con,
|
||||
avl_tree_rlock(global.source_tree);
|
||||
if (source_find_mount(uri) != NULL) {
|
||||
INFO1("Source tried to log in as %s, but mountpoint is already used", uri);
|
||||
connection_close(con);
|
||||
httpp_destroy(parser);
|
||||
client_send_404(client, "Mountpoint in use");
|
||||
avl_tree_unlock(global.source_tree);
|
||||
return;
|
||||
}
|
||||
avl_tree_unlock(global.source_tree);
|
||||
|
||||
if (!connection_create_source(con, parser, uri)) {
|
||||
connection_close(con);
|
||||
httpp_destroy(parser);
|
||||
if (!connection_create_source(client, con, parser, uri)) {
|
||||
client_destroy(client);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,6 +486,11 @@ static void _handle_get_request(connection_t *con,
|
||||
*/
|
||||
/* TODO: add GUID-xxxxxx */
|
||||
if (strcmp(uri, "/stats.xml") == 0) {
|
||||
if (!_check_source_pass(parser)) {
|
||||
INFO1("Source (%s) attempted to login with bad password", uri);
|
||||
client_send_401(client);
|
||||
return;
|
||||
}
|
||||
DEBUG0("Stats request, sending xml stats");
|
||||
stats_sendxml(client);
|
||||
client_destroy(client);
|
||||
@ -491,7 +550,7 @@ static void _handle_get_request(connection_t *con,
|
||||
if (strcmp(uri, "/allstreams.txt") == 0) {
|
||||
if (!_check_source_pass(parser)) {
|
||||
INFO0("Client attempted to fetch allstreams.txt with bad password");
|
||||
client_send_404(client, "Bad ice-password");
|
||||
client_send_401(client);
|
||||
} else {
|
||||
avl_node *node;
|
||||
source_t *s;
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "thread.h"
|
||||
#include "sock.h"
|
||||
|
||||
struct _client_tag;
|
||||
|
||||
typedef struct connection_tag
|
||||
{
|
||||
unsigned long id;
|
||||
@ -26,8 +28,8 @@ void connection_shutdown(void);
|
||||
void connection_accept_loop(void);
|
||||
void connection_close(connection_t *con);
|
||||
connection_t *create_connection(sock_t sock, char *ip);
|
||||
int connection_create_source(connection_t *con, http_parser_t *parser,
|
||||
char *mount);
|
||||
int connection_create_source(struct _client_tag *client, connection_t *con,
|
||||
http_parser_t *parser, char *mount);
|
||||
|
||||
extern rwlock_t _source_shutdown_rwlock;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "connection.h"
|
||||
#include "refbuf.h"
|
||||
#include "format.h"
|
||||
#include "client.h"
|
||||
#include "source.h"
|
||||
|
||||
#include "global.h"
|
||||
|
@ -70,6 +70,7 @@ static void *_slave_thread(void *arg) {
|
||||
char header[4096];
|
||||
connection_t *con;
|
||||
http_parser_t *parser;
|
||||
client_t *client;
|
||||
int interval = config_get_config()->master_update_interval;
|
||||
|
||||
while (_initialized) {
|
||||
@ -85,6 +86,7 @@ static void *_slave_thread(void *arg) {
|
||||
WARN0("Relay slave failed to contact master server to fetch stream list");
|
||||
continue;
|
||||
}
|
||||
// FIXME: This is now broken...
|
||||
sock_write(mastersock, "GET /allstreams.txt HTTP/1.0\r\nice-password: %s\r\n\r\n", config_get_config()->source_password);
|
||||
while (sock_read_line(mastersock, buf, sizeof(buf))) {
|
||||
buf[strlen(buf)] = 0;
|
||||
@ -119,10 +121,10 @@ static void *_slave_thread(void *arg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!connection_create_source(con, parser,
|
||||
client = client_create(con, parser);
|
||||
if (!connection_create_source(client, con, parser,
|
||||
httpp_getvar(parser, HTTPP_VAR_URI))) {
|
||||
connection_close(con);
|
||||
httpp_destroy(parser);
|
||||
client_destroy(client);
|
||||
}
|
||||
continue;
|
||||
|
||||
|
@ -39,11 +39,12 @@ static int _compare_clients(void *compare_arg, void *a, void *b);
|
||||
static int _remove_client(void *key);
|
||||
static int _free_client(void *key);
|
||||
|
||||
source_t *source_create(connection_t *con, http_parser_t *parser, const char *mount, format_type_t type)
|
||||
source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type)
|
||||
{
|
||||
source_t *src;
|
||||
|
||||
src = (source_t *)malloc(sizeof(source_t));
|
||||
src->client = client;
|
||||
src->mount = (char *)strdup(mount);
|
||||
src->format = format_get_plugin(type, src->mount);
|
||||
src->con = con;
|
||||
@ -94,8 +95,7 @@ int source_free_source(void *key)
|
||||
source_t *source = (source_t *)key;
|
||||
|
||||
free(source->mount);
|
||||
connection_close(source->con);
|
||||
httpp_destroy(source->parser);
|
||||
client_destroy(source->client);
|
||||
avl_tree_free(source->pending_tree, _free_client);
|
||||
avl_tree_free(source->client_tree, _free_client);
|
||||
source->format->free_plugin(source->format);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
typedef struct source_tag
|
||||
{
|
||||
client_t *client;
|
||||
connection_t *con;
|
||||
http_parser_t *parser;
|
||||
|
||||
@ -15,7 +16,7 @@ typedef struct source_tag
|
||||
rwlock_t *shutdown_rwlock;
|
||||
} source_t;
|
||||
|
||||
source_t *source_create(connection_t *con, http_parser_t *parser, const char *mount, format_type_t type);
|
||||
source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type);
|
||||
source_t *source_find_mount(const char *mount);
|
||||
int source_compare_sources(void *arg, void *a, void *b);
|
||||
int source_free_source(void *key);
|
||||
|
99
src/util.c
99
src/util.c
@ -268,3 +268,102 @@ char *util_normalise_uri(char *uri) {
|
||||
}
|
||||
}
|
||||
|
||||
static char base64table[64] = {
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
|
||||
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
|
||||
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
|
||||
};
|
||||
|
||||
/* This isn't efficient, but it doesn't need to be */
|
||||
char *util_base64_encode(char *data)
|
||||
{
|
||||
int len = strlen(data);
|
||||
char *out = malloc(len*4/3 + 4);
|
||||
char *result = out;
|
||||
int chunk;
|
||||
|
||||
while(len > 0) {
|
||||
chunk = (len >3)?3:len;
|
||||
*out++ = base64table[(*data & 0xFC)>>2];
|
||||
*out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
|
||||
switch(chunk) {
|
||||
case 3:
|
||||
*out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
|
||||
*out++ = base64table[(*(data+2)) & 0x3F];
|
||||
break;
|
||||
case 2:
|
||||
*out++ = base64table[((*(data+1) & 0x0F)<<2)];
|
||||
*out++ = '=';
|
||||
break;
|
||||
case 1:
|
||||
*out++ = '=';
|
||||
*out++ = '=';
|
||||
break;
|
||||
}
|
||||
data += chunk;
|
||||
len -= chunk;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int base64chartoval(char input)
|
||||
{
|
||||
if(input >= 'A' && input <= 'Z')
|
||||
return input - 'A';
|
||||
else if(input >= 'a' && input <= 'z')
|
||||
return input - 'a' + 26;
|
||||
else if(input >= '0' && input <= '9')
|
||||
return input - '0' + 52;
|
||||
else if(input == '+')
|
||||
return 62;
|
||||
else if(input == '/')
|
||||
return 63;
|
||||
else if(input == '=')
|
||||
return -1;
|
||||
else
|
||||
return -2;
|
||||
}
|
||||
|
||||
char *util_base64_decode(char *input)
|
||||
{
|
||||
int len = strlen(input);
|
||||
char *out = malloc(len*3/4 + 5);
|
||||
char *result = out;
|
||||
signed char vals[4];
|
||||
|
||||
while(len > 0) {
|
||||
if(len < 4)
|
||||
{
|
||||
free(result);
|
||||
return NULL; /* Invalid Base64 data */
|
||||
}
|
||||
|
||||
vals[0] = base64chartoval(*input++);
|
||||
vals[1] = base64chartoval(*input++);
|
||||
vals[2] = base64chartoval(*input++);
|
||||
vals[3] = base64chartoval(*input++);
|
||||
|
||||
if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*out++ = vals[0]<<2 | vals[1]>>4;
|
||||
if(vals[2] >= 0)
|
||||
*out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
|
||||
else
|
||||
*out++ = 0;
|
||||
|
||||
if(vals[3] >= 0)
|
||||
*out++ = ((vals[2]&0x03)<<6) | (vals[3]);
|
||||
else
|
||||
*out++ = 0;
|
||||
|
||||
len -= 4;
|
||||
}
|
||||
*out = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -11,5 +11,7 @@ char *util_get_extension(char *path);
|
||||
char *util_get_path_from_uri(char *uri);
|
||||
char *util_get_path_from_normalised_uri(char *uri);
|
||||
char *util_normalise_uri(char *uri);
|
||||
char *util_base64_encode(char *data);
|
||||
char *util_base64_decode(char *input);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user