Commit 5490120d authored by Philipp Schafft's avatar Philipp Schafft 🦁

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.
parent 1a426f7f
......@@ -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 */
......
This diff is collapsed.
......@@ -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;
......
/* 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
/* 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
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment