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).
|
2011-11-25 21:39:30 -05:00
|
|
|
* Copyright 2011, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
|
|
|
* Dave 'justdave' Miller <justdave@mozilla.com>.
|
2004-01-28 20:02:12 -05:00
|
|
|
*/
|
|
|
|
|
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>
|
2007-11-15 10:12:21 -05:00
|
|
|
#include <errno.h>
|
2001-09-09 22:21:46 -04:00
|
|
|
#include <string.h>
|
2003-03-09 06:27:06 -05:00
|
|
|
#ifdef HAVE_POLL
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#endif
|
2007-10-23 18:25:31 -04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2001-10-20 02:43:04 -04:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
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
|
|
|
|
2005-12-17 07:41:34 -05:00
|
|
|
#include "compat.h"
|
2001-09-09 22:21:46 -04:00
|
|
|
|
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
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
typedef struct client_queue_tag {
|
|
|
|
client_t *client;
|
|
|
|
int offset;
|
|
|
|
int stream_offset;
|
|
|
|
int shoutcast;
|
2007-10-24 22:25:49 -04:00
|
|
|
char *shoutcast_mount;
|
2005-08-11 19:29:58 -04:00
|
|
|
struct client_queue_tag *next;
|
|
|
|
} client_queue_t;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *filename;
|
|
|
|
time_t file_recheck;
|
|
|
|
time_t file_mtime;
|
|
|
|
avl_tree *contents;
|
|
|
|
} cache_file_contents;
|
|
|
|
|
2009-01-08 22:18:03 -05:00
|
|
|
static spin_t _connection_lock;
|
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;
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
|
|
|
|
static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
|
2007-08-28 23:51:22 -04:00
|
|
|
static int ssl_ok;
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
static SSL_CTX *ssl_ctx;
|
|
|
|
#endif
|
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
/* filtering client connection based on IP */
|
|
|
|
cache_file_contents banned_ip, allowed_ip;
|
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
rwlock_t _source_shutdown_rwlock;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2008-07-21 22:24:30 -04:00
|
|
|
static void _handle_connection(void);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
static int compare_ip (void *arg, void *a, void *b)
|
|
|
|
{
|
|
|
|
const char *ip = (const char *)a;
|
|
|
|
const char *pattern = (const char *)b;
|
|
|
|
|
|
|
|
return strcmp (pattern, ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int free_filtered_ip (void*x)
|
|
|
|
{
|
|
|
|
free (x);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-09-09 22:21:46 -04:00
|
|
|
void connection_initialize(void)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
if (_initialized) return;
|
|
|
|
|
2009-01-08 22:18:03 -05:00
|
|
|
thread_spin_create (&_connection_lock);
|
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);
|
2006-03-14 21:30:26 -05:00
|
|
|
_req_queue = NULL;
|
|
|
|
_req_queue_tail = &_req_queue;
|
|
|
|
_con_queue = NULL;
|
|
|
|
_con_queue_tail = &_con_queue;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
banned_ip.contents = NULL;
|
|
|
|
banned_ip.file_mtime = 0;
|
|
|
|
|
|
|
|
allowed_ip.contents = NULL;
|
|
|
|
allowed_ip.file_mtime = 0;
|
|
|
|
|
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;
|
|
|
|
|
2007-08-28 23:51:22 -04:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
SSL_CTX_free (ssl_ctx);
|
|
|
|
#endif
|
2007-10-23 18:25:31 -04:00
|
|
|
if (banned_ip.contents) avl_tree_free (banned_ip.contents, free_filtered_ip);
|
|
|
|
if (allowed_ip.contents) avl_tree_free (allowed_ip.contents, free_filtered_ip);
|
|
|
|
|
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);
|
2009-01-08 22:18:03 -05:00
|
|
|
thread_spin_destroy (&_connection_lock);
|
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
|
|
|
|
2009-01-08 22:18:03 -05:00
|
|
|
thread_spin_lock (&_connection_lock);
|
2003-03-14 21:10:19 -05:00
|
|
|
id = _current_id++;
|
2009-01-08 22:18:03 -05:00
|
|
|
thread_spin_unlock (&_connection_lock);
|
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
|
|
|
}
|
|
|
|
|
2007-08-28 23:51:22 -04:00
|
|
|
|
|
|
|
#ifdef HAVE_OPENSSL
|
2007-10-21 22:29:49 -04:00
|
|
|
static void get_ssl_certificate (ice_config_t *config)
|
2007-08-28 23:51:22 -04:00
|
|
|
{
|
|
|
|
SSL_METHOD *method;
|
2011-11-25 17:12:11 -05:00
|
|
|
long ssl_opts;
|
2007-08-28 23:51:22 -04:00
|
|
|
ssl_ok = 0;
|
|
|
|
|
|
|
|
SSL_load_error_strings(); /* readable error messages */
|
|
|
|
SSL_library_init(); /* initialize library */
|
|
|
|
|
|
|
|
method = SSLv23_server_method();
|
|
|
|
ssl_ctx = SSL_CTX_new (method);
|
2011-11-25 17:12:11 -05:00
|
|
|
ssl_opts = SSL_CTX_get_options (ssl_ctx);
|
|
|
|
SSL_CTX_set_options (ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2);
|
2007-08-28 23:51:22 -04:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (config->cert_file == NULL)
|
|
|
|
break;
|
2011-11-25 17:12:11 -05:00
|
|
|
if (SSL_CTX_use_certificate_chain_file (ssl_ctx, config->cert_file) <= 0)
|
2007-08-28 23:51:22 -04:00
|
|
|
{
|
|
|
|
WARN1 ("Invalid cert file %s", config->cert_file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (SSL_CTX_use_PrivateKey_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0)
|
|
|
|
{
|
|
|
|
WARN1 ("Invalid private key file %s", config->cert_file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!SSL_CTX_check_private_key (ssl_ctx))
|
|
|
|
{
|
2007-10-21 22:29:49 -04:00
|
|
|
ERROR1 ("Invalid %s - Private key does not match cert public key", config->cert_file);
|
2007-08-28 23:51:22 -04:00
|
|
|
break;
|
|
|
|
}
|
2011-11-25 17:12:11 -05:00
|
|
|
if (SSL_CTX_set_cipher_list(ssl_ctx, config->cipher_list) <= 0)
|
|
|
|
{
|
|
|
|
WARN1 ("Invalid cipher list: %s", config->cipher_list);
|
|
|
|
}
|
2007-08-28 23:51:22 -04:00
|
|
|
ssl_ok = 1;
|
|
|
|
INFO1 ("SSL certificate found at %s", config->cert_file);
|
2011-11-25 17:12:11 -05:00
|
|
|
INFO1 ("SSL using ciphers %s", config->cipher_list);
|
2007-10-21 22:29:49 -04:00
|
|
|
return;
|
2007-08-28 23:51:22 -04:00
|
|
|
} while (0);
|
2007-10-21 22:29:49 -04:00
|
|
|
INFO0 ("No SSL capability on any configured ports");
|
2007-08-28 23:51:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handlers for reading and writing a connection_t when there is ssl
|
|
|
|
* configured on the listening port
|
|
|
|
*/
|
|
|
|
static int connection_read_ssl (connection_t *con, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
int bytes = SSL_read (con->ssl, buf, len);
|
|
|
|
|
|
|
|
if (bytes < 0)
|
|
|
|
{
|
|
|
|
switch (SSL_get_error (con->ssl, bytes))
|
|
|
|
{
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
con->error = 1;
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int connection_send_ssl (connection_t *con, const void *buf, size_t len)
|
|
|
|
{
|
|
|
|
int bytes = SSL_write (con->ssl, buf, len);
|
|
|
|
|
|
|
|
if (bytes < 0)
|
|
|
|
{
|
|
|
|
switch (SSL_get_error (con->ssl, bytes))
|
|
|
|
{
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
con->error = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
con->sent_bytes += bytes;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* SSL not compiled in, so at least log it */
|
2007-10-21 22:29:49 -04:00
|
|
|
static void get_ssl_certificate (ice_config_t *config)
|
2007-08-28 23:51:22 -04:00
|
|
|
{
|
|
|
|
ssl_ok = 0;
|
|
|
|
INFO0 ("No SSL capability");
|
|
|
|
}
|
|
|
|
#endif /* HAVE_OPENSSL */
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
/* function to handle the re-populating of the avl tree containing IP addresses
|
|
|
|
* for deciding whether a connection of an incoming request is to be dropped.
|
|
|
|
*/
|
|
|
|
static void recheck_ip_file (cache_file_contents *cache)
|
|
|
|
{
|
|
|
|
time_t now = time(NULL);
|
|
|
|
if (now >= cache->file_recheck)
|
|
|
|
{
|
|
|
|
struct stat file_stat;
|
|
|
|
FILE *file = NULL;
|
|
|
|
int count = 0;
|
|
|
|
avl_tree *new_ips;
|
|
|
|
char line [MAX_LINE_LEN];
|
|
|
|
|
|
|
|
cache->file_recheck = now + 10;
|
|
|
|
if (cache->filename == NULL)
|
|
|
|
{
|
|
|
|
if (cache->contents)
|
|
|
|
{
|
|
|
|
avl_tree_free (cache->contents, free_filtered_ip);
|
|
|
|
cache->contents = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (stat (cache->filename, &file_stat) < 0)
|
|
|
|
{
|
|
|
|
WARN2 ("failed to check status of \"%s\": %s", cache->filename, strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (file_stat.st_mtime == cache->file_mtime)
|
|
|
|
return; /* common case, no update to file */
|
|
|
|
|
|
|
|
cache->file_mtime = file_stat.st_mtime;
|
|
|
|
|
|
|
|
file = fopen (cache->filename, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
{
|
|
|
|
WARN2("Failed to open file \"%s\": %s", cache->filename, strerror (errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_ips = avl_tree_new (compare_ip, NULL);
|
|
|
|
|
|
|
|
while (get_line (file, line, MAX_LINE_LEN))
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
if(!line[0] || line[0] == '#')
|
|
|
|
continue;
|
|
|
|
count++;
|
|
|
|
str = strdup (line);
|
|
|
|
if (str)
|
|
|
|
avl_insert (new_ips, str);
|
|
|
|
}
|
|
|
|
fclose (file);
|
|
|
|
INFO2 ("%d entries read from file \"%s\"", count, cache->filename);
|
|
|
|
|
|
|
|
if (cache->contents) avl_tree_free (cache->contents, free_filtered_ip);
|
|
|
|
cache->contents = new_ips;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* return 0 if the passed ip address is not to be handled by icecast, non-zero otherwise */
|
|
|
|
static int accept_ip_address (char *ip)
|
|
|
|
{
|
|
|
|
void *result;
|
|
|
|
|
|
|
|
recheck_ip_file (&banned_ip);
|
|
|
|
recheck_ip_file (&allowed_ip);
|
|
|
|
|
|
|
|
if (banned_ip.contents)
|
|
|
|
{
|
|
|
|
if (avl_get_by_key (banned_ip.contents, ip, &result) == 0)
|
|
|
|
{
|
|
|
|
DEBUG1 ("%s is banned", ip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (allowed_ip.contents)
|
|
|
|
{
|
|
|
|
if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0)
|
|
|
|
{
|
|
|
|
DEBUG1 ("%s is allowed", ip);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG1 ("%s is not allowed", ip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
|
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_t *con;
|
2005-08-11 19:29:58 -04:00
|
|
|
con = (connection_t *)calloc(1, sizeof(connection_t));
|
|
|
|
if (con)
|
|
|
|
{
|
|
|
|
con->sock = sock;
|
|
|
|
con->serversock = serversock;
|
|
|
|
con->con_time = time(NULL);
|
|
|
|
con->id = _next_connection_id();
|
|
|
|
con->ip = ip;
|
2007-08-28 23:51:22 -04:00
|
|
|
con->read = connection_read;
|
|
|
|
con->send = connection_send;
|
2005-08-11 19:29:58 -04:00
|
|
|
}
|
2003-03-05 08:03:35 -05:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
return con;
|
2002-08-05 10:48:04 -04:00
|
|
|
}
|
|
|
|
|
2007-08-28 23:51:22 -04:00
|
|
|
/* prepare connection for interacting over a SSL connection
|
|
|
|
*/
|
|
|
|
void connection_uses_ssl (connection_t *con)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
con->read = connection_read_ssl;
|
|
|
|
con->send = connection_send_ssl;
|
|
|
|
con->ssl = SSL_new (ssl_ctx);
|
|
|
|
SSL_set_accept_state (con->ssl);
|
|
|
|
SSL_set_fd (con->ssl, con->sock);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2007-10-24 18:42:49 -04:00
|
|
|
static sock_t wait_for_serversock(int timeout)
|
2003-03-09 06:27:06 -05:00
|
|
|
{
|
|
|
|
#ifdef HAVE_POLL
|
2007-10-21 22:29:49 -04:00
|
|
|
struct pollfd ufds [global.server_sockets];
|
2003-03-09 06:27:06 -05:00
|
|
|
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) {
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
else if(ret == 0) {
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
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))
|
|
|
|
{
|
2007-10-24 18:42:49 -04:00
|
|
|
sock_close (global.serversock[i]);
|
2003-06-20 14:50:24 -04:00
|
|
|
WARN0("Had to close a listening socket");
|
|
|
|
}
|
2007-10-24 18:42:49 -04:00
|
|
|
global.serversock[i] = SOCK_ERROR;
|
2003-06-20 14:50:24 -04:00
|
|
|
}
|
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++)
|
|
|
|
{
|
2007-10-24 18:42:49 -04:00
|
|
|
if (global.serversock[i] == SOCK_ERROR)
|
2003-06-20 14:50:24 -04:00
|
|
|
continue;
|
|
|
|
if (i!=dst)
|
|
|
|
global.serversock[dst] = global.serversock[i];
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
global.server_sockets = dst;
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
fd_set rfds;
|
|
|
|
struct timeval tv, *p=NULL;
|
|
|
|
int i, ret;
|
2007-10-24 18:42:49 -04:00
|
|
|
sock_t max = SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
|
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
|
|
|
FD_SET(global.serversock[i], &rfds);
|
2007-10-24 18:42:49 -04:00
|
|
|
if (max == SOCK_ERROR || global.serversock[i] > max)
|
2003-03-09 06:27:06 -05:00
|
|
|
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) {
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
else if(ret == 0) {
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR;
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
for(i=0; i < global.server_sockets; i++) {
|
|
|
|
if(FD_ISSET(global.serversock[i], &rfds))
|
|
|
|
return global.serversock[i];
|
|
|
|
}
|
2007-10-24 18:42:49 -04:00
|
|
|
return SOCK_ERROR; /* Should be impossible, stop compiler warnings */
|
2003-03-09 06:27:06 -05:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-07-21 22:24:30 -04:00
|
|
|
static connection_t *_accept_connection(int duration)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2008-07-21 22:24:30 -04:00
|
|
|
sock_t sock, serversock;
|
2003-03-14 21:10:19 -05:00
|
|
|
char *ip;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2008-07-21 22:24:30 -04:00
|
|
|
serversock = wait_for_serversock (duration);
|
2007-10-24 18:42:49 -04:00
|
|
|
if (serversock == SOCK_ERROR)
|
2003-03-09 06:27:06 -05:00
|
|
|
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);
|
2007-10-24 18:42:49 -04:00
|
|
|
if (sock != SOCK_ERROR)
|
2005-08-11 19:29:58 -04:00
|
|
|
{
|
2007-10-23 18:25:31 -04:00
|
|
|
connection_t *con = NULL;
|
2007-08-08 23:36:03 -04:00
|
|
|
/* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
|
|
|
|
if (strncmp (ip, "::ffff:", 7) == 0)
|
|
|
|
memmove (ip, ip+7, strlen (ip+7)+1);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
if (accept_ip_address (ip))
|
|
|
|
con = connection_create (sock, serversock, ip);
|
2007-08-08 23:36:03 -04:00
|
|
|
if (con)
|
|
|
|
return con;
|
|
|
|
sock_close (sock);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!sock_recoverable(sock_error()))
|
|
|
|
{
|
|
|
|
WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
|
|
|
|
thread_sleep (500000);
|
|
|
|
}
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
|
|
|
free(ip);
|
|
|
|
return NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* add client to connection queue. At this point some header information
|
|
|
|
* has been collected, so we now pass it onto the connection thread for
|
|
|
|
* further processing
|
|
|
|
*/
|
|
|
|
static void _add_connection (client_queue_t *node)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2005-08-11 19:29:58 -04:00
|
|
|
*_con_queue_tail = node;
|
|
|
|
_con_queue_tail = (volatile client_queue_t **)&node->next;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* this returns queued clients for the connection thread. headers are
|
|
|
|
* already provided, but need to be parsed.
|
|
|
|
*/
|
|
|
|
static client_queue_t *_get_connection(void)
|
|
|
|
{
|
|
|
|
client_queue_t *node = NULL;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* common case, no new connections so don't bother taking locks */
|
|
|
|
if (_con_queue)
|
|
|
|
{
|
|
|
|
node = (client_queue_t *)_con_queue;
|
|
|
|
_con_queue = node->next;
|
|
|
|
if (_con_queue == NULL)
|
|
|
|
_con_queue_tail = &_con_queue;
|
2007-08-08 22:51:53 -04:00
|
|
|
node->next = NULL;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
return node;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* run along queue checking for any data that has come in or a timeout */
|
2005-12-17 07:23:09 -05:00
|
|
|
static void process_request_queue (void)
|
2005-08-11 19:29:58 -04:00
|
|
|
{
|
|
|
|
client_queue_t **node_ref = (client_queue_t **)&_req_queue;
|
|
|
|
ice_config_t *config = config_get_config ();
|
|
|
|
int timeout = config->header_timeout;
|
|
|
|
config_release_config();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
while (*node_ref)
|
|
|
|
{
|
|
|
|
client_queue_t *node = *node_ref;
|
|
|
|
client_t *client = node->client;
|
|
|
|
int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
|
|
|
|
char *buf = client->refbuf->data + node->offset;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
if (client->con->con_time + timeout <= time(NULL))
|
|
|
|
len = 0;
|
|
|
|
else
|
|
|
|
len = client_read_bytes (client, buf, len);
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
int pass_it = 1;
|
|
|
|
char *ptr;
|
|
|
|
|
2005-10-01 10:08:36 -04:00
|
|
|
/* handle \n, \r\n and nsvcap which for some strange reason has
|
|
|
|
* EOL as \r\r\n */
|
2005-08-11 19:29:58 -04:00
|
|
|
node->offset += len;
|
|
|
|
client->refbuf->data [node->offset] = '\000';
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (node->shoutcast == 1)
|
|
|
|
{
|
|
|
|
/* password line */
|
2005-10-01 10:08:36 -04:00
|
|
|
if (strstr (client->refbuf->data, "\r\r\n") != NULL)
|
|
|
|
break;
|
2005-08-11 19:29:58 -04:00
|
|
|
if (strstr (client->refbuf->data, "\r\n") != NULL)
|
|
|
|
break;
|
|
|
|
if (strstr (client->refbuf->data, "\n") != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* stream_offset refers to the start of any data sent after the
|
|
|
|
* http style headers, we don't want to lose those */
|
2005-10-01 10:08:36 -04:00
|
|
|
ptr = strstr (client->refbuf->data, "\r\r\n\r\r\n");
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
node->stream_offset = (ptr+6) - client->refbuf->data;
|
|
|
|
break;
|
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
ptr = strstr (client->refbuf->data, "\r\n\r\n");
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
node->stream_offset = (ptr+4) - client->refbuf->data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ptr = strstr (client->refbuf->data, "\n\n");
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
node->stream_offset = (ptr+2) - client->refbuf->data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pass_it = 0;
|
|
|
|
} while (0);
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
if (pass_it)
|
|
|
|
{
|
|
|
|
if ((client_queue_t **)_req_queue_tail == &(node->next))
|
|
|
|
_req_queue_tail = (volatile client_queue_t **)node_ref;
|
|
|
|
*node_ref = node->next;
|
|
|
|
node->next = NULL;
|
|
|
|
_add_connection (node);
|
2006-03-14 21:30:26 -05:00
|
|
|
continue;
|
2005-08-11 19:29:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (len == 0 || client->con->error)
|
|
|
|
{
|
|
|
|
if ((client_queue_t **)_req_queue_tail == &node->next)
|
|
|
|
_req_queue_tail = (volatile client_queue_t **)node_ref;
|
|
|
|
*node_ref = node->next;
|
|
|
|
client_destroy (client);
|
|
|
|
free (node);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node_ref = &node->next;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2008-07-21 22:24:30 -04:00
|
|
|
_handle_connection();
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* add node to the queue of requests. This is where the clients are when
|
|
|
|
* initial http details are read.
|
|
|
|
*/
|
|
|
|
static void _add_request_queue (client_queue_t *node)
|
|
|
|
{
|
|
|
|
*_req_queue_tail = node;
|
|
|
|
_req_queue_tail = (volatile client_queue_t **)&node->next;
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
|
2008-07-21 22:24:30 -04:00
|
|
|
void connection_accept_loop (void)
|
2001-09-09 22:21:46 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_t *con;
|
2007-10-21 22:29:49 -04:00
|
|
|
ice_config_t *config;
|
2008-07-21 22:24:30 -04:00
|
|
|
int duration = 300;
|
2007-10-21 22:29:49 -04:00
|
|
|
|
|
|
|
config = config_get_config ();
|
|
|
|
get_ssl_certificate (config);
|
|
|
|
config_release_config ();
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2003-07-24 19:45:29 -04:00
|
|
|
while (global.running == ICE_RUNNING)
|
|
|
|
{
|
2008-07-21 22:24:30 -04:00
|
|
|
con = _accept_connection (duration);
|
2005-08-11 19:29:58 -04:00
|
|
|
|
|
|
|
if (con)
|
2003-07-24 19:45:29 -04:00
|
|
|
{
|
2005-08-11 19:29:58 -04:00
|
|
|
client_queue_t *node;
|
|
|
|
ice_config_t *config;
|
|
|
|
client_t *client = NULL;
|
2007-10-15 21:53:06 -04:00
|
|
|
listener_t *listener;
|
2003-07-24 19:45:29 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
global_lock();
|
|
|
|
if (client_create (&client, con, NULL) < 0)
|
|
|
|
{
|
|
|
|
global_unlock();
|
2005-11-16 19:54:28 -05:00
|
|
|
client_send_403 (client, "Icecast connection limit reached");
|
2007-10-21 22:29:49 -04:00
|
|
|
/* don't be too eager as this is an imposed hard limit */
|
|
|
|
thread_sleep (400000);
|
2005-08-11 19:29:58 -04:00
|
|
|
continue;
|
|
|
|
}
|
2003-07-24 19:45:29 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* setup client for reading incoming http */
|
|
|
|
client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000';
|
|
|
|
|
2009-01-07 21:47:44 -05:00
|
|
|
if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay (client->con->sock))
|
|
|
|
{
|
2009-01-11 11:46:08 -05:00
|
|
|
global_unlock();
|
2009-01-07 21:47:44 -05:00
|
|
|
WARN0 ("failed to set tcp options on client connection, dropping");
|
|
|
|
client_destroy (client);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
node = calloc (1, sizeof (client_queue_t));
|
|
|
|
if (node == NULL)
|
|
|
|
{
|
2009-01-11 11:46:08 -05:00
|
|
|
global_unlock();
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
node->client = client;
|
|
|
|
|
|
|
|
config = config_get_config();
|
2007-10-15 21:53:06 -04:00
|
|
|
listener = config_get_listen_sock (config, client->con);
|
|
|
|
|
|
|
|
if (listener)
|
2005-08-11 19:29:58 -04:00
|
|
|
{
|
2007-10-15 21:53:06 -04:00
|
|
|
if (listener->shoutcast_compat)
|
|
|
|
node->shoutcast = 1;
|
|
|
|
if (listener->ssl && ssl_ok)
|
|
|
|
connection_uses_ssl (client->con);
|
2007-10-24 22:25:49 -04:00
|
|
|
if (listener->shoutcast_mount)
|
|
|
|
node->shoutcast_mount = strdup (listener->shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
}
|
2009-01-11 11:46:08 -05:00
|
|
|
global_unlock();
|
2007-08-28 23:51:22 -04:00
|
|
|
config_release_config();
|
2005-08-11 19:29:58 -04:00
|
|
|
|
|
|
|
_add_request_queue (node);
|
|
|
|
stats_event_inc (NULL, "connections");
|
2008-07-21 22:24:30 -04:00
|
|
|
duration = 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_req_queue == NULL)
|
|
|
|
duration = 300; /* use longer timeouts when nothing waiting */
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
process_request_queue ();
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
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
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
2005-08-24 20:07:17 -04:00
|
|
|
int connection_complete_source (source_t *source, int response)
|
2004-02-19 11:32:26 -05:00
|
|
|
{
|
2009-01-11 11:46:08 -05:00
|
|
|
ice_config_t *config;
|
2004-02-19 11:32:26 -05:00
|
|
|
|
|
|
|
global_lock ();
|
|
|
|
DEBUG1 ("sources count is %d", global.sources);
|
|
|
|
|
2009-01-11 11:46:08 -05:00
|
|
|
config = config_get_config();
|
2004-02-19 11:32:26 -05:00
|
|
|
if (global.sources < config->source_limit)
|
|
|
|
{
|
2007-08-16 18:49:13 -04:00
|
|
|
const 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)
|
|
|
|
{
|
|
|
|
config_release_config();
|
2009-01-11 11:46:08 -05:00
|
|
|
global_unlock();
|
2005-08-24 20:07:17 -04:00
|
|
|
if (response)
|
|
|
|
{
|
2005-11-16 19:54:28 -05:00
|
|
|
client_send_403 (source->client, "Content-type not supported");
|
2005-08-24 20:07:17 -04:00
|
|
|
source->client = NULL;
|
|
|
|
}
|
2004-02-19 11:32:26 -05:00
|
|
|
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();
|
2005-08-24 20:07:17 -04:00
|
|
|
if (response)
|
|
|
|
{
|
2005-11-16 19:54:28 -05:00
|
|
|
client_send_403 (source->client, "internal format allocation problem");
|
2005-08-24 20:07:17 -04:00
|
|
|
source->client = NULL;
|
|
|
|
}
|
2004-02-19 11:32:26 -05:00
|
|
|
WARN1 ("plugin format failed for \"%s\"", source->mount);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
global.sources++;
|
|
|
|
stats_event_args (NULL, "sources", "%d", global.sources);
|
|
|
|
global_unlock();
|
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);
|
2007-08-11 13:44:45 -04:00
|
|
|
source_update_settings (config, source, mountinfo);
|
2004-02-19 11:32:26 -05:00
|
|
|
config_release_config();
|
2007-08-11 13:44:45 -04:00
|
|
|
slave_rebuild_mounts();
|
2004-02-19 11:32:26 -05:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2005-08-24 20:07:17 -04:00
|
|
|
if (response)
|
|
|
|
{
|
2005-11-16 19:54:28 -05:00
|
|
|
client_send_403 (source->client, "too many sources connected");
|
2005-08-24 20:07:17 -04:00
|
|
|
source->client = NULL;
|
|
|
|
}
|
2004-02-19 11:32:26 -05:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-17 04:32:15 -04:00
|
|
|
static int _check_pass_http(http_parser_t *parser,
|
2007-08-16 18:49:13 -04:00
|
|
|
const char *correctuser, const char *correctpass)
|
2002-08-16 10:26:48 -04:00
|
|
|
{
|
|
|
|
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
|
2007-08-16 18:49:13 -04:00
|
|
|
const char *header = httpp_getvar(parser, "authorization");
|
2002-08-16 10:26:48 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-08-16 18:49:13 -04:00
|
|
|
static int _check_pass_icy(http_parser_t *parser, const char *correctpass)
|
2003-03-07 23:57:02 -05:00
|
|
|
{
|
2007-08-16 18:49:13 -04:00
|
|
|
const char *password;
|
2003-03-07 23:57:02 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-08-16 18:49:13 -04:00
|
|
|
static int _check_pass_ice(http_parser_t *parser, const char *correctpass)
|
2002-08-12 10:48:31 -04:00
|
|
|
{
|
2007-08-16 18:49:13 -04:00
|
|
|
const 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;
|
2007-08-16 18:49:13 -04:00
|
|
|
const 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
|
|
|
}
|
|
|
|
|
2009-01-13 20:18:22 -05:00
|
|
|
|
|
|
|
/* return 0 for failed, 1 for ok
|
|
|
|
*/
|
|
|
|
int connection_check_pass (http_parser_t *parser, const char *user, const char *pass)
|
2002-08-17 04:32:15 -04:00
|
|
|
{
|
2002-10-10 04:50:58 -04:00
|
|
|
int ret;
|
2007-08-16 18:49:13 -04:00
|
|
|
const char *protocol;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
2003-02-11 09:23:34 -05:00
|
|
|
if(!pass) {
|
|
|
|
WARN0("No source password set, rejecting source");
|
2009-01-13 20:18:22 -05:00
|
|
|
return -1;
|
2003-02-11 09:23:34 -05:00
|
|
|
}
|
|
|
|
|
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);
|
2009-01-13 20:18:22 -05:00
|
|
|
if (!ret)
|
2003-03-07 23:57:02 -05:00
|
|
|
{
|
2009-01-13 20:18:22 -05:00
|
|
|
ice_config_t *config = config_get_config_unlocked();
|
|
|
|
if (config->ice_login)
|
|
|
|
{
|
|
|
|
ret = _check_pass_ice(parser, pass);
|
|
|
|
if(ret)
|
|
|
|
WARN0("Source is using deprecated icecast login");
|
|
|
|
}
|
2003-03-07 23:57:02 -05:00
|
|
|
}
|
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
|
|
|
|
2009-01-13 20:18:22 -05:00
|
|
|
/* only called for native icecast source clients */
|
|
|
|
static void _handle_source_request (client_t *client, const char *uri)
|
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;
|
|
|
|
}
|
2009-01-13 20:18:22 -05:00
|
|
|
switch (client_check_source_auth (client, uri))
|
|
|
|
{
|
|
|
|
case 0: /* authenticated from config file */
|
|
|
|
source_startup (client, uri, ICECAST_SOURCE_AUTH);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* auth pending */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* failed */
|
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);
|
2009-01-13 20:18:22 -05:00
|
|
|
break;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2009-01-13 20:18:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void source_startup (client_t *client, const char *uri, int auth_style)
|
|
|
|
{
|
|
|
|
source_t *source;
|
2004-02-19 16:16:59 -05:00
|
|
|
source = source_reserve (uri);
|
2009-01-13 20:18:22 -05:00
|
|
|
|
2004-02-19 16:16:59 -05:00
|
|
|
if (source)
|
|
|
|
{
|
|
|
|
source->client = client;
|
2005-05-06 11:57:15 -04:00
|
|
|
source->parser = client->parser;
|
|
|
|
source->con = client->con;
|
2005-08-24 20:07:17 -04:00
|
|
|
if (connection_complete_source (source, 1) < 0)
|
2004-02-19 16:16:59 -05:00
|
|
|
{
|
2005-09-08 09:32:17 -04:00
|
|
|
source_clear_source (source);
|
2004-02-19 16:16:59 -05:00
|
|
|
source_free_source (source);
|
2009-01-13 20:18:22 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
client->respcode = 200;
|
|
|
|
if (auth_style == SHOUTCAST_SOURCE_AUTH)
|
|
|
|
{
|
|
|
|
source->shoutcast_compat = 1;
|
|
|
|
source_client_callback (client, source);
|
2004-02-19 16:16:59 -05:00
|
|
|
}
|
|
|
|
else
|
2005-08-12 11:27:32 -04:00
|
|
|
{
|
2005-08-18 16:37:35 -04:00
|
|
|
refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
|
2005-08-12 11:27:32 -04:00
|
|
|
client->respcode = 200;
|
2005-08-18 16:37:35 -04:00
|
|
|
snprintf (ok->data, PER_CLIENT_REFBUF_SIZE,
|
2005-08-12 11:27:32 -04:00
|
|
|
"HTTP/1.0 200 OK\r\n\r\n");
|
2005-08-18 16:37:35 -04:00
|
|
|
ok->len = strlen (ok->data);
|
|
|
|
/* we may have unprocessed data read in, so don't overwrite it */
|
|
|
|
ok->associated = client->refbuf;
|
|
|
|
client->refbuf = ok;
|
2005-08-12 11:27:32 -04:00
|
|
|
fserve_add_client_callback (client, source_client_callback, source);
|
|
|
|
}
|
2004-02-19 16:16:59 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-11-16 19:54:28 -05:00
|
|
|
client_send_403 (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;
|
2005-08-12 11:27:32 -04:00
|
|
|
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
|
|
|
|
"HTTP/1.0 200 OK\r\n\r\n");
|
|
|
|
client->refbuf->len = strlen (client->refbuf->data);
|
|
|
|
fserve_add_client_callback (client, stats_callback, NULL);
|
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-05 08:03:35 -05:00
|
|
|
int port;
|
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-10-26 15:29:12 -04:00
|
|
|
char *uri = passed_uri;
|
2007-10-15 21:53:06 -04:00
|
|
|
listener_t *listen_sock;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
config = config_get_config();
|
|
|
|
port = config->port;
|
2007-10-15 21:53:06 -04:00
|
|
|
|
|
|
|
listen_sock = config_get_listen_sock (config, client->con);
|
|
|
|
if (listen_sock)
|
|
|
|
{
|
|
|
|
serverhost = listen_sock->bind_address;
|
|
|
|
serverport = listen_sock->port;
|
2003-04-23 08:44:29 -04:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2007-08-21 18:30:30 -04:00
|
|
|
auth_add_listener (uri, client);
|
2004-10-26 15:29:12 -04:00
|
|
|
if (uri != passed_uri) free (uri);
|
2002-08-12 10:48:31 -04:00
|
|
|
}
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
static void _handle_shoutcast_compatible (client_queue_t *node)
|
|
|
|
{
|
2004-11-11 10:47:33 -05:00
|
|
|
char *http_compliant;
|
|
|
|
int http_compliant_len = 0;
|
|
|
|
http_parser_t *parser;
|
2005-08-11 19:29:58 -04:00
|
|
|
ice_config_t *config = config_get_config ();
|
|
|
|
char *shoutcast_mount;
|
|
|
|
client_t *client = node->client;
|
2004-11-11 10:47:33 -05:00
|
|
|
|
2007-10-24 22:25:49 -04:00
|
|
|
if (node->shoutcast_mount)
|
|
|
|
shoutcast_mount = node->shoutcast_mount;
|
|
|
|
else
|
|
|
|
shoutcast_mount = config->shoutcast_mount;
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
if (node->shoutcast == 1)
|
|
|
|
{
|
2005-09-26 12:34:51 -04:00
|
|
|
char *source_password, *ptr, *headers;
|
2007-10-24 22:25:49 -04:00
|
|
|
mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
|
|
|
|
if (mountinfo && mountinfo->password)
|
|
|
|
source_password = strdup (mountinfo->password);
|
|
|
|
else
|
2011-11-25 16:24:28 -05:00
|
|
|
{
|
|
|
|
if (config->source_password)
|
|
|
|
source_password = strdup (config->source_password);
|
|
|
|
else
|
|
|
|
source_password = NULL;
|
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
config_release_config();
|
|
|
|
|
|
|
|
/* Get rid of trailing \r\n or \n after password */
|
2005-10-01 10:08:36 -04:00
|
|
|
ptr = strstr (client->refbuf->data, "\r\r\n");
|
2005-09-26 12:34:51 -04:00
|
|
|
if (ptr)
|
2005-10-01 10:08:36 -04:00
|
|
|
headers = ptr+3;
|
2005-09-26 12:34:51 -04:00
|
|
|
else
|
|
|
|
{
|
2005-10-01 10:08:36 -04:00
|
|
|
ptr = strstr (client->refbuf->data, "\r\n");
|
2005-09-26 12:34:51 -04:00
|
|
|
if (ptr)
|
2005-10-01 10:08:36 -04:00
|
|
|
headers = ptr+2;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ptr = strstr (client->refbuf->data, "\n");
|
|
|
|
if (ptr)
|
|
|
|
headers = ptr+1;
|
|
|
|
}
|
2005-09-26 12:34:51 -04:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
{
|
|
|
|
client_destroy (client);
|
|
|
|
free (source_password);
|
2007-10-24 22:25:49 -04:00
|
|
|
free (node->shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
free (node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*ptr = '\0';
|
|
|
|
|
2011-11-25 16:24:28 -05:00
|
|
|
if (source_password && strcmp (client->refbuf->data, source_password) == 0)
|
2005-08-11 19:29:58 -04:00
|
|
|
{
|
|
|
|
client->respcode = 200;
|
|
|
|
/* send this non-blocking but if there is only a partial write
|
|
|
|
* then leave to header timeout */
|
2007-08-08 22:51:53 -04:00
|
|
|
sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
|
2005-09-26 12:34:51 -04:00
|
|
|
node->offset -= (headers - client->refbuf->data);
|
|
|
|
memmove (client->refbuf->data, headers, node->offset+1);
|
2005-08-11 19:29:58 -04:00
|
|
|
node->shoutcast = 2;
|
|
|
|
/* we've checked the password, now send it back for reading headers */
|
|
|
|
_add_request_queue (node);
|
|
|
|
free (source_password);
|
|
|
|
return;
|
|
|
|
}
|
2005-10-01 10:08:36 -04:00
|
|
|
else
|
|
|
|
INFO1 ("password does not match \"%s\"", client->refbuf->data);
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
2007-10-24 21:03:47 -04:00
|
|
|
free (source_password);
|
2007-10-24 22:25:49 -04:00
|
|
|
free (node->shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
free (node);
|
2004-11-11 10:47:33 -05:00
|
|
|
return;
|
|
|
|
}
|
2007-10-24 22:25:49 -04:00
|
|
|
/* actually make a copy as we are dropping the config lock */
|
|
|
|
shoutcast_mount = strdup (shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
config_release_config();
|
2004-11-11 10:47:33 -05:00
|
|
|
/* 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 */
|
2005-08-11 19:29:58 -04:00
|
|
|
http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
|
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,
|
2005-08-11 19:29:58 -04:00
|
|
|
"SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
|
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)))
|
|
|
|
{
|
2005-08-11 19:29:58 -04:00
|
|
|
/* we may have more than just headers, so prepare for it */
|
|
|
|
if (node->stream_offset == node->offset)
|
|
|
|
client->refbuf->len = 0;
|
|
|
|
else
|
2005-05-06 11:57:15 -04:00
|
|
|
{
|
2005-08-11 19:29:58 -04:00
|
|
|
char *ptr = client->refbuf->data;
|
|
|
|
client->refbuf->len = node->offset - node->stream_offset;
|
|
|
|
memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
|
2005-05-06 11:57:15 -04:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
client->parser = parser;
|
2009-01-13 20:18:22 -05:00
|
|
|
source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
|
2004-11-11 10:47:33 -05:00
|
|
|
}
|
2006-03-07 14:35:18 -05:00
|
|
|
else {
|
|
|
|
httpp_destroy (parser);
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
2006-03-07 14:35:18 -05:00
|
|
|
}
|
2005-05-06 11:57:15 -04:00
|
|
|
free (http_compliant);
|
2005-08-11 19:29:58 -04:00
|
|
|
free (shoutcast_mount);
|
2007-10-24 22:25:49 -04:00
|
|
|
free (node->shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
free (node);
|
|
|
|
return;
|
2004-11-11 10:47:33 -05:00
|
|
|
}
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2008-07-21 22:24:30 -04:00
|
|
|
static void _handle_connection(void)
|
2002-08-12 10:48:31 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
http_parser_t *parser;
|
2007-08-16 18:49:13 -04:00
|
|
|
const char *rawuri;
|
2008-07-21 22:24:30 -04:00
|
|
|
client_queue_t *node;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2008-07-21 22:24:30 -04:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
node = _get_connection();
|
2005-08-11 19:29:58 -04:00
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
client_t *client = node->client;
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2004-11-11 10:47:33 -05:00
|
|
|
/* Check for special shoutcast compatability processing */
|
2008-07-21 22:24:30 -04:00
|
|
|
if (node->shoutcast)
|
2005-08-11 19:29:58 -04:00
|
|
|
{
|
|
|
|
_handle_shoutcast_compatible (node);
|
2003-03-14 21:10:19 -05:00
|
|
|
continue;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* process normal HTTP headers */
|
2003-03-14 21:10:19 -05:00
|
|
|
parser = httpp_create_parser();
|
|
|
|
httpp_initialize(parser, NULL);
|
2005-08-11 19:29:58 -04:00
|
|
|
client->parser = parser;
|
|
|
|
if (httpp_parse (parser, client->refbuf->data, node->offset))
|
|
|
|
{
|
2007-08-16 18:49:13 -04:00
|
|
|
char *uri;
|
|
|
|
|
2005-08-11 19:29:58 -04:00
|
|
|
/* we may have more than just headers, so prepare for it */
|
|
|
|
if (node->stream_offset == node->offset)
|
|
|
|
client->refbuf->len = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *ptr = client->refbuf->data;
|
|
|
|
client->refbuf->len = node->offset - node->stream_offset;
|
|
|
|
memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
|
|
|
|
}
|
2007-10-24 22:25:49 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
free (node->shoutcast_mount);
|
2005-08-11 19:29:58 -04:00
|
|
|
free (node);
|
2008-07-21 22:24:30 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
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");
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
2003-03-14 21:10:19 -05:00
|
|
|
continue;
|
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
|
2002-08-11 10:23:39 -04:00
|
|
|
uri = util_normalise_uri(rawuri);
|
|
|
|
|
2005-05-06 11:57:15 -04:00
|
|
|
if (uri == NULL)
|
|
|
|
{
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
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) {
|
2009-01-13 20:18:22 -05:00
|
|
|
_handle_source_request (client, uri);
|
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);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
free (node);
|
2002-08-09 04:06:00 -04:00
|
|
|
ERROR0("HTTP request parsing failed");
|
2005-08-11 19:29:58 -04:00
|
|
|
client_destroy (client);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2005-08-11 19:29:58 -04:00
|
|
|
continue;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2008-07-21 22:24:30 -04:00
|
|
|
break;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|
|
|
|
|
2007-10-15 21:53:06 -04:00
|
|
|
|
|
|
|
/* called when listening thread is not checking for incoming connections */
|
|
|
|
int connection_setup_sockets (ice_config_t *config)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
listener_t *listener, **prev;
|
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
free (banned_ip.filename);
|
|
|
|
banned_ip.filename = NULL;
|
|
|
|
free (allowed_ip.filename);
|
|
|
|
allowed_ip.filename = NULL;
|
|
|
|
|
2007-10-15 21:53:06 -04:00
|
|
|
global_lock();
|
|
|
|
if (global.serversock)
|
|
|
|
{
|
|
|
|
for (; count < global.server_sockets; count++)
|
|
|
|
sock_close (global.serversock [count]);
|
|
|
|
free (global.serversock);
|
|
|
|
global.serversock = NULL;
|
|
|
|
}
|
|
|
|
if (config == NULL)
|
|
|
|
{
|
|
|
|
global_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-23 18:25:31 -04:00
|
|
|
/* setup the banned/allowed IP filenames from the xml */
|
|
|
|
if (config->banfile)
|
|
|
|
banned_ip.filename = strdup (config->banfile);
|
|
|
|
|
|
|
|
if (config->allowfile)
|
|
|
|
allowed_ip.filename = strdup (config->allowfile);
|
|
|
|
|
2007-10-15 21:53:06 -04:00
|
|
|
count = 0;
|
|
|
|
global.serversock = calloc (config->listen_sock_count, sizeof (sock_t));
|
|
|
|
|
|
|
|
listener = config->listen_sock;
|
|
|
|
prev = &config->listen_sock;
|
|
|
|
while (listener)
|
|
|
|
{
|
|
|
|
int successful = 0;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
sock_t sock = sock_get_server_socket (listener->port, listener->bind_address);
|
|
|
|
if (sock == SOCK_ERROR)
|
|
|
|
break;
|
|
|
|
if (sock_listen (sock, ICE_LISTEN_QUEUE) == SOCK_ERROR)
|
|
|
|
{
|
|
|
|
sock_close (sock);
|
|
|
|
break;
|
|
|
|
}
|
2009-03-14 12:05:12 -04:00
|
|
|
/* some win32 setups do not do TCP win scaling well, so allow an override */
|
|
|
|
if (listener->so_sndbuf)
|
|
|
|
sock_set_send_buffer (sock, listener->so_sndbuf);
|
2009-01-07 21:47:44 -05:00
|
|
|
sock_set_blocking (sock, 0);
|
2007-10-15 21:53:06 -04:00
|
|
|
successful = 1;
|
|
|
|
global.serversock [count] = sock;
|
|
|
|
count++;
|
|
|
|
} while(0);
|
|
|
|
if (successful == 0)
|
|
|
|
{
|
|
|
|
if (listener->bind_address)
|
|
|
|
ERROR2 ("Could not create listener socket on port %d bind %s",
|
|
|
|
listener->port, listener->bind_address);
|
|
|
|
else
|
|
|
|
ERROR1 ("Could not create listener socket on port %d", listener->port);
|
|
|
|
/* remove failed connection */
|
|
|
|
*prev = config_clear_listener (listener);
|
|
|
|
listener = *prev;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (listener->bind_address)
|
|
|
|
INFO2 ("listener socket on port %d address %s", listener->port, listener->bind_address);
|
|
|
|
else
|
|
|
|
INFO1 ("listener socket on port %d", listener->port);
|
|
|
|
prev = &listener->next;
|
|
|
|
listener = listener->next;
|
|
|
|
}
|
|
|
|
global.server_sockets = count;
|
|
|
|
global_unlock();
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
ERROR0 ("No listening sockets established");
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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);
|
2007-08-28 23:51:22 -04:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
if (con->ssl) { SSL_shutdown (con->ssl); SSL_free (con->ssl); }
|
|
|
|
#endif
|
2003-03-14 21:10:19 -05:00
|
|
|
free(con);
|
2001-09-09 22:21:46 -04:00
|
|
|
}
|