mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Update: Rewrote listen socket handling code comepletly.
This moves all the listen socket code into a nice and abstracting file. Notes: * Altering listen socket setup does not yet work on config reload. (Did it ever work?) * Server will start with no listen sockets. (There are unconfirmed rumours it sometimes(?) did before.) This is to be re-implemented in another commit. It can also be improved to work allow checking on reload or other config changes. * For slave connections the server address is now checked against the allow/deny-IP list.
This commit is contained in:
parent
1a426f7f81
commit
5490120d4d
@ -32,6 +32,7 @@ noinst_HEADERS = \
|
||||
refobject.h \
|
||||
module.h \
|
||||
reportxml.h \
|
||||
listensocket.h \
|
||||
event.h \
|
||||
event_log.h \
|
||||
event_exec.h \
|
||||
@ -74,6 +75,7 @@ icecast_SOURCES = \
|
||||
refobject.c \
|
||||
module.c \
|
||||
reportxml.c \
|
||||
listensocket.c \
|
||||
format.c \
|
||||
format_ogg.c \
|
||||
format_mp3.c \
|
||||
|
@ -2473,24 +2473,23 @@ mount_proxy *config_find_mount (ice_config_t *config,
|
||||
return mountinfo;
|
||||
}
|
||||
|
||||
/* Helper function to locate the configuration details of the listening
|
||||
* socket
|
||||
*/
|
||||
listener_t *config_get_listen_sock(ice_config_t *config, connection_t *con)
|
||||
{
|
||||
listener_t *listener;
|
||||
int i = 0;
|
||||
listener_t *config_copy_listener_one(const listener_t *listener) {
|
||||
listener_t *n;
|
||||
|
||||
listener = config->listen_sock;
|
||||
while (listener) {
|
||||
if (i >= global.server_sockets) {
|
||||
listener = NULL;
|
||||
} else {
|
||||
if (global.serversock[i] == con->serversock)
|
||||
break;
|
||||
listener = listener->next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
if (listener == NULL)
|
||||
return NULL;
|
||||
|
||||
n = calloc(1, sizeof(*n));
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
|
||||
n->next = NULL;
|
||||
n->port = listener->port;
|
||||
n->so_sndbuf = listener->so_sndbuf;
|
||||
n->bind_address = (char*)xmlStrdup(XMLSTR(listener->bind_address));
|
||||
n->shoutcast_compat = listener->shoutcast_compat;
|
||||
n->shoutcast_mount = (char*)xmlStrdup(XMLSTR(listener->shoutcast_mount));
|
||||
n->tls = listener->tls;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -262,7 +262,8 @@ void config_set_config(ice_config_t *config);
|
||||
listener_t *config_clear_listener (listener_t *listener);
|
||||
void config_clear(ice_config_t *config);
|
||||
mount_proxy *config_find_mount(ice_config_t *config, const char *mount, mount_type type);
|
||||
listener_t *config_get_listen_sock(ice_config_t *config, connection_t *con);
|
||||
|
||||
listener_t *config_copy_listener_one(const listener_t *listener);
|
||||
|
||||
config_options_t *config_parse_options(xmlNodePtr node);
|
||||
void config_clear_options(config_options_t *options);
|
||||
|
@ -110,7 +110,7 @@ static inline void client_reuseconnection(client_t *client) {
|
||||
return;
|
||||
|
||||
con = client->con;
|
||||
con = connection_create(con->sock, con->serversock, strdup(con->ip));
|
||||
con = connection_create(con->sock, con->listensocket_real, con->listensocket_effective, strdup(con->ip));
|
||||
reuse = client->reuse;
|
||||
client->con->sock = -1; /* TODO: do not use magic */
|
||||
|
||||
|
218
src/connection.c
218
src/connection.c
@ -58,6 +58,8 @@
|
||||
#include "matchfile.h"
|
||||
#include "tls.h"
|
||||
#include "acl.h"
|
||||
#include "refobject.h"
|
||||
#include "listensocket.h"
|
||||
|
||||
#define CATMODULE "connection"
|
||||
|
||||
@ -144,6 +146,13 @@ void connection_shutdown(void)
|
||||
void connection_reread_config(ice_config_t *config)
|
||||
{
|
||||
get_tls_certificate(config);
|
||||
/* This does not work yet.
|
||||
* listensocket_container_configure() should keep sockets that are the same.
|
||||
* Otherwise the Kernel doesn't let us do it.
|
||||
* -- ph3-der-loewe, 2018-04-17
|
||||
listensocket_container_configure(global.listensockets, config);
|
||||
listensocket_container_setup(global.listensockets);
|
||||
*/
|
||||
}
|
||||
|
||||
static unsigned long _next_connection_id(void)
|
||||
@ -245,13 +254,21 @@ static int connection_send(connection_t *con, const void *buf, size_t len)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
|
||||
connection_t *connection_create(sock_t sock, listensocket_t *listensocket_real, listensocket_t* listensocket_effective, char *ip)
|
||||
{
|
||||
connection_t *con;
|
||||
|
||||
if (!matchfile_match_allow_deny(allowed_ip, banned_ip, ip))
|
||||
return NULL;
|
||||
|
||||
con = (connection_t *)calloc(1, sizeof(connection_t));
|
||||
if (con) {
|
||||
refobject_ref(listensocket_real);
|
||||
refobject_ref(listensocket_effective);
|
||||
|
||||
con->sock = sock;
|
||||
con->serversock = serversock;
|
||||
con->listensocket_real = listensocket_real;
|
||||
con->listensocket_effective = listensocket_effective;
|
||||
con->con_time = time(NULL);
|
||||
con->id = _next_connection_id();
|
||||
con->ip = ip;
|
||||
@ -355,117 +372,6 @@ int connection_read_put_back(connection_t *con, const void *buf, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
static sock_t wait_for_serversock(int timeout)
|
||||
{
|
||||
#ifdef HAVE_POLL
|
||||
struct pollfd ufds [global.server_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 SOCK_ERROR;
|
||||
} else if(ret == 0) {
|
||||
return SOCK_ERROR;
|
||||
} else {
|
||||
int dst;
|
||||
for(i=0; i < global.server_sockets; i++) {
|
||||
if(ufds[i].revents & POLLIN)
|
||||
return ufds[i].fd;
|
||||
if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
|
||||
if (ufds[i].revents & (POLLHUP|POLLERR)) {
|
||||
sock_close (global.serversock[i]);
|
||||
ICECAST_LOG_WARN("Had to close a listening socket");
|
||||
}
|
||||
global.serversock[i] = SOCK_ERROR;
|
||||
}
|
||||
}
|
||||
/* remove any closed sockets */
|
||||
for(i=0, dst=0; i < global.server_sockets; i++) {
|
||||
if (global.serversock[i] == SOCK_ERROR)
|
||||
continue;
|
||||
if (i!=dst)
|
||||
global.serversock[dst] = global.serversock[i];
|
||||
dst++;
|
||||
}
|
||||
global.server_sockets = dst;
|
||||
return SOCK_ERROR;
|
||||
}
|
||||
#else
|
||||
fd_set rfds;
|
||||
struct timeval tv, *p=NULL;
|
||||
int i, ret;
|
||||
sock_t max = SOCK_ERROR;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
|
||||
for(i=0; i < global.server_sockets; i++) {
|
||||
FD_SET(global.serversock[i], &rfds);
|
||||
if (max == SOCK_ERROR || global.serversock[i] > max)
|
||||
max = global.serversock[i];
|
||||
}
|
||||
|
||||
if(timeout >= 0) {
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
p = &tv;
|
||||
}
|
||||
|
||||
ret = select(max+1, &rfds, NULL, NULL, p);
|
||||
if(ret < 0) {
|
||||
return SOCK_ERROR;
|
||||
} else if(ret == 0) {
|
||||
return SOCK_ERROR;
|
||||
} else {
|
||||
for(i=0; i < global.server_sockets; i++) {
|
||||
if(FD_ISSET(global.serversock[i], &rfds))
|
||||
return global.serversock[i];
|
||||
}
|
||||
return SOCK_ERROR; /* Should be impossible, stop compiler warnings */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static connection_t *_accept_connection(int duration)
|
||||
{
|
||||
sock_t sock, serversock;
|
||||
char *ip;
|
||||
|
||||
serversock = wait_for_serversock (duration);
|
||||
if (serversock == SOCK_ERROR)
|
||||
return NULL;
|
||||
|
||||
/* malloc enough room for a full IP address (including ipv6) */
|
||||
ip = (char *)malloc(MAX_ADDR_LEN);
|
||||
|
||||
sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
|
||||
if (sock != SOCK_ERROR) {
|
||||
connection_t *con = NULL;
|
||||
/* 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);
|
||||
|
||||
if (matchfile_match_allow_deny(allowed_ip, banned_ip, ip))
|
||||
con = connection_create (sock, serversock, ip);
|
||||
if (con)
|
||||
return con;
|
||||
sock_close(sock);
|
||||
} else {
|
||||
if (!sock_recoverable(sock_error())) {
|
||||
ICECAST_LOG_WARN("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
|
||||
thread_sleep(500000);
|
||||
}
|
||||
}
|
||||
free(ip);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
@ -704,16 +610,14 @@ static void _add_request_queue(client_queue_t *node)
|
||||
static client_queue_t *create_client_node(client_t *client)
|
||||
{
|
||||
client_queue_t *node = calloc (1, sizeof (client_queue_t));
|
||||
ice_config_t *config;
|
||||
listener_t *listener;
|
||||
const listener_t *listener;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
node->client = client;
|
||||
|
||||
config = config_get_config();
|
||||
listener = config_get_listen_sock(config, client->con);
|
||||
listener = listensocket_get_listener(client->con->listensocket_effective);
|
||||
|
||||
if (listener) {
|
||||
if (listener->shoutcast_compat)
|
||||
@ -725,8 +629,6 @@ static client_queue_t *create_client_node(client_t *client)
|
||||
node->shoutcast_mount = strdup(listener->shoutcast_mount);
|
||||
}
|
||||
|
||||
config_release_config();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -776,7 +678,7 @@ void connection_accept_loop(void)
|
||||
config_release_config();
|
||||
|
||||
while (global.running == ICECAST_RUNNING) {
|
||||
con = _accept_connection (duration);
|
||||
con = listensocket_container_accept(global.listensockets, duration);
|
||||
|
||||
if (con) {
|
||||
connection_queue(con);
|
||||
@ -1234,7 +1136,7 @@ static int _handle_resources(client_t *client, char **uri)
|
||||
char *vhost_colon;
|
||||
char *new_uri = NULL;
|
||||
ice_config_t *config;
|
||||
listener_t *listen_sock;
|
||||
const listener_t *listen_sock;
|
||||
resource_t *resource;
|
||||
|
||||
if (http_host) {
|
||||
@ -1246,8 +1148,8 @@ static int _handle_resources(client_t *client, char **uri)
|
||||
}
|
||||
}
|
||||
|
||||
listen_sock = listensocket_get_listener(client->con->listensocket_effective);
|
||||
config = config_get_config();
|
||||
listen_sock = config_get_listen_sock (config, client->con);
|
||||
if (listen_sock) {
|
||||
serverhost = listen_sock->bind_address;
|
||||
serverport = listen_sock->port;
|
||||
@ -1497,7 +1399,7 @@ static void __prepare_shoutcast_admin_cgi_request(client_t *client)
|
||||
ice_config_t *config;
|
||||
const char *sc_mount;
|
||||
const char *pass = httpp_get_query_param(client->parser, "pass");
|
||||
listener_t *listener;
|
||||
const listener_t *listener;
|
||||
|
||||
if (pass == NULL) {
|
||||
ICECAST_LOG_ERROR("missing pass parameter");
|
||||
@ -1509,10 +1411,10 @@ static void __prepare_shoutcast_admin_cgi_request(client_t *client)
|
||||
return;
|
||||
}
|
||||
|
||||
listener = listensocket_get_listener(client->con->listensocket_effective);
|
||||
global_lock();
|
||||
config = config_get_config();
|
||||
sc_mount = config->shoutcast_mount;
|
||||
listener = config_get_listen_sock(config, client->con);
|
||||
|
||||
if (listener && listener->shoutcast_mount)
|
||||
sc_mount = listener->shoutcast_mount;
|
||||
@ -1733,21 +1635,14 @@ static void _handle_connection(void)
|
||||
|
||||
|
||||
/* called when listening thread is not checking for incoming connections */
|
||||
int connection_setup_sockets (ice_config_t *config)
|
||||
void connection_setup_sockets (ice_config_t *config)
|
||||
{
|
||||
int count = 0;
|
||||
listener_t *listener, **prev;
|
||||
|
||||
global_lock();
|
||||
if (global.serversock) {
|
||||
for (; count < global.server_sockets; count++)
|
||||
sock_close (global.serversock [count]);
|
||||
free (global.serversock);
|
||||
global.serversock = NULL;
|
||||
}
|
||||
refobject_unref(global.listensockets);
|
||||
|
||||
if (config == NULL) {
|
||||
global_unlock();
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup the banned/allowed IP filenames from the xml */
|
||||
@ -1763,57 +1658,12 @@ int connection_setup_sockets (ice_config_t *config)
|
||||
allowed_ip = matchfile_new(config->allowfile);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
global.serversock = calloc(config->listen_sock_count, sizeof(sock_t));
|
||||
global.listensockets = listensocket_container_new();
|
||||
listensocket_container_configure(global.listensockets, config);
|
||||
|
||||
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, ICECAST_LISTEN_QUEUE) == SOCK_ERROR) {
|
||||
sock_close (sock);
|
||||
break;
|
||||
}
|
||||
/* 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);
|
||||
sock_set_blocking (sock, 0);
|
||||
successful = 1;
|
||||
global.serversock [count] = sock;
|
||||
count++;
|
||||
} while(0);
|
||||
if (successful == 0) {
|
||||
if (listener->bind_address) {
|
||||
ICECAST_LOG_ERROR("Could not create listener socket on port %d bind %s",
|
||||
listener->port, listener->bind_address);
|
||||
} else {
|
||||
ICECAST_LOG_ERROR("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) {
|
||||
ICECAST_LOG_INFO("listener socket on port %d address %s", listener->port, listener->bind_address);
|
||||
} else {
|
||||
ICECAST_LOG_INFO("listener socket on port %d", listener->port);
|
||||
}
|
||||
prev = &listener->next;
|
||||
listener = listener->next;
|
||||
}
|
||||
global.server_sockets = count;
|
||||
global_unlock();
|
||||
|
||||
if (count == 0)
|
||||
ICECAST_LOG_ERROR("No listening sockets established");
|
||||
|
||||
return count;
|
||||
listensocket_container_setup(global.listensockets);;
|
||||
}
|
||||
|
||||
|
||||
@ -1829,5 +1679,7 @@ void connection_close(connection_t *con)
|
||||
free(con->ip);
|
||||
if (con->readbufferlen)
|
||||
free(con->readbuffer);
|
||||
refobject_unref(con->listensocket_real);
|
||||
refobject_unref(con->listensocket_effective);
|
||||
free(con);
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ struct connection_tag {
|
||||
uint64_t sent_bytes;
|
||||
|
||||
sock_t sock;
|
||||
sock_t serversock;
|
||||
listensocket_t *listensocket_real;
|
||||
listensocket_t *listensocket_effective;
|
||||
int error;
|
||||
|
||||
tlsmode_t tlsmode;
|
||||
@ -50,9 +51,9 @@ void connection_initialize(void);
|
||||
void connection_shutdown(void);
|
||||
void connection_reread_config(ice_config_t *config);
|
||||
void connection_accept_loop(void);
|
||||
int connection_setup_sockets(ice_config_t *config);
|
||||
void connection_setup_sockets(ice_config_t *config);
|
||||
void connection_close(connection_t *con);
|
||||
connection_t *connection_create(sock_t sock, sock_t serversock, char *ip);
|
||||
connection_t *connection_create(sock_t sock, listensocket_t *listensocket_real, listensocket_t* listensocket_effective, char *ip);
|
||||
int connection_complete_source(source_t *source, int response);
|
||||
void connection_queue(connection_t *con);
|
||||
void connection_uses_tls(connection_t *con);
|
||||
|
@ -31,7 +31,7 @@ static mutex_t _global_mutex;
|
||||
|
||||
void global_initialize(void)
|
||||
{
|
||||
global.server_sockets = 0;
|
||||
global.listensockets = NULL;
|
||||
global.relays = NULL;
|
||||
global.master_relays = NULL;
|
||||
global.running = 0;
|
||||
|
@ -22,13 +22,11 @@
|
||||
|
||||
#include "common/thread/thread.h"
|
||||
#include "common/avl/avl.h"
|
||||
#include "common/net/sock.h"
|
||||
#include "icecasttypes.h"
|
||||
|
||||
typedef struct ice_global_tag
|
||||
{
|
||||
sock_t *serversock;
|
||||
int server_sockets;
|
||||
listensocket_container_t *listensockets;
|
||||
|
||||
int running;
|
||||
|
||||
|
@ -104,6 +104,11 @@ typedef struct reportxml_tag reportxml_t;
|
||||
typedef struct reportxml_node_tag reportxml_node_t;
|
||||
typedef struct reportxml_database_tag reportxml_database_t;
|
||||
|
||||
/* ---[ listensocket.[ch] ]--- */
|
||||
|
||||
typedef struct listensocket_container_tag listensocket_container_t;
|
||||
typedef struct listensocket_tag listensocket_t;
|
||||
|
||||
/* ---[ refobject.[ch] ]--- */
|
||||
|
||||
typedef struct refobject_base_tag refobject_base_t;
|
||||
@ -116,6 +121,8 @@ typedef union __attribute__ ((__transparent_union__)) {
|
||||
reportxml_t *reportxml;
|
||||
reportxml_node_t *reportxml_node;
|
||||
reportxml_database_t *reportxml_database;
|
||||
listensocket_container_t *listensocket_container;
|
||||
listensocket_t *listensocket;
|
||||
} refobject_t;
|
||||
#else
|
||||
typedef void * refobject_t;
|
||||
|
421
src/listensocket.c
Normal file
421
src/listensocket.c
Normal file
@ -0,0 +1,421 @@
|
||||
/* Icecast
|
||||
*
|
||||
* This program is distributed under the GNU General Public License, version 2.
|
||||
* A copy of this license is included with this source.
|
||||
*
|
||||
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||
*/
|
||||
|
||||
/**
|
||||
* Listen socket operations.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "common/net/sock.h"
|
||||
|
||||
#include "listensocket.h"
|
||||
#include "global.h"
|
||||
#include "connection.h"
|
||||
#include "refobject.h"
|
||||
|
||||
#include "logging.h"
|
||||
#define CATMODULE "listensocket"
|
||||
|
||||
|
||||
struct listensocket_container_tag {
|
||||
refobject_base_t __base;
|
||||
listensocket_t **sock;
|
||||
int *sockref;
|
||||
size_t sock_len;
|
||||
};
|
||||
struct listensocket_tag {
|
||||
refobject_base_t __base;
|
||||
size_t sockrefc;
|
||||
listener_t *listener;
|
||||
sock_t sock;
|
||||
};
|
||||
|
||||
static listensocket_t * listensocket_new(const listener_t *listener);
|
||||
#ifdef HAVE_POLL
|
||||
static int listensocket__poll_fill(listensocket_t *self, struct pollfd *p);
|
||||
#else
|
||||
static int listensocket__select_set(listensocket_t *self, fd_set *set, int *max);
|
||||
static int listensocket__select_isset(listensocket_t *self, fd_set *set);
|
||||
#endif
|
||||
|
||||
static void listensocket_container_clear_sockets(listensocket_container_t *self)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (self->sock == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->sock_len; i++) {
|
||||
if (self->sock[i] != NULL) {
|
||||
if (self->sockref[i]) {
|
||||
listensocket_unrefsock(self->sock[i]);
|
||||
}
|
||||
refobject_unref(self->sock[i]);
|
||||
self->sock[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
self->sock_len = 0;
|
||||
free(self->sock);
|
||||
free(self->sockref);
|
||||
self->sock = NULL;
|
||||
self->sockref = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void __listensocket_container_free(refobject_t self, void **userdata)
|
||||
{
|
||||
listensocket_container_t *container = REFOBJECT_TO_TYPE(self, listensocket_container_t *);
|
||||
listensocket_container_clear_sockets(container);
|
||||
}
|
||||
|
||||
listensocket_container_t * listensocket_container_new(void)
|
||||
{
|
||||
listensocket_container_t *self = REFOBJECT_TO_TYPE(refobject_new(sizeof(listensocket_container_t), __listensocket_container_free, NULL, NULL, NULL), listensocket_container_t *);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
|
||||
self->sock = NULL;
|
||||
self->sock_len = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
int listensocket_container_configure(listensocket_container_t *self, const ice_config_t *config)
|
||||
{
|
||||
listensocket_t **n;
|
||||
listener_t *cur;
|
||||
int *r;
|
||||
size_t i;
|
||||
|
||||
if (!self || !config)
|
||||
return -1;
|
||||
|
||||
if (!config->listen_sock_count) {
|
||||
listensocket_container_clear_sockets(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = calloc(config->listen_sock_count, sizeof(listensocket_t *));
|
||||
r = calloc(config->listen_sock_count, sizeof(int));
|
||||
if (!n || !r) {
|
||||
free(n);
|
||||
free(r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = config->listen_sock;
|
||||
for (i = 0; i < config->listen_sock_count; i++) {
|
||||
if (cur) {
|
||||
n[i] = listensocket_new(cur);
|
||||
} else {
|
||||
n[i] = NULL;
|
||||
}
|
||||
if (n[i] == NULL) {
|
||||
for (; i; i--) {
|
||||
refobject_unref(n[i - 1]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
listensocket_container_clear_sockets(self);
|
||||
|
||||
self->sock = n;
|
||||
self->sockref = r;
|
||||
self->sock_len = config->listen_sock_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int listensocket_container_setup(listensocket_container_t *self) {
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!self)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < self->sock_len; i++) {
|
||||
if (self->sockref[i])
|
||||
continue;
|
||||
|
||||
if (listensocket_refsock(self->sock[i]) == 0) {
|
||||
self->sockref[i] = 1;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static connection_t * listensocket_container_accept__inner(listensocket_container_t *self, int timeout)
|
||||
{
|
||||
#ifdef HAVE_POLL
|
||||
struct pollfd ufds[self->sock_len];
|
||||
listensocket_t *socks[self->sock_len];
|
||||
size_t i, found, p;
|
||||
int ok;
|
||||
int ret;
|
||||
|
||||
for (i = 0, found = 0; i < self->sock_len; i++) {
|
||||
ok = self->sockref[i];
|
||||
|
||||
if (ok && listensocket__poll_fill(self->sock[i], &(ufds[found])) == -1) {
|
||||
ICECAST_LOG_WARN("Can not poll on closed socket.");
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
socks[found] = self->sock[i];
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ICECAST_LOG_ERROR("No sockets found to poll on.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = poll(ufds, found, timeout);
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < found; i++) {
|
||||
if (ufds[i].revents & POLLIN) {
|
||||
return listensocket_accept(socks[i]);
|
||||
}
|
||||
|
||||
if (!(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)))
|
||||
continue;
|
||||
|
||||
for (p = 0; p < self->sock_len; p++) {
|
||||
if (self->sock[p] == socks[i]) {
|
||||
if (self->sockref[p]) {
|
||||
ICECAST_LOG_ERROR("Closing listen socket in error state.");
|
||||
listensocket_unrefsock(socks[i]);
|
||||
self->sockref[p] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#else
|
||||
fd_set rfds;
|
||||
size_t i;
|
||||
struct timeval tv, *p=NULL;
|
||||
int ret;
|
||||
int max = -1;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
|
||||
if (timeout >= 0) {
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
p = &tv;
|
||||
}
|
||||
|
||||
for (i = 0; i < self->sock_len; i++) {
|
||||
if (self->sockref[i]) {
|
||||
listensocket__select_set(self->sock[i], &rfds, &max);
|
||||
}
|
||||
}
|
||||
|
||||
ret = select(max+1, &rfds, NULL, NULL, p);
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < self->sock_len; i++) {
|
||||
if (self->sockref[i]) {
|
||||
if (listensocket__select_isset(self->sock[i], &rfds)) {
|
||||
return listensocket_accept(self->sock[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
connection_t * listensocket_container_accept(listensocket_container_t *self, int timeout)
|
||||
{
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
return listensocket_container_accept__inner(self, timeout);
|
||||
}
|
||||
|
||||
static void __listensocket_free(refobject_t self, void **userdata)
|
||||
{
|
||||
listensocket_t *listensocket = REFOBJECT_TO_TYPE(self, listensocket_t *);
|
||||
|
||||
if (listensocket->sockrefc) {
|
||||
ICECAST_LOG_ERROR("BUG: listensocket->sockrefc == 0 && listensocket->sockrefc == %zu", listensocket->sockrefc);
|
||||
listensocket->sockrefc = 1;
|
||||
listensocket_unrefsock(listensocket);
|
||||
}
|
||||
|
||||
while ((listensocket->listener = config_clear_listener(listensocket->listener)));
|
||||
}
|
||||
|
||||
static listensocket_t * listensocket_new(const listener_t *listener) {
|
||||
listensocket_t *self;
|
||||
|
||||
if (listener == NULL)
|
||||
return NULL;
|
||||
|
||||
self = REFOBJECT_TO_TYPE(refobject_new(sizeof(listensocket_t), __listensocket_free, NULL, NULL, NULL), listensocket_t *);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
self->sock = SOCK_ERROR;
|
||||
|
||||
self->listener = config_copy_listener_one(listener);
|
||||
if (self->listener == NULL) {
|
||||
refobject_unref(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
int listensocket_refsock(listensocket_t *self)
|
||||
{
|
||||
if (!self)
|
||||
return -1;
|
||||
|
||||
if (self->sockrefc) {
|
||||
self->sockrefc++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
self->sock = sock_get_server_socket(self->listener->port, self->listener->bind_address);
|
||||
if (self->sock == SOCK_ERROR)
|
||||
return -1;
|
||||
|
||||
if (sock_listen(self->sock, ICECAST_LISTEN_QUEUE) == SOCK_ERROR) {
|
||||
sock_close(self->sock);
|
||||
self->sock = SOCK_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->listener->so_sndbuf)
|
||||
sock_set_send_buffer(self->sock, self->listener->so_sndbuf);
|
||||
|
||||
sock_set_blocking(self->sock, 0);
|
||||
|
||||
self->sockrefc++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int listensocket_unrefsock(listensocket_t *self)
|
||||
{
|
||||
if (!self)
|
||||
return -1;
|
||||
|
||||
self->sockrefc--;
|
||||
if (self->sockrefc)
|
||||
return 0;
|
||||
|
||||
if (self->sock == SOCK_ERROR)
|
||||
return 0;
|
||||
|
||||
sock_close(self->sock);
|
||||
self->sock = SOCK_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
connection_t * listensocket_accept(listensocket_t *self)
|
||||
{
|
||||
connection_t *con;
|
||||
sock_t sock;
|
||||
char *ip;
|
||||
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
ip = calloc(MAX_ADDR_LEN, 1);
|
||||
if (!ip)
|
||||
return NULL;
|
||||
|
||||
sock = sock_accept(self->sock, ip, MAX_ADDR_LEN);
|
||||
if (sock == SOCK_ERROR) {
|
||||
free(ip);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strncmp(ip, "::ffff:", 7) == 0) {
|
||||
memmove(ip, ip+7, strlen(ip+7)+1);
|
||||
}
|
||||
|
||||
con = connection_create(sock, self, self, ip);
|
||||
if (con == NULL) {
|
||||
sock_close(sock);
|
||||
free(ip);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
const listener_t * listensocket_get_listener(listensocket_t *self)
|
||||
{
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
return self->listener;
|
||||
}
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
static int listensocket__poll_fill(listensocket_t *self, struct pollfd *p)
|
||||
{
|
||||
if (!self || self->sock == SOCK_ERROR)
|
||||
return -1;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->fd = self->sock;
|
||||
p->events = POLLIN;
|
||||
p->revents = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int listensocket__select_set(listensocket_t *self, fd_set *set, int *max)
|
||||
{
|
||||
if (!self || self->sock == SOCK_ERROR)
|
||||
return -1;
|
||||
|
||||
if (*max < self->sock)
|
||||
*max = self->sock;
|
||||
|
||||
FD_SET(self->sock, set);
|
||||
return 0;
|
||||
}
|
||||
static int listensocket__select_isset(listensocket_t *self, fd_set *set)
|
||||
{
|
||||
if (!self || self->sock == SOCK_ERROR)
|
||||
return -1;
|
||||
return FD_ISSET(self->sock, set);
|
||||
}
|
||||
#endif
|
25
src/listensocket.h
Normal file
25
src/listensocket.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* Icecast
|
||||
*
|
||||
* This program is distributed under the GNU General Public License, version 2.
|
||||
* A copy of this license is included with this source.
|
||||
*
|
||||
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||
*/
|
||||
|
||||
#ifndef __LISTENSOCKET_H__
|
||||
#define __LISTENSOCKET_H__
|
||||
|
||||
#include "icecasttypes.h"
|
||||
#include "cfgfile.h"
|
||||
|
||||
listensocket_container_t * listensocket_container_new(void);
|
||||
int listensocket_container_configure(listensocket_container_t *self, const ice_config_t *config);
|
||||
int listensocket_container_setup(listensocket_container_t *self);
|
||||
connection_t * listensocket_container_accept(listensocket_container_t *self, int timeout);
|
||||
|
||||
int listensocket_refsock(listensocket_t *self);
|
||||
int listensocket_unrefsock(listensocket_t *self);
|
||||
connection_t * listensocket_accept(listensocket_t *self);
|
||||
const listener_t * listensocket_get_listener(listensocket_t *self);
|
||||
|
||||
#endif
|
22
src/main.c
22
src/main.c
@ -318,20 +318,6 @@ static int _start_logging(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int _start_listening(void)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i < global.server_sockets; i++) {
|
||||
if (sock_listen(global.serversock[i], ICECAST_LISTEN_QUEUE) == SOCK_ERROR)
|
||||
return 0;
|
||||
|
||||
sock_set_blocking(global.serversock[i], 0);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pidfile_update(ice_config_t *config, int always_try)
|
||||
{
|
||||
char *newpidfile = NULL;
|
||||
@ -389,13 +375,7 @@ static int _server_proc_init(void)
|
||||
{
|
||||
ice_config_t *config = config_get_config_unlocked();
|
||||
|
||||
if (connection_setup_sockets (config) < 1)
|
||||
return 0;
|
||||
|
||||
if (!_start_listening()) {
|
||||
_fatal_error("Failed trying to listen on server socket");
|
||||
return 0;
|
||||
}
|
||||
connection_setup_sockets(config);
|
||||
|
||||
pidfile_update(config, 1);
|
||||
|
||||
|
@ -200,7 +200,7 @@ static client_t *open_relay_connection (relay_server *relay)
|
||||
ICECAST_LOG_WARN("Failed to connect to %s:%d", server, port);
|
||||
break;
|
||||
}
|
||||
con = connection_create (streamsock, -1, strdup (server));
|
||||
con = connection_create(streamsock, NULL, NULL, strdup(server));
|
||||
|
||||
/* At this point we may not know if we are relaying an mp3 or vorbis
|
||||
* stream, but only send the icy-metadata header if the relay details
|
||||
|
Loading…
Reference in New Issue
Block a user