1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-11-03 04:17:17 -05:00

kh26 update. The 2 main issues are :-

possible corruption with short-send of non-ogg metadata, specifically the
write merged block.
the slave stream listing was not honouring non-hidden attributes or auth

The smaller parts are mainly code re-organisation, a backward compatibility
option and options for IP handling.

svn path=/icecast/branches/kh/icecast/; revision=17364
This commit is contained in:
Karl Heyes 2010-08-17 00:26:27 +00:00
parent 8796ecc4e0
commit d8753a5b99
15 changed files with 217 additions and 131 deletions

12
NEWS
View File

@ -1,5 +1,6 @@
Feature differences from SVN trunk
. FLV wrapping for mp3/aac listeners requesting it with ?type=.flv
. define a fixed number of worker threads (default 1) for processing clients
. allow for wildcards (*[] expansion) in mount-name and ban/allow files
. can limit mountpoint by outgoing bandwidth as well as a max listeners count
@ -16,6 +17,17 @@ Feature differences from SVN trunk
any extra tags are show in the conf/icecast.xml.dist file
2.3.2-kh26
. fix possible content corruption on shoutcast metadata inserted streams if a short
send occurs during metadata sending.
. do not list non-hidden streams in /admin/stream[list|streams] and make sure auth
applies to /admin/streams.
. allow for the so-sndbuf option per-mount as well as on all connections.
. old style bind-address setting (not listen-socket) was not working
. allow a negative ban-client value to use the internal deny block of IPs to prevent
more than 1 IP connecting at the same time.
. small code changes for main worker thread
2.3.2-kh25
. FLV clients could skip frames on a truncated write, leading to playback problems.
. various client scheduling tunings, allows quicker reschedule when only some data

View File

@ -95,7 +95,7 @@
#define PACKAGE_NAME "Icecast"
/* Version number of package */
#define VERSION "2.3.2-kh25"
#define VERSION "2.3.2-kh26"
/* Define to the version of this package. */
#define PACKAGE_VERSION VERSION

View File

@ -1,4 +1,4 @@
AC_INIT([Icecast], [2.3.2-kh25], [karl@xiph.org])
AC_INIT([Icecast], [2.3.2-kh26], [karl@xiph.org])
LT_INIT
AC_PREREQ(2.59)

View File

@ -412,6 +412,10 @@ static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo
client->flags |= CLIENT_AUTHENTICATED;
/* some win32 setups do not do TCP win scaling well, so allow an override */
if (mountinfo && mountinfo->so_sndbuf > 0)
sock_set_send_buffer (client->connection.sock, mountinfo->so_sndbuf);
/* check whether we are processing a streamlist request for slaves */
if (strcmp (mount, "/admin/streams") == 0)
{
@ -534,10 +538,15 @@ void auth_add_listener (const char *mount, client_t *client)
{
if (mountinfo->skip_accesslog)
client->flags |= CLIENT_SKIP_ACCESSLOG;
if (mountinfo->ban_client || mountinfo->no_mount)
if (mountinfo->ban_client)
{
DEBUG1 ("ban client value is %d", mountinfo->ban_client);
if (mountinfo->ban_client < 0)
client->flags |= CLIENT_IP_BAN_LIFT;
connection_add_banned_ip (client->connection.ip, mountinfo->ban_client);
}
if (mountinfo->no_mount)
{
if (mountinfo->ban_client)
connection_add_banned_ip (client->connection.ip, mountinfo->ban_client);
config_release_config ();
client_send_403 (client, "mountpoint unavailable");
return;
@ -562,7 +571,10 @@ void auth_add_listener (const char *mount, client_t *client)
}
else
{
add_authenticated_listener (mount, mountinfo, client);
if (client->flags & CLIENT_AUTHENTICATED)
add_authenticated_listener (mount, mountinfo, client);
else
client_send_403 (client, "Forbidden");
}
config_release_config ();
}

View File

@ -792,6 +792,7 @@ static int _parse_mount (xmlNodePtr node, void *arg)
config_get_bool, &mount->url_ogg_meta },
{ "no-mount", config_get_bool, &mount->no_mount },
{ "ban-client", config_get_int, &mount->ban_client },
{ "so-sndbuf", config_get_int, &mount->so_sndbuf },
{ "hidden", config_get_bool, &mount->hidden },
{ "authentication", auth_get_authenticator, &mount->auth },
{ "on-connect", config_get_str, &mount->on_connect },
@ -842,7 +843,9 @@ static int _parse_mount (xmlNodePtr node, void *arg)
if (mount->url_ogg_meta)
mount->ogg_passthrough = 0;
if (mount->queue_block_size < 100)
mount->queue_block_size = 2900;
mount->queue_block_size = 1400;
if (mount->ban_client < 0)
mount->no_mount = 0;
mount->next = config->mounts;
config->mounts = mount;
@ -1061,6 +1064,7 @@ static int _parse_listen_sock (xmlNodePtr node, void *arg)
static int _parse_root (xmlNodePtr node, ice_config_t *config)
{
char *bindaddress = NULL;
struct cfg_tag icecast_tags[] =
{
{ "location", config_get_str, &config->location },
@ -1070,6 +1074,7 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
{ "source-password", config_get_str, &config->source_password },
{ "hostname", config_get_str, &config->hostname },
{ "port", config_get_int, &config->port },
{ "bind-address", config_get_str, &bindaddress },
{ "fileserve", config_get_bool, &config->fileserve },
{ "relays-on-demand", config_get_bool, &config->on_demand },
{ "master-server", config_get_str, &config->master_server },
@ -1104,8 +1109,22 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
config->max_redirects = 1;
if (config->listen_sock_count == 0)
{
WARN0 ("No listen-socket defintions");
return -1;
if (config->port)
{
listener_t *listener = calloc (1, sizeof(listener_t));
listener->refcount = 1;
listener->port = config->port;
listener->qlen = ICE_LISTEN_QUEUE;
listener->bind_address = (char*)xmlStrdup (XMLSTR(bindaddress));
listener->next = config->listen_sock;
config->listen_sock = listener;
config->listen_sock_count++;
}
else
{
WARN0 ("No listen-socket defintions");
return -1;
}
}
return 0;
}

View File

@ -101,6 +101,7 @@ typedef struct _mount_proxy {
int ban_client; /* do we add a client on this to the ban list automatically */
int no_mount; /* Do we permit direct requests of this mountpoint? (or only
indirect, through fallbacks) */
int so_sndbuf; /* TCP send buffer size for new clients */
int burst_size; /* amount to send to a new client if possible, -1 take
* from global setting */
unsigned int queue_size_limit;

View File

@ -101,6 +101,13 @@ void client_destroy(client_t *client)
if (client->respcode > 0 && client->parser)
logging_access(client);
if (client->flags & CLIENT_IP_BAN_LIFT)
{
INFO1 ("lifting IP ban on client at %s", client->connection.ip);
connection_release_banned_ip (client->connection.ip);
client->flags &= ~CLIENT_IP_BAN_LIFT;
}
connection_close (&client->connection);
if (client->parser)
httpp_destroy (client->parser);
@ -372,6 +379,7 @@ void client_add_worker (client_t *client)
worker_wakeup (handler);
}
#ifdef _WIN32
#define pipe_create sock_create_pipe_emulation
#define pipe_write(A, B, C) send(A, B, C, 0)
@ -382,26 +390,6 @@ void client_add_worker (client_t *client)
#define pipe_read read
#endif
static void worker_move_clients (worker_t *from)
{
client_t *client = from->clients;
if (workers == NULL || from == NULL || workers->running == 0)
return;
while (client)
{
client->worker = workers;
client = client->next_on_worker;
}
thread_spin_lock (&workers->lock);
*workers->pending_clients_tail = from->clients;
workers->pending_clients_tail = from->last_p;
workers->pending_count += from->count;
thread_spin_unlock (&workers->lock);
from->count = 0;
from->clients = NULL;
from->last_p = &from->clients;
}
static void worker_add_pending_clients (worker_t *worker)
{
@ -440,87 +428,113 @@ static void worker_wait (worker_t *worker)
worker_add_pending_clients (worker);
worker->time_ms = timing_get_time();
worker->current_time.tv_sec = worker->time_ms/1000;
worker->current_time.tv_sec = (time_t)(worker->time_ms/1000);
worker->current_time.tv_nsec = worker->current_time.tv_sec - (worker->time_ms*1000);
worker->wakeup_ms = worker->time_ms + 60000;
}
void *worker (void *arg)
static void worker_relocate_clients (worker_t *worker)
{
worker_t *handler = arg;
client_t *client, **prevp;
long prev_count = -1;
handler->running = 1;
handler->wakeup_ms = (int64_t)0;
prevp = &handler->clients;
while (1)
if (workers == NULL)
return;
while (worker->count || worker->pending_count)
{
if (handler->running == 0)
{
handler->wakeup_ms = handler->time_ms + 50;
worker_add_pending_clients (handler);
if (handler->count == 0)
break;
worker_move_clients (handler);
}
if (prev_count != handler->count)
{
DEBUG2 ("%p now has %d clients", handler, handler->count);
prev_count = handler->count;
}
client_t *client = worker->clients, **prevp = &worker->clients;
worker_wait (handler);
client = handler->clients;
prevp = &handler->clients;
worker->wakeup_ms = worker->time_ms + 150;
while (client)
{
if (client->worker != handler)
abort();
if (client->flags & CLIENT_ACTIVE)
{
client->worker = workers;
prevp = &client->next_on_worker;
}
else
{
*prevp = client->next_on_worker;
worker_add_client (worker, client);
worker->count--;
}
client = *prevp;
}
if (worker->clients)
{
thread_spin_lock (&workers->lock);
*workers->pending_clients_tail = worker->clients;
workers->pending_clients_tail = prevp;
workers->pending_count += worker->count;
thread_spin_unlock (&workers->lock);
worker_wakeup (workers);
worker->clients = NULL;
worker->last_p = &worker->clients;
worker->count = 0;
}
worker_wait (worker);
}
}
void *worker (void *arg)
{
worker_t *worker = arg;
long prev_count = -1;
worker->running = 1;
worker->wakeup_ms = (int64_t)0;
while (1)
{
client_t *client = worker->clients, **prevp = &worker->clients;
if (prev_count != worker->count)
{
DEBUG2 ("%p now has %d clients", worker, worker->count);
prev_count = worker->count;
}
while (client)
{
if (client->worker != worker) abort();
/* process client details but skip those that are not ready yet */
if (client->flags & CLIENT_ACTIVE)
{
if (client->schedule_ms <= handler->time_ms+10)
int ret = 0;
client_t *nx = client->next_on_worker;
if (client->schedule_ms <= worker->time_ms+10)
{
int ret;
client_t *nx = client->next_on_worker;
client->schedule_ms = handler->time_ms;
client->schedule_ms = worker->time_ms;
ret = client->ops->process (client);
/* special handler, client has moved away to another worker */
if (ret > 0)
{
handler->count--;
if (nx == NULL) /* moved last client */
handler->last_p = prevp;
*prevp = nx;
client = nx;
continue;
}
if (ret < 0)
{
client_t *to_go = client;
*prevp = to_go->next_on_worker;
client->next_on_worker = NULL;
client->worker = NULL;
handler->count--;
if (client->ops->release)
client->ops->release (client);
client = *prevp;
if (client == NULL)
handler->last_p = prevp;
}
if (ret)
{
worker->count--;
if (nx == NULL) /* is this the last client */
worker->last_p = prevp;
client = *prevp = nx;
continue;
}
}
if (client->schedule_ms < handler->wakeup_ms)
handler->wakeup_ms = client->schedule_ms;
if (client->schedule_ms < worker->wakeup_ms)
worker->wakeup_ms = client->schedule_ms;
}
prevp = &client->next_on_worker;
client = *prevp;
}
if (worker->running == 0)
{
if (global.running == ICE_RUNNING)
break;
if (worker->count == 0 && worker->pending_count == 0)
break;
}
worker_wait (worker);
}
worker_relocate_clients (worker);
INFO0 ("shutting down");
return NULL;
}

View File

@ -149,6 +149,7 @@ void worker_wakeup (worker_t *worker);
#define CLIENT_HAS_INTRO_CONTENT (040)
#define CLIENT_SKIP_ACCESSLOG (0100)
#define CLIENT_HAS_MOVED (0200)
#define CLIENT_IP_BAN_LIFT (0400)
#define CLIENT_FORMAT_BIT (01000)
#endif /* __CLIENT_H__ */

View File

@ -356,7 +356,7 @@ static void add_banned_ip (avl_tree *t, const char *ip, time_t now)
void connection_add_banned_ip (const char *ip, int duration)
{
time_t timeout = -1;
time_t timeout = 0;
if (duration > 0)
timeout = time(NULL) + duration;
@ -368,6 +368,16 @@ void connection_add_banned_ip (const char *ip, int duration)
}
}
void connection_release_banned_ip (const char *ip)
{
if (banned_ip.contents)
{
global_lock();
avl_delete (banned_ip.contents, (void*)ip, free_filtered_line);
global_unlock();
}
}
void connection_stats (void)
{
if (banned_ip.contents)

View File

@ -61,6 +61,7 @@ int connection_init (connection_t *con, sock_t sock, const char *addr);
int connection_complete_source (struct source_tag *source);
void connection_uses_ssl (connection_t *con);
void connection_add_banned_ip (const char *ip, int duration);
void connection_release_banned_ip (const char *ip);
void connection_stats (void);
#ifdef HAVE_OPENSSL
int connection_read_ssl (connection_t *con, void *buf, size_t len);

View File

@ -342,31 +342,43 @@ static int send_stream_metadata (client_t *client, refbuf_t *refbuf, unsigned in
/* If there is a change in metadata then send it else
* send a single zero value byte in its place
*/
if (associated && associated != client_mp3->associated)
if (client->flags & CLIENT_IN_METADATA)
{
metadata = associated->data + client_mp3->metadata_offset;
meta_len = associated->len - client_mp3->metadata_offset;
client->flags &= ~CLIENT_USING_BLANK_META;
refbuf_release (client_mp3->associated);
refbuf_addref (associated);
client_mp3->associated = associated;
/* rare but possible case of resuming a send part way through a metadata block */
metadata = client_mp3->associated->data + client_mp3->metadata_offset;
meta_len = client_mp3->associated->len - client_mp3->metadata_offset;
}
else
{
if (associated || ((client->flags & CLIENT_USING_BLANK_META) &&
client_mp3->metadata_offset == 0))
if (associated && associated != client_mp3->associated)
{
metadata = "\0";
meta_len = 1;
/* change of metadata found, but we do not release the blank one as that
* could race against the source client use of it. */
metadata = associated->data;
meta_len = associated->len;
if (client->flags & CLIENT_USING_BLANK_META)
client->flags &= ~CLIENT_USING_BLANK_META;
else
refbuf_release (client_mp3->associated);
refbuf_addref (associated);
client_mp3->associated = associated;
}
else
{
char *meta = blank_meta.data;
metadata = meta + client_mp3->metadata_offset;
meta_len = blank_meta.len - client_mp3->metadata_offset;
client->flags |= CLIENT_USING_BLANK_META;
refbuf_release (client_mp3->associated);
client_mp3->associated = NULL;
if (associated) /* previously sent metadata does not need to be sent again */
{
metadata = "\0";
meta_len = 1;
}
else
{
char *meta = blank_meta.data;
metadata = meta + client_mp3->metadata_offset;
meta_len = blank_meta.len - client_mp3->metadata_offset;
client->flags |= CLIENT_USING_BLANK_META;
refbuf_release (client_mp3->associated);
client_mp3->associated = &blank_meta;
}
}
}
/* if there is normal stream data to send as well as metadata then try
@ -389,6 +401,8 @@ static int send_stream_metadata (client_t *client, refbuf_t *refbuf, unsigned in
client->flags |= CLIENT_IN_METADATA;
client->schedule_ms += 200;
}
else
client->flags &= ~CLIENT_IN_METADATA;
client_mp3->since_meta_block = 0;
client->pos += remaining;
client->queue_pos += remaining;
@ -401,6 +415,7 @@ static int send_stream_metadata (client_t *client, refbuf_t *refbuf, unsigned in
client->pos += ret;
client->queue_pos += ret;
}
client->flags |= CLIENT_IN_METADATA;
return ret > 0 ? ret : 0;
}
ret = client_send_bytes (client, metadata, meta_len);
@ -434,15 +449,6 @@ static int format_mp3_write_buf_to_client (client_t *client)
do
{
/* send any unwritten metadata to the client */
if (client->flags & CLIENT_IN_METADATA)
{
ret = send_stream_metadata (client, refbuf, 0);
if (client->flags & CLIENT_IN_METADATA)
break;
written += ret;
}
/* see if we need to send the current metadata to the client */
if (client_mp3->interval)
{
@ -459,6 +465,7 @@ static int format_mp3_write_buf_to_client (client_t *client)
len -= remaining;
if (ret <= (int)remaining || len > client_mp3->interval)
break;
written += ret;
}
}
/* write any mp3, maybe after the metadata block */

View File

@ -677,6 +677,7 @@ static int prefile_send (client_t *client)
{
refbuf_t *refbuf = client->refbuf;
int loop = 6, bytes, written = 0;
worker_t *worker = client->worker;
while (loop)
{
@ -733,15 +734,15 @@ static int prefile_send (client_t *client)
bytes = client->check_buffer (client);
if (bytes < 0)
{
client->schedule_ms = client->worker->time_ms + 300;
client->schedule_ms = worker->time_ms + 300;
return 0;
}
written += bytes;
global_add_bitrates (global.out_bitrate, bytes, client->worker->time_ms);
global_add_bitrates (global.out_bitrate, bytes, worker->time_ms);
if (written > 30000)
break;
}
client->schedule_ms = client->worker->time_ms + 100;
client->schedule_ms = worker->time_ms + 100;
return 0;
}
@ -774,9 +775,11 @@ static int file_send (client_t *client)
refbuf_t *refbuf = client->refbuf;
int loop = 6, bytes, written = 0, ret = 0;
fh_node *fh = client->shared_data;
time_t now = client->worker->current_time.tv_sec;
worker_t *worker = client->worker;
time_t now;
client->schedule_ms = client->worker->time_ms;
client->schedule_ms = worker->time_ms;
now = worker->current_time.tv_sec;
/* slowdown if max bandwidth is exceeded, but allow for short-lived connections to avoid
* this, eg admin requests */
if (throttle_sends > 1 && now - client->connection.con_time > 1)
@ -816,13 +819,17 @@ static int throttled_file_send (client_t *client)
refbuf_t *refbuf = client->refbuf;
int bytes;
fh_node *fh = client->shared_data;
time_t now = client->worker->current_time.tv_sec;
unsigned long secs = now - client->timer_start;
unsigned int rate = secs ? ((client->counter+1400)/secs) : 0;
time_t now;
worker_t *worker = client->worker;
unsigned long secs;
unsigned int rate = 0;
unsigned int limit = fh->finfo.limit;
if (fserve_running == 0 || client->connection.error)
return -1;
now = worker->current_time.tv_sec;
secs = now - client->timer_start;
client->schedule_ms = worker->time_ms;
if (client->connection.discon_time && now >= client->connection.discon_time)
return -1;
if (fh->finfo.fallback)
@ -830,15 +837,17 @@ static int throttled_file_send (client_t *client)
fserve_move_listener (client);
return 0;
}
thread_mutex_lock (&fh->lock);
if (client->flags & CLIENT_WANTS_FLV) /* increase limit for flv clients as wrapping takes more space */
limit = (unsigned long)(limit * 1.02);
if (secs)
rate = (client->counter+1400)/secs;
thread_mutex_lock (&fh->lock);
if (rate > limit)
{
client->schedule_ms = client->worker->time_ms + (1000*(rate - fh->finfo.limit))/fh->finfo.limit;
rate_add (fh->format->out_bitrate, 0, client->worker->time_ms);
client->schedule_ms += (1000*(rate - limit))/limit;
rate_add (fh->format->out_bitrate, 0, worker->time_ms);
thread_mutex_unlock (&fh->lock);
global_add_bitrates (global.out_bitrate, 0, client->worker->time_ms);
global_add_bitrates (global.out_bitrate, 0, worker->time_ms);
return 0;
}
if (fh->stats_update <= now)
@ -855,7 +864,7 @@ static int throttled_file_send (client_t *client)
thread_mutex_unlock (&fh->lock);
client->intro_offset = 0;
client->pos = refbuf->len = 0;
client->schedule_ms = client->worker->time_ms + 150;
client->schedule_ms += 150;
return 0;
}
client->pos = 0;
@ -863,11 +872,11 @@ static int throttled_file_send (client_t *client)
bytes = client->check_buffer (client);
if (bytes < 0)
bytes = 0;
rate_add (fh->format->out_bitrate, bytes, client->worker->time_ms);
rate_add (fh->format->out_bitrate, bytes, worker->time_ms);
thread_mutex_unlock (&fh->lock);
global_add_bitrates (global.out_bitrate, bytes, client->worker->time_ms);
global_add_bitrates (global.out_bitrate, bytes, worker->time_ms);
client->counter += bytes;
client->schedule_ms = client->worker->time_ms + (1000/(fh->finfo.limit/1400*2));
client->schedule_ms += (1000/(limit/1400*2));
/* progessive slowdown if max bandwidth is exceeded. */
if (throttle_sends > 1)

View File

@ -1345,6 +1345,7 @@ static void relay_release (client_t *client)
static int relay_startup (client_t *client)
{
relay_server *relay = get_relay_details (client);
worker_t *worker = client->worker;
if (relay->cleanup)
{
@ -1356,7 +1357,7 @@ static int relay_startup (client_t *client)
}
if (global.running != ICE_RUNNING)
return 0; /* wait for cleanup */
if (relay->running == 0 || relay->start > client->worker->current_time.tv_sec)
if (relay->running == 0 || relay->start > worker->current_time.tv_sec)
{
client->schedule_ms = client->worker->time_ms + 1000;
return 0;
@ -1368,7 +1369,7 @@ static int relay_startup (client_t *client)
int start_relay = src->listeners; // 0 or non-zero
src->flags |= SOURCE_ON_DEMAND;
if (client->worker->current_time.tv_sec % 10 == 0)
if (worker->current_time.tv_sec % 10 == 0)
{
mount_proxy * mountinfo = config_find_mount (config_get_config(), src->mount);
if (mountinfo && mountinfo->fallback_mount)

View File

@ -1103,7 +1103,7 @@ refbuf_t *stats_get_streams (int prepend)
int ret;
stats_source_t *source = (stats_source_t *)node->key;
if (source->flags & STATS_SLAVE)
if ((source->flags & STATS_HIDDEN) == 0)
{
if (remaining <= strlen (source->source) + prelen + 3)
{

View File

@ -3,7 +3,7 @@
[Setup]
AppName=Icecast2-KH
AppVerName=Icecast v2.3.2-kh25
AppVerName=Icecast v2.3.2-kh26
AppPublisherURL=http://www.icecast.org
AppSupportURL=http://www.icecast.org
AppUpdatesURL=http://www.icecast.org
@ -13,7 +13,7 @@ AllowNoIcons=yes
LicenseFile=..\COPYING
InfoAfterFile=..\README
OutputDir=.
OutputBaseFilename=icecast2_win32_v2.3.2-kh25_setup
OutputBaseFilename=icecast2_win32_v2.3.2-kh26
WizardImageFile=icecast2logo2.bmp
WizardImageStretch=no
VersionInfoVersion=2.3.2
@ -68,4 +68,3 @@ Filename: "{app}\icecastService.exe"; Parameters: "install ""{app}""";Descriptio
[UninstallRun]
Filename: "{app}\icecastService.exe"; Parameters: "remove"