2004-01-28 20:02:12 -05:00
|
|
|
/* Icecast
|
|
|
|
*
|
|
|
|
* This program is distributed under the GNU General Public License, version 2.
|
|
|
|
* A copy of this license is included with this source.
|
|
|
|
*
|
|
|
|
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
|
|
|
|
* Michael Smith <msmith@xiph.org>,
|
|
|
|
* oddsock <oddsock@xiph.org>,
|
|
|
|
* Karl Heyes <karl@xiph.org>
|
|
|
|
* and others (see AUTHORS for details).
|
|
|
|
*/
|
|
|
|
|
2003-03-27 12:10:14 -05:00
|
|
|
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
2003-07-20 21:58:54 -04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2001-09-09 22:21:46 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2001-10-20 02:43:04 -04:00
|
|
|
#include <time.h>
|
2003-03-09 06:27:06 -05:00
|
|
|
#ifdef HAVE_POLL
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#endif
|
2001-10-20 02:43:04 -04:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <sys/time.h>
|
2001-09-09 22:21:46 -04:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2001-10-20 02:43:04 -04:00
|
|
|
#else
|
2002-02-06 20:04:09 -05:00
|
|
|
#include <winsock2.h>
|
2001-10-20 02:43:04 -04:00
|
|
|
#define snprintf _snprintf
|
|
|
|
#define strcasecmp stricmp
|
2002-02-06 20:04:09 -05:00
|
|
|
#define strncasecmp strnicmp
|
2001-10-20 02:43:04 -04:00
|
|
|
#endif
|
2001-09-09 22:21:46 -04:00
|
|
|
|
|
|
|
#include "os.h"
|
|
|
|
|
2003-07-16 15:41:59 -04:00
|
|
|
#include "thread/thread.h"
|
|
|
|
#include "avl/avl.h"
|
|
|
|
#include "net/sock.h"
|
|
|
|
#include "httpp/httpp.h"
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-07-20 21:58:54 -04:00
|
|
|
#include "cfgfile.h"
|
2001-09-09 22:21:46 -04:00
|
|
|
#include "global.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "connection.h"
|
|
|
|
#include "refbuf.h"
|
|
|
|
#include "client.h"
|
|
|
|
#include "stats.h"
|
|
|
|
#include "logging.h"
|
2002-08-09 02:52:07 -04:00
|
|
|
#include "xslt.h"
|
2002-08-18 01:06:58 -04:00
|
|
|
#include "fserve.h"
|
2003-07-24 19:45:29 -04:00
|
|
|
#include "sighandler.h"
|
2003-02-02 09:33:47 -05:00
|
|
|
|
|
|
|
#include "yp.h"
|
2001-09-09 22:21:46 -04:00
|
|
|
#include "source.h"
|
2002-12-30 02:55:56 -05:00
|
|
|
#include "format.h"
|
2002-12-31 01:28:39 -05:00
|
|
|
#include "format_mp3.h"
|
2003-03-05 08:03:35 -05:00
|
|
|
#include "event.h"
|
2003-03-06 09:17:33 -05:00
|
|
|
#include "admin.h"
|
2004-01-14 20:01:09 -05:00
|
|
|
#include "auth.h"
|
2001-09-09 22:21:46 -04:00
|
|
|
|
|
|
|
#define CATMODULE "connection"
|
|
|
|
|
2004-11-11 10:47:33 -05:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
#define SHOUTCAST_SOURCE_AUTH 1
|
|
|
|
#define ICECAST_SOURCE_AUTH 0
|
|
|
|
|
2001-09-09 22:21:46 -04:00
|
|
|
typedef struct con_queue_tag {
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_t *con;
|
|
|
|
struct con_queue_tag *next;
|
2001-09-09 22:21:46 -04:00
|
|
|
} con_queue_t;
|
|
|
|
|
|
|
|
typedef struct _thread_queue_tag {
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_type *thread_id;
|
|
|
|
struct _thread_queue_tag *next;
|
2001-09-09 22:21:46 -04:00
|
|
|
} thread_queue_t;
|
|
|
|
|
|
|
|
static mutex_t _connection_mutex;
|
2004-10-26 12:31:16 -04:00
|
|
|
static volatile unsigned long _current_id = 0;
|
2001-09-09 22:21:46 -04:00
|
|
|
static int _initialized = 0;
|
|
|
|
|
2004-10-27 10:13:29 -04:00
|
|
|
volatile static con_queue_t *_queue = NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
static mutex_t _queue_mutex;
|
|
|
|
|
|
|
|
static thread_queue_t *_conhands = NULL;
|
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
rwlock_t _source_shutdown_rwlock;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
|
|
|
static void *_handle_connection(void *arg);
|
|
|
|
|
|
|
|
void connection_initialize(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
if (_initialized) return;
|
|
|
|
|
|
|
|
thread_mutex_create(&_connection_mutex);
|
|
|
|
thread_mutex_create(&_queue_mutex);
|
2004-01-29 11:46:54 -05:00
|
|
|
thread_mutex_create(&move_clients_mutex);
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_rwlock_create(&_source_shutdown_rwlock);
|
2002-08-05 10:48:04 -04:00
|
|
|
thread_cond_create(&global.shutdown_cond);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
_initialized = 1;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void connection_shutdown(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
if (!_initialized) return;
|
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
thread_cond_destroy(&global.shutdown_cond);
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_rwlock_destroy(&_source_shutdown_rwlock);
|
|
|
|
thread_mutex_destroy(&_queue_mutex);
|
|
|
|
thread_mutex_destroy(&_connection_mutex);
|
2004-01-29 11:46:54 -05:00
|
|
|
thread_mutex_destroy(&move_clients_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
_initialized = 0;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long _next_connection_id(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
unsigned long id;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_mutex_lock(&_connection_mutex);
|
|
|
|
id = _current_id++;
|
|
|
|
thread_mutex_unlock(&_connection_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return id;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2003-04-23 08:44:29 -04:00
|
|
|
connection_t *create_connection(sock_t sock, sock_t serversock, char *ip) {
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_t *con;
|
|
|
|
con = (connection_t *)malloc(sizeof(connection_t));
|
|
|
|
memset(con, 0, sizeof(connection_t));
|
|
|
|
con->sock = sock;
|
2003-04-23 08:44:29 -04:00
|
|
|
con->serversock = serversock;
|
2003-03-14 21:10:19 -05:00
|
|
|
con->con_time = time(NULL);
|
|
|
|
con->id = _next_connection_id();
|
|
|
|
con->ip = ip;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
con->event_number = EVENT_NO_EVENT;
|
|
|
|
con->event = NULL;
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return con;
|
2002-08-05 10:48:04 -04:00
|
|
|
}
|
|
|
|
|
2003-03-09 06:27:06 -05:00
|
|
|
static int wait_for_serversock(int timeout)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_POLL
|
|
|
|
struct pollfd ufds[MAX_LISTEN_SOCKETS];
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
|
|
|
ufds[i].fd = global.serversock[i];
|
|
|
|
ufds[i].events = POLLIN;
|
|
|
|
ufds[i].revents = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = poll(ufds, global.server_sockets, timeout);
|
|
|
|
if(ret < 0) {
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
else if(ret == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
2003-06-20 14:50:24 -04:00
|
|
|
int dst;
|
2003-03-09 06:27:06 -05:00
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
2003-06-20 14:50:24 -04:00
|
|
|
if(ufds[i].revents & POLLIN)
|
2003-03-09 06:27:06 -05:00
|
|
|
return ufds[i].fd;
|
2003-06-20 14:50:24 -04:00
|
|
|
if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
|
|
|
|
{
|
|
|
|
if (ufds[i].revents & (POLLHUP|POLLERR))
|
|
|
|
{
|
|
|
|
close (global.serversock[i]);
|
|
|
|
WARN0("Had to close a listening socket");
|
|
|
|
}
|
|
|
|
global.serversock[i] = -1;
|
|
|
|
}
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
2003-06-20 14:50:24 -04:00
|
|
|
/* remove any closed sockets */
|
|
|
|
for(i=0, dst=0; i < global.server_sockets; i++)
|
|
|
|
{
|
|
|
|
if (global.serversock[i] == -1)
|
|
|
|
continue;
|
|
|
|
if (i!=dst)
|
|
|
|
global.serversock[dst] = global.serversock[i];
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
global.server_sockets = dst;
|
|
|
|
return -1;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
fd_set rfds;
|
|
|
|
struct timeval tv, *p=NULL;
|
|
|
|
int i, ret;
|
|
|
|
int max = -1;
|
|
|
|
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
|
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
|
|
|
FD_SET(global.serversock[i], &rfds);
|
|
|
|
if(global.serversock[i] > max)
|
|
|
|
max = global.serversock[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(timeout >= 0) {
|
|
|
|
tv.tv_sec = timeout/1000;
|
2003-03-12 00:40:45 -05:00
|
|
|
tv.tv_usec = (timeout % 1000) * 1000;
|
2003-03-09 06:27:06 -05:00
|
|
|
p = &tv;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = select(max+1, &rfds, NULL, NULL, p);
|
|
|
|
if(ret < 0) {
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
else if(ret == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
|
|
|
if(FD_ISSET(global.serversock[i], &rfds))
|
|
|
|
return global.serversock[i];
|
|
|
|
}
|
|
|
|
return -1; /* Should be impossible, stop compiler warnings */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2001-09-09 22:21:46 -04:00
|
|
|
static connection_t *_accept_connection(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
int sock;
|
|
|
|
connection_t *con;
|
|
|
|
char *ip;
|
2003-03-09 06:27:06 -05:00
|
|
|
int serversock;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-09 06:27:06 -05:00
|
|
|
serversock = wait_for_serversock(100);
|
|
|
|
if(serversock < 0)
|
|
|
|
return NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* malloc enough room for a full IP address (including ipv6) */
|
|
|
|
ip = (char *)malloc(MAX_ADDR_LEN);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
|
|
|
|
if (sock >= 0) {
|
2003-04-23 08:44:29 -04:00
|
|
|
con = create_connection(sock, serversock, ip);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return con;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (!sock_recoverable(sock_error()))
|
|
|
|
WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
|
|
|
|
|
|
|
|
free(ip);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _add_connection(connection_t *con)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
con_queue_t *node;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
node = (con_queue_t *)malloc(sizeof(con_queue_t));
|
|
|
|
|
|
|
|
thread_mutex_lock(&_queue_mutex);
|
|
|
|
node->con = con;
|
2004-10-26 12:31:16 -04:00
|
|
|
node->next = (con_queue_t *)_queue;
|
2003-03-14 21:10:19 -05:00
|
|
|
_queue = node;
|
|
|
|
thread_mutex_unlock(&_queue_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2002-12-29 10:46:32 -05:00
|
|
|
static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
/* create item */
|
|
|
|
thread_queue_t *item = (thread_queue_t *)malloc(sizeof(thread_queue_t));
|
|
|
|
item->thread_id = thread_id;
|
|
|
|
item->next = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
thread_mutex_lock(&_queue_mutex);
|
|
|
|
if (*queue == NULL) {
|
|
|
|
*queue = item;
|
|
|
|
} else {
|
|
|
|
item->next = *queue;
|
|
|
|
*queue = item;
|
|
|
|
}
|
|
|
|
thread_mutex_unlock(&_queue_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2002-12-29 10:46:32 -05:00
|
|
|
static thread_type *_pop_thread(thread_queue_t **queue)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_type *id;
|
|
|
|
thread_queue_t *item;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_mutex_lock(&_queue_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
item = *queue;
|
|
|
|
if (item == NULL) {
|
|
|
|
thread_mutex_unlock(&_queue_mutex);
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
*queue = item->next;
|
|
|
|
item->next = NULL;
|
|
|
|
id = item->thread_id;
|
|
|
|
free(item);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_mutex_unlock(&_queue_mutex);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return id;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _build_pool(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
ice_config_t *config;
|
|
|
|
int i;
|
2002-12-29 10:46:32 -05:00
|
|
|
thread_type *tid;
|
2003-03-14 21:10:19 -05:00
|
|
|
char buff[64];
|
2003-03-05 08:03:35 -05:00
|
|
|
int threadpool_size;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
config = config_get_config();
|
2003-03-05 08:03:35 -05:00
|
|
|
threadpool_size = config->threadpool_size;
|
|
|
|
config_release_config();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
for (i = 0; i < threadpool_size; i++) {
|
|
|
|
snprintf(buff, 64, "Connection Thread #%d", i);
|
|
|
|
tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
|
|
|
|
_push_thread(&_conhands, tid);
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _destroy_pool(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_type *id;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
id = _pop_thread(&_conhands);
|
|
|
|
while (id != NULL) {
|
|
|
|
thread_join(id);
|
|
|
|
id = _pop_thread(&_conhands);
|
|
|
|
}
|
2004-10-26 12:31:16 -04:00
|
|
|
INFO0("All connection threads down");
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void connection_accept_loop(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_t *con;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
_build_pool();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-07-24 19:45:29 -04:00
|
|
|
while (global.running == ICE_RUNNING)
|
|
|
|
{
|
2003-07-25 10:29:33 -04:00
|
|
|
if (global . schedule_config_reread)
|
2003-07-24 19:45:29 -04:00
|
|
|
{
|
|
|
|
/* reread config file */
|
|
|
|
INFO0("Scheduling config reread ...");
|
|
|
|
|
|
|
|
connection_inject_event(EVENT_CONFIG_READ, NULL);
|
2003-07-25 10:29:33 -04:00
|
|
|
global . schedule_config_reread = 0;
|
2003-07-24 19:45:29 -04:00
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
con = _accept_connection();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (con) {
|
|
|
|
_add_connection(con);
|
|
|
|
}
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
/* Give all the other threads notification to shut down */
|
|
|
|
thread_cond_broadcast(&global.shutdown_cond);
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
_destroy_pool();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* wait for all the sources to shutdown */
|
|
|
|
thread_rwlock_wlock(&_source_shutdown_rwlock);
|
|
|
|
thread_rwlock_unlock(&_source_shutdown_rwlock);
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static connection_t *_get_connection(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
con_queue_t *node = NULL;
|
|
|
|
con_queue_t *oldnode = NULL;
|
|
|
|
connection_t *con = NULL;
|
|
|
|
|
2004-10-26 12:31:16 -04:00
|
|
|
/* common case, no new connections so don't bother taking locks */
|
|
|
|
if (_queue == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_mutex_lock(&_queue_mutex);
|
|
|
|
if (_queue) {
|
2004-10-26 12:31:16 -04:00
|
|
|
node = (con_queue_t *)_queue;
|
2003-03-14 21:10:19 -05:00
|
|
|
while (node->next) {
|
|
|
|
oldnode = node;
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* node is now the last node
|
|
|
|
** and oldnode is the previous one, or NULL
|
|
|
|
*/
|
|
|
|
if (oldnode) oldnode->next = NULL;
|
|
|
|
else (_queue) = NULL;
|
|
|
|
}
|
|
|
|
thread_mutex_unlock(&_queue_mutex);
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
con = node->con;
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return con;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2003-03-05 08:03:35 -05:00
|
|
|
void connection_inject_event(int eventnum, void *event_data) {
|
|
|
|
connection_t *con = calloc(1, sizeof(connection_t));
|
|
|
|
|
|
|
|
con->event_number = eventnum;
|
|
|
|
con->event = event_data;
|
|
|
|
|
|
|
|
_add_connection(con);
|
|
|
|
}
|
|
|
|
|
2004-02-19 11:32:26 -05:00
|
|
|
|
|
|
|
/* Called when activating a source. Verifies that the source count is not
|
|
|
|
* exceeded and applies any initial parameters.
|
2003-02-17 06:56:12 -05:00
|
|
|
*/
|
2004-02-19 11:32:26 -05:00
|
|
|
int connection_complete_source (source_t *source)
|
|
|
|
{
|
|
|
|
ice_config_t *config = config_get_config();
|
|
|
|
|
|
|
|
global_lock ();
|
|
|
|
DEBUG1 ("sources count is %d", global.sources);
|
|
|
|
|
|
|
|
if (global.sources < config->source_limit)
|
|
|
|
{
|
|
|
|
char *contenttype;
|
2005-05-30 10:50:57 -04:00
|
|
|
mount_proxy *mountinfo;
|
2004-02-19 11:32:26 -05:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
global_unlock();
|
|
|
|
config_release_config();
|
|
|
|
if (source->client)
|
|
|
|
client_send_404 (source->client, "Content-type not supported");
|
|
|
|
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-02-26 06:56:48 -05:00
|
|
|
WARN0("No content-type header, falling back to backwards compatibility mode "
|
2004-02-19 11:32:26 -05:00
|
|
|
"for icecast 1.x relays. Assuming content is mp3.");
|
2004-11-18 18:49:59 -05:00
|
|
|
format_type = FORMAT_TYPE_GENERIC;
|
2004-02-19 11:32:26 -05:00
|
|
|
}
|
|
|
|
|
2004-08-20 11:13:59 -04:00
|
|
|
if (format_get_plugin (format_type, source) < 0)
|
2004-02-19 11:32:26 -05:00
|
|
|
{
|
|
|
|
global_unlock();
|
|
|
|
config_release_config();
|
|
|
|
if (source->client)
|
|
|
|
client_send_404 (source->client, "internal format allocation problem");
|
|
|
|
WARN1 ("plugin format failed for \"%s\"", source->mount);
|
2005-04-18 10:32:26 -04:00
|
|
|
source->client = NULL;
|
2004-02-19 11:32:26 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
global.sources++;
|
2005-06-10 21:24:58 -04:00
|
|
|
stats_event_args (NULL, "sources", "%d", global.sources);
|
2004-02-19 11:32:26 -05:00
|
|
|
global_unlock();
|
|
|
|
|
|
|
|
/* for relays, we don't yet have a client, however we do require one
|
|
|
|
* to retrieve the stream from. This is created here, quite late,
|
|
|
|
* because we can't use this client to return an error code/message,
|
|
|
|
* so we only do this once we know we're going to accept the source.
|
|
|
|
*/
|
|
|
|
if (source->client == NULL)
|
2005-05-06 11:57:15 -04:00
|
|
|
{
|
2004-02-19 11:32:26 -05:00
|
|
|
source->client = client_create (source->con, source->parser);
|
2005-05-06 11:57:15 -04:00
|
|
|
if (source->client == NULL)
|
|
|
|
{
|
|
|
|
config_release_config();
|
|
|
|
global_lock();
|
|
|
|
global.sources--;
|
|
|
|
global_unlock();
|
|
|
|
connection_close (source->con);
|
|
|
|
source->con = NULL;
|
|
|
|
httpp_destroy (source->parser);
|
|
|
|
source->parser = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2004-02-19 11:32:26 -05:00
|
|
|
|
2005-05-30 10:50:57 -04:00
|
|
|
source->running = 1;
|
|
|
|
mountinfo = config_find_mount (config, source->mount);
|
|
|
|
if (mountinfo == NULL)
|
|
|
|
source_update_settings (config, source, mountinfo);
|
|
|
|
source_recheck_mounts ();
|
2004-02-19 11:32:26 -05:00
|
|
|
config_release_config();
|
|
|
|
|
|
|
|
source->shutdown_rwlock = &_source_shutdown_rwlock;
|
|
|
|
DEBUG0 ("source is ready to start");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2004-10-26 12:31:16 -04:00
|
|
|
WARN1("Request to add source when maximum source limit "
|
2004-02-19 11:32:26 -05:00
|
|
|
"reached %d", global.sources);
|
|
|
|
|
|
|
|
global_unlock();
|
|
|
|
config_release_config();
|
|
|
|
|
|
|
|
if (source->client)
|
|
|
|
client_send_404 (source->client, "too many sources connected");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-17 04:32:15 -04:00
|
|
|
static int _check_pass_http(http_parser_t *parser,
|
|
|
|
char *correctuser, char *correctpass)
|
2002-08-16 10:26:48 -04:00
|
|
|
{
|
|
|
|
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
|
|
|
|
char *header = httpp_getvar(parser, "authorization");
|
|
|
|
char *userpass, *tmp;
|
|
|
|
char *username, *password;
|
|
|
|
|
|
|
|
if(header == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(strncmp(header, "Basic ", 6))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
userpass = util_base64_decode(header+6);
|
2002-08-17 04:32:15 -04:00
|
|
|
if(userpass == NULL) {
|
|
|
|
WARN1("Base64 decode of Authorization header \"%s\" failed",
|
|
|
|
header+6);
|
2002-08-16 10:26:48 -04:00
|
|
|
return 0;
|
2002-08-17 04:32:15 -04:00
|
|
|
}
|
2002-08-16 10:26:48 -04:00
|
|
|
|
|
|
|
tmp = strchr(userpass, ':');
|
|
|
|
if(!tmp) {
|
|
|
|
free(userpass);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*tmp = 0;
|
|
|
|
username = userpass;
|
|
|
|
password = tmp+1;
|
|
|
|
|
2002-08-16 10:55:56 -04:00
|
|
|
if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
|
2002-08-16 10:26:48 -04:00
|
|
|
free(userpass);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-08-17 04:32:15 -04:00
|
|
|
free(userpass);
|
2002-08-16 10:26:48 -04:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2003-03-07 23:57:02 -05:00
|
|
|
static int _check_pass_icy(http_parser_t *parser, char *correctpass)
|
|
|
|
{
|
|
|
|
char *password;
|
|
|
|
|
|
|
|
password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
|
|
|
|
if(!password)
|
|
|
|
return 0;
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (strcmp(password, correctpass))
|
2003-03-07 23:57:02 -05:00
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2002-08-17 04:32:15 -04:00
|
|
|
static int _check_pass_ice(http_parser_t *parser, char *correctpass)
|
2002-08-12 10:48:31 -04:00
|
|
|
{
|
2002-08-17 04:32:15 -04:00
|
|
|
char *password;
|
2002-08-12 10:48:31 -04:00
|
|
|
|
|
|
|
password = httpp_getvar(parser, "ice-password");
|
|
|
|
if(!password)
|
|
|
|
password = "";
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (strcmp(password, correctpass))
|
2002-08-12 10:48:31 -04:00
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2003-03-06 09:17:33 -05:00
|
|
|
int connection_check_admin_pass(http_parser_t *parser)
|
2003-02-06 08:10:48 -05:00
|
|
|
{
|
2003-12-12 18:06:44 -05:00
|
|
|
int ret;
|
2003-03-05 08:03:35 -05:00
|
|
|
ice_config_t *config = config_get_config();
|
|
|
|
char *pass = config->admin_password;
|
|
|
|
char *user = config->admin_username;
|
2004-11-19 10:05:36 -05:00
|
|
|
char *protocol;
|
2003-12-12 18:06:44 -05:00
|
|
|
|
|
|
|
if(!pass || !user) {
|
|
|
|
config_release_config();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-11-19 10:05:36 -05:00
|
|
|
protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
|
|
|
|
if (protocol && strcmp (protocol, "ICY") == 0)
|
|
|
|
ret = _check_pass_icy (parser, pass);
|
|
|
|
else
|
|
|
|
ret = _check_pass_http (parser, user, pass);
|
2003-03-05 08:03:35 -05:00
|
|
|
config_release_config();
|
2003-12-12 18:06:44 -05:00
|
|
|
return ret;
|
|
|
|
}
|
2004-01-14 20:01:09 -05:00
|
|
|
|
2003-12-12 18:06:44 -05:00
|
|
|
int connection_check_relay_pass(http_parser_t *parser)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
ice_config_t *config = config_get_config();
|
|
|
|
char *pass = config->relay_password;
|
2005-05-12 20:35:08 -04:00
|
|
|
char *user = config->relay_username;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
2003-12-12 18:06:44 -05:00
|
|
|
if(!pass || !user) {
|
|
|
|
config_release_config();
|
2003-02-06 08:10:48 -05:00
|
|
|
return 0;
|
2003-12-12 18:06:44 -05:00
|
|
|
}
|
2003-02-06 08:10:48 -05:00
|
|
|
|
2003-12-12 18:06:44 -05:00
|
|
|
ret = _check_pass_http(parser, user, pass);
|
|
|
|
config_release_config();
|
|
|
|
return ret;
|
2003-02-06 08:10:48 -05:00
|
|
|
}
|
|
|
|
|
2005-05-08 07:54:46 -04:00
|
|
|
int connection_check_source_pass(http_parser_t *parser, const char *mount)
|
2002-08-17 04:32:15 -04:00
|
|
|
{
|
2003-03-05 08:03:35 -05:00
|
|
|
ice_config_t *config = config_get_config();
|
|
|
|
char *pass = config->source_password;
|
2003-02-11 09:23:34 -05:00
|
|
|
char *user = "source";
|
2002-10-10 04:50:58 -04:00
|
|
|
int ret;
|
2003-03-05 08:03:35 -05:00
|
|
|
int ice_login = config->ice_login;
|
2003-03-07 23:57:02 -05:00
|
|
|
char *protocol;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
2005-05-08 07:54:46 -04:00
|
|
|
mount_proxy *mountinfo = config_find_mount (config, mount);
|
2002-08-17 04:32:15 -04:00
|
|
|
|
2005-05-08 07:54:46 -04:00
|
|
|
if (mountinfo)
|
|
|
|
{
|
|
|
|
if (mountinfo->password)
|
|
|
|
pass = mountinfo->password;
|
|
|
|
if (mountinfo->username)
|
|
|
|
user = mountinfo->username;
|
2003-02-11 09:23:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!pass) {
|
|
|
|
WARN0("No source password set, rejecting source");
|
2003-12-12 18:06:44 -05:00
|
|
|
config_release_config();
|
2003-02-11 09:23:34 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-08 00:38:52 -05:00
|
|
|
protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
|
2003-03-07 23:57:02 -05:00
|
|
|
if(protocol != NULL && !strcmp(protocol, "ICY")) {
|
|
|
|
ret = _check_pass_icy(parser, pass);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = _check_pass_http(parser, user, pass);
|
|
|
|
if(!ret && ice_login)
|
|
|
|
{
|
|
|
|
ret = _check_pass_ice(parser, pass);
|
|
|
|
if(ret)
|
|
|
|
WARN0("Source is using deprecated icecast login");
|
|
|
|
}
|
2002-10-10 04:50:58 -04:00
|
|
|
}
|
2003-12-12 18:06:44 -05:00
|
|
|
config_release_config();
|
2002-10-10 04:50:58 -04:00
|
|
|
return ret;
|
2002-08-16 10:26:48 -04:00
|
|
|
}
|
|
|
|
|
2004-02-19 16:16:59 -05:00
|
|
|
|
2005-05-06 11:57:15 -04:00
|
|
|
static void _handle_source_request (client_t *client, char *uri, int auth_style)
|
2002-08-12 10:48:31 -04:00
|
|
|
{
|
2004-02-19 16:16:59 -05:00
|
|
|
source_t *source;
|
2002-08-16 10:26:48 -04:00
|
|
|
|
2002-08-12 10:48:31 -04:00
|
|
|
INFO1("Source logging in at mountpoint \"%s\"", uri);
|
2005-03-14 18:07:34 -05:00
|
|
|
|
2004-10-26 12:31:16 -04:00
|
|
|
if (uri[0] != '/')
|
|
|
|
{
|
|
|
|
WARN0 ("source mountpoint not starting with /");
|
|
|
|
client_send_401 (client);
|
|
|
|
return;
|
|
|
|
}
|
2004-11-11 10:47:33 -05:00
|
|
|
if (auth_style == ICECAST_SOURCE_AUTH) {
|
2005-05-06 11:57:15 -04:00
|
|
|
if (connection_check_source_pass (client->parser, uri) == 0)
|
|
|
|
{
|
2004-11-11 10:47:33 -05:00
|
|
|
/* We commonly get this if the source client is using the wrong
|
|
|
|
* protocol: attempt to diagnose this and return an error
|
|
|
|
*/
|
|
|
|
/* TODO: Do what the above comment says */
|
2005-04-18 10:32:26 -04:00
|
|
|
INFO1("Source (%s) attempted to login with invalid or missing password", uri);
|
2004-11-11 10:47:33 -05:00
|
|
|
client_send_401(client);
|
|
|
|
return;
|
|
|
|
}
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2004-02-19 16:16:59 -05:00
|
|
|
source = source_reserve (uri);
|
|
|
|
if (source)
|
|
|
|
{
|
2004-11-11 10:47:33 -05:00
|
|
|
if (auth_style == SHOUTCAST_SOURCE_AUTH) {
|
|
|
|
source->shoutcast_compat = 1;
|
|
|
|
}
|
2004-02-19 16:16:59 -05:00
|
|
|
source->client = client;
|
2005-05-06 11:57:15 -04:00
|
|
|
source->parser = client->parser;
|
|
|
|
source->con = client->con;
|
2004-02-19 16:16:59 -05:00
|
|
|
if (connection_complete_source (source) < 0)
|
|
|
|
{
|
|
|
|
source->client = NULL;
|
|
|
|
source_free_source (source);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
thread_create ("Source Thread", source_client_thread,
|
|
|
|
source, THREAD_DETACHED);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
client_send_404 (client, "Mountpoint in use");
|
2004-10-26 12:31:16 -04:00
|
|
|
WARN1 ("Mountpoint %s in use", uri);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
|
2004-02-19 16:16:59 -05:00
|
|
|
|
2005-05-06 11:57:15 -04:00
|
|
|
static void _handle_stats_request (client_t *client, char *uri)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
stats_event_inc(NULL, "stats_connections");
|
2005-05-06 11:57:15 -04:00
|
|
|
|
|
|
|
if (connection_check_admin_pass (client->parser) == 0)
|
|
|
|
{
|
|
|
|
client_send_401 (client);
|
2002-08-12 10:48:31 -04:00
|
|
|
ERROR0("Bad password for stats connection");
|
|
|
|
return;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-05-06 11:57:15 -04:00
|
|
|
|
2005-05-30 22:40:23 -04:00
|
|
|
client->respcode = 200;
|
|
|
|
if (sock_write (client->con->sock, "HTTP/1.0 200 OK\r\n\r\n") < 19)
|
|
|
|
{
|
|
|
|
client_destroy (client);
|
|
|
|
ERROR0 ("failed to write header");
|
|
|
|
return;
|
|
|
|
}
|
2005-05-06 11:57:15 -04:00
|
|
|
|
|
|
|
thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
|
2005-05-06 11:57:15 -04:00
|
|
|
static void _handle_get_request (client_t *client, char *passed_uri)
|
2002-08-12 10:48:31 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
source_t *source;
|
2003-03-05 08:03:35 -05:00
|
|
|
int fileserve;
|
|
|
|
int port;
|
2003-04-23 08:44:29 -04:00
|
|
|
int i;
|
2003-07-22 20:27:10 -04:00
|
|
|
char *serverhost = NULL;
|
|
|
|
int serverport = 0;
|
2003-04-23 08:44:29 -04:00
|
|
|
aliases *alias;
|
2003-03-05 08:03:35 -05:00
|
|
|
ice_config_t *config;
|
2004-05-17 00:33:46 -04:00
|
|
|
int ret;
|
2004-10-26 15:29:12 -04:00
|
|
|
char *uri = passed_uri;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
config = config_get_config();
|
|
|
|
fileserve = config->fileserve;
|
|
|
|
port = config->port;
|
2005-03-14 18:07:34 -05:00
|
|
|
for(i = 0; i < global.server_sockets; i++) {
|
2005-05-06 11:57:15 -04:00
|
|
|
if(global.serversock[i] == client->con->serversock) {
|
2003-04-23 08:44:29 -04:00
|
|
|
serverhost = config->listeners[i].bind_address;
|
|
|
|
serverport = config->listeners[i].port;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
alias = config->aliases;
|
2002-08-12 10:48:31 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* there are several types of HTTP GET clients
|
|
|
|
** media clients, which are looking for a source (eg, URI = /stream.ogg)
|
|
|
|
** stats clients, which are looking for /admin/stats.xml
|
2003-04-23 08:44:29 -04:00
|
|
|
** and directory server authorizers, which are looking for /GUID-xxxxxxxx
|
2002-08-12 10:48:31 -04:00
|
|
|
** (where xxxxxx is the GUID in question) - this isn't implemented yet.
|
2003-03-14 21:10:19 -05:00
|
|
|
** we need to handle the latter two before the former, as the latter two
|
|
|
|
** aren't subject to the limits.
|
|
|
|
*/
|
|
|
|
/* TODO: add GUID-xxxxxx */
|
2002-12-31 01:28:39 -05:00
|
|
|
|
2003-04-23 08:44:29 -04:00
|
|
|
/* Handle aliases */
|
|
|
|
while(alias) {
|
|
|
|
if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
|
2004-10-26 15:29:12 -04:00
|
|
|
uri = strdup (alias->destination);
|
|
|
|
DEBUG2 ("alias has made %s into %s", passed_uri, uri);
|
2003-04-23 08:44:29 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
alias = alias->next;
|
|
|
|
}
|
2004-10-26 15:29:12 -04:00
|
|
|
config_release_config();
|
|
|
|
|
|
|
|
stats_event_inc(NULL, "client_connections");
|
2003-04-23 08:44:29 -04:00
|
|
|
|
2003-03-06 09:17:33 -05:00
|
|
|
/* Dispatch all admin requests */
|
2004-11-11 10:47:33 -05:00
|
|
|
if ((strcmp(uri, "/admin.cgi") == 0) ||
|
|
|
|
(strncmp(uri, "/admin/", 7) == 0)) {
|
2003-03-06 09:17:33 -05:00
|
|
|
admin_handle_request(client, uri);
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2002-12-31 02:49:34 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* Here we are parsing the URI request to see
|
|
|
|
** if the extension is .xsl, if so, then process
|
|
|
|
** this request as an XSLT request
|
|
|
|
*/
|
2005-06-18 06:54:53 -04:00
|
|
|
if (util_check_valid_extension (uri) == XSLT_CONTENT)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
/* If the file exists, then transform it, otherwise, write a 404 */
|
2005-06-18 06:54:53 -04:00
|
|
|
DEBUG0("Stats request, sending XSL transformed stats");
|
|
|
|
stats_transform_xslt (client, uri);
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2002-08-12 10:48:31 -04:00
|
|
|
return;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-06-17 18:55:59 -04:00
|
|
|
|
|
|
|
if (fserve_client_create (client, uri))
|
2003-03-05 08:03:35 -05:00
|
|
|
{
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2002-08-18 01:06:58 -04:00
|
|
|
return;
|
|
|
|
}
|
2002-08-12 10:48:31 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
avl_tree_rlock(global.source_tree);
|
|
|
|
source = source_find_mount(uri);
|
|
|
|
if (source) {
|
2002-08-12 10:48:31 -04:00
|
|
|
DEBUG0("Source found for client");
|
2004-01-14 20:01:09 -05:00
|
|
|
|
|
|
|
/* The source may not be the requested source - it might have gone
|
|
|
|
* via one or more fallbacks. We only reject it for no-mount if it's
|
|
|
|
* the originally requested source
|
|
|
|
*/
|
|
|
|
if(strcmp(uri, source->mount) == 0 && source->no_mount) {
|
|
|
|
avl_tree_unlock(global.source_tree);
|
2004-02-20 12:42:57 -05:00
|
|
|
client_send_404(client, "This mount is unavailable.");
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2004-01-14 20:01:09 -05:00
|
|
|
return;
|
2004-02-19 10:24:06 -05:00
|
|
|
}
|
2005-06-08 21:51:47 -04:00
|
|
|
if (source->running == 0 && source->on_demand == 0)
|
2004-02-19 10:24:06 -05:00
|
|
|
{
|
|
|
|
avl_tree_unlock(global.source_tree);
|
|
|
|
DEBUG0("inactive source, client dropped");
|
|
|
|
client_send_404(client, "This mount is unavailable.");
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2004-02-19 10:24:06 -05:00
|
|
|
return;
|
2004-01-14 20:01:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for any required authentication first */
|
|
|
|
if(source->authenticator != NULL) {
|
2004-05-17 00:33:46 -04:00
|
|
|
ret = auth_check_client(source, client);
|
|
|
|
if(ret != AUTH_OK) {
|
2004-02-27 10:15:40 -05:00
|
|
|
avl_tree_unlock(global.source_tree);
|
2004-05-17 00:33:46 -04:00
|
|
|
if (ret == AUTH_FORBIDDEN) {
|
|
|
|
INFO1("Client attempted to log multiple times to source "
|
|
|
|
"(\"%s\")", uri);
|
|
|
|
client_send_403(client);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* If not FORBIDDEN, default to 401 */
|
|
|
|
INFO1("Client attempted to log in to source (\"%s\")with "
|
2004-01-14 20:01:09 -05:00
|
|
|
"incorrect or missing password", uri);
|
2004-05-17 00:33:46 -04:00
|
|
|
client_send_401(client);
|
|
|
|
}
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2004-01-14 20:01:09 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
global_lock();
|
2004-01-14 20:01:09 -05:00
|
|
|
/* Early-out for per-source max listeners. This gets checked again
|
|
|
|
* by the source itself, later. This route gives a useful message to
|
|
|
|
* the client, also.
|
|
|
|
*/
|
2005-05-06 11:57:15 -04:00
|
|
|
if (source->max_listeners != -1 &&
|
2005-05-25 23:04:48 -04:00
|
|
|
source->listeners >= (unsigned long)source->max_listeners)
|
2003-03-02 05:13:59 -05:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
global_unlock();
|
2003-03-02 05:13:59 -05:00
|
|
|
avl_tree_unlock(global.source_tree);
|
2004-02-20 12:42:57 -05:00
|
|
|
client_send_404(client,
|
|
|
|
"Too many clients on this mountpoint. Try again later.");
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2003-03-02 05:13:59 -05:00
|
|
|
return;
|
|
|
|
}
|
2003-03-14 21:10:19 -05:00
|
|
|
global_unlock();
|
|
|
|
|
|
|
|
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
|
2003-01-18 02:08:00 -05:00
|
|
|
sock_set_nodelay(client->con->sock);
|
2005-06-03 11:35:52 -04:00
|
|
|
|
2005-06-07 21:36:51 -04:00
|
|
|
client->write_to_client = format_generic_write_to_client;
|
|
|
|
client->check_buffer = format_check_http_buffer;
|
2005-08-07 10:50:59 -04:00
|
|
|
client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
|
2005-06-07 21:36:51 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
avl_tree_wlock(source->pending_tree);
|
|
|
|
avl_insert(source->pending_tree, (void *)client);
|
|
|
|
avl_tree_unlock(source->pending_tree);
|
2005-06-09 16:54:08 -04:00
|
|
|
stats_event_inc (NULL, "listener_connections");
|
2005-06-08 21:51:47 -04:00
|
|
|
|
|
|
|
if (source->running == 0 && source->on_demand)
|
|
|
|
{
|
|
|
|
/* enable on-demand relay to start, wake up the slave thread */
|
|
|
|
DEBUG0("kicking off on-demand relay");
|
|
|
|
source->on_demand_req = 1;
|
|
|
|
slave_rescan ();
|
|
|
|
}
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
avl_tree_unlock(global.source_tree);
|
|
|
|
|
|
|
|
if (!source) {
|
2002-08-12 10:48:31 -04:00
|
|
|
DEBUG0("Source not found for client");
|
|
|
|
client_send_404(client, "The source you requested could not be found.");
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
|
2004-11-17 11:02:04 -05:00
|
|
|
void _handle_shoutcast_compatible(connection_t *con, char *mount, char *source_password) {
|
2004-11-11 10:47:33 -05:00
|
|
|
char shoutcast_password[256];
|
|
|
|
char *http_compliant;
|
|
|
|
int http_compliant_len = 0;
|
|
|
|
char header[4096];
|
|
|
|
http_parser_t *parser;
|
|
|
|
|
|
|
|
memset(shoutcast_password, 0, sizeof (shoutcast_password));
|
|
|
|
/* Step one of shoutcast auth protocol, read encoder password (1 line) */
|
|
|
|
if (util_read_header(con->sock, shoutcast_password,
|
|
|
|
sizeof (shoutcast_password),
|
|
|
|
READ_LINE) == 0) {
|
|
|
|
/* either we didn't get a complete line, or we timed out */
|
|
|
|
connection_close(con);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Get rid of trailing \n */
|
|
|
|
shoutcast_password[strlen(shoutcast_password)-1] = '\000';
|
|
|
|
if (strcmp(shoutcast_password, source_password)) {
|
|
|
|
ERROR0("Invalid source password");
|
|
|
|
connection_close(con);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Step two of shoutcast auth protocol, send OK2. For those
|
|
|
|
interested, OK2 means it supports metadata updates via admin.cgi,
|
|
|
|
and the string "OK" can also be sent, but will indicate to the
|
|
|
|
shoutcast source client to not send metadata updates.
|
|
|
|
I believe icecast 1.x used to send OK. */
|
|
|
|
sock_write(con->sock, "%s\r\n", "OK2");
|
|
|
|
|
|
|
|
memset(header, 0, sizeof (header));
|
|
|
|
/* Step three of shoutcast auth protocol, read HTTP-style
|
|
|
|
request headers and process them.*/
|
|
|
|
if (util_read_header(con->sock, header, sizeof (header),
|
|
|
|
READ_ENTIRE_HEADER) == 0) {
|
|
|
|
/* either we didn't get a complete header, or we timed out */
|
|
|
|
connection_close(con);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* 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 */
|
2004-11-17 11:02:04 -05:00
|
|
|
http_compliant_len = strlen(header) + strlen(mount) + 20;
|
2004-11-11 10:47:33 -05:00
|
|
|
http_compliant = (char *)calloc(1, http_compliant_len);
|
2004-11-17 11:02:04 -05:00
|
|
|
snprintf (http_compliant, http_compliant_len,
|
|
|
|
"SOURCE %s HTTP/1.0\r\n%s", mount, header);
|
2004-11-11 10:47:33 -05:00
|
|
|
parser = httpp_create_parser();
|
|
|
|
httpp_initialize(parser, NULL);
|
2005-05-06 11:57:15 -04:00
|
|
|
if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
|
|
|
|
{
|
|
|
|
client_t *client = client_create (con, parser);
|
|
|
|
if (client)
|
|
|
|
{
|
|
|
|
_handle_source_request (client, mount, SHOUTCAST_SOURCE_AUTH);
|
|
|
|
free (http_compliant);
|
|
|
|
return;
|
|
|
|
}
|
2004-11-11 10:47:33 -05:00
|
|
|
}
|
2005-05-06 11:57:15 -04:00
|
|
|
connection_close (con);
|
|
|
|
httpp_destroy (parser);
|
|
|
|
free (http_compliant);
|
2004-11-11 10:47:33 -05:00
|
|
|
}
|
|
|
|
|
2002-08-12 10:48:31 -04:00
|
|
|
static void *_handle_connection(void *arg)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
char header[4096];
|
|
|
|
connection_t *con;
|
|
|
|
http_parser_t *parser;
|
2002-08-11 10:23:39 -04:00
|
|
|
char *rawuri, *uri;
|
2003-03-14 21:10:19 -05:00
|
|
|
client_t *client;
|
2004-11-11 10:47:33 -05:00
|
|
|
int i = 0;
|
|
|
|
int continue_flag = 0;
|
|
|
|
ice_config_t *config;
|
|
|
|
char *source_password;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
while (global.running == ICE_RUNNING) {
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* grab a connection and set the socket to blocking */
|
|
|
|
while ((con = _get_connection())) {
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
/* Handle meta-connections */
|
|
|
|
if(con->event_number > 0) {
|
|
|
|
switch(con->event_number) {
|
|
|
|
case EVENT_CONFIG_READ:
|
|
|
|
event_config_read(con->event);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERROR1("Unknown event number: %d", con->event_number);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(con);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
stats_event_inc(NULL, "connections");
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_set_blocking(con->sock, SOCK_BLOCK);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2004-11-11 10:47:33 -05:00
|
|
|
continue_flag = 0;
|
|
|
|
/* Check for special shoutcast compatability processing */
|
|
|
|
for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
|
|
|
|
if(global.serversock[i] == con->serversock) {
|
|
|
|
config = config_get_config();
|
|
|
|
if (config->listeners[i].shoutcast_compat) {
|
2004-11-17 11:02:04 -05:00
|
|
|
char *shoutcast_mount = strdup (config->shoutcast_mount);
|
2005-05-08 07:54:46 -04:00
|
|
|
mount_proxy *mountinfo = config_find_mount (config, config->shoutcast_mount);
|
|
|
|
if (mountinfo && mountinfo->password)
|
|
|
|
source_password = strdup (mountinfo->password);
|
|
|
|
else
|
|
|
|
source_password = strdup (config->source_password);
|
2004-11-11 10:47:33 -05:00
|
|
|
config_release_config();
|
2004-11-17 11:02:04 -05:00
|
|
|
_handle_shoutcast_compatible(con, shoutcast_mount, source_password);
|
2004-11-11 10:47:33 -05:00
|
|
|
free(source_password);
|
2004-11-17 11:02:04 -05:00
|
|
|
free (shoutcast_mount);
|
2004-11-11 10:47:33 -05:00
|
|
|
continue_flag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
config_release_config();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(continue_flag) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
/* fill header with the http header */
|
2004-10-26 12:31:16 -04:00
|
|
|
memset(header, 0, sizeof (header));
|
2004-11-11 10:47:33 -05:00
|
|
|
if (util_read_header(con->sock, header, sizeof (header),
|
|
|
|
READ_ENTIRE_HEADER) == 0) {
|
2003-03-14 21:10:19 -05:00
|
|
|
/* either we didn't get a complete header, or we timed out */
|
|
|
|
connection_close(con);
|
|
|
|
continue;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
parser = httpp_create_parser();
|
|
|
|
httpp_initialize(parser, NULL);
|
|
|
|
if (httpp_parse(parser, header, strlen(header))) {
|
|
|
|
/* handle the connection or something */
|
|
|
|
|
|
|
|
if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
|
2002-08-12 10:48:31 -04:00
|
|
|
strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
|
2002-08-09 04:06:00 -04:00
|
|
|
ERROR0("Bad HTTP protocol detected");
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_close(con);
|
|
|
|
httpp_destroy(parser);
|
|
|
|
continue;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2002-08-11 10:23:39 -04:00
|
|
|
rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
|
|
|
|
uri = util_normalise_uri(rawuri);
|
|
|
|
|
2005-05-06 11:57:15 -04:00
|
|
|
if (uri == NULL)
|
|
|
|
{
|
|
|
|
sock_write(con->sock, "The path you requested was invalid\r\n");
|
|
|
|
connection_close(con);
|
|
|
|
httpp_destroy(parser);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
client = client_create (con, parser);
|
|
|
|
if (client == NULL)
|
|
|
|
{
|
|
|
|
sock_write (con->sock, "HTTP/1.0 404 File Not Found\r\n"
|
|
|
|
"Content-Type: text/html\r\n\r\n"
|
|
|
|
"<b>Connection limit reached</b>");
|
|
|
|
connection_close(con);
|
|
|
|
httpp_destroy(parser);
|
2002-08-11 10:23:39 -04:00
|
|
|
continue;
|
|
|
|
}
|
2002-08-10 04:01:56 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (parser->req_type == httpp_req_source) {
|
2005-05-06 11:57:15 -04:00
|
|
|
_handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
else if (parser->req_type == httpp_req_stats) {
|
2005-05-06 11:57:15 -04:00
|
|
|
_handle_stats_request (client, uri);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
else if (parser->req_type == httpp_req_get) {
|
2005-05-06 11:57:15 -04:00
|
|
|
_handle_get_request (client, uri);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
else {
|
2002-08-09 04:06:00 -04:00
|
|
|
ERROR0("Wrong request type from client");
|
2005-05-06 11:57:15 -04:00
|
|
|
client_send_400 (client, "unknown request");
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
free(uri);
|
2004-10-26 12:31:16 -04:00
|
|
|
continue;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2003-03-07 23:57:02 -05:00
|
|
|
else {
|
2002-08-09 04:06:00 -04:00
|
|
|
ERROR0("HTTP request parsing failed");
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_close(con);
|
|
|
|
httpp_destroy(parser);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2004-10-26 12:31:16 -04:00
|
|
|
thread_sleep (100000);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2004-10-26 12:31:16 -04:00
|
|
|
DEBUG0 ("Connection thread done");
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void connection_close(connection_t *con)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_close(con->sock);
|
|
|
|
if (con->ip) free(con->ip);
|
|
|
|
if (con->host) free(con->host);
|
|
|
|
free(con);
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|