mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-01-03 14:56:34 -05:00
206c66d4b2
When select(2) is used we only support as many file handles as there is space in fd_set. The limit is given in FD_SETSIZE. Once we use filehandles with an value >= FD_SETSIZE we might end up corrupting memory. Therefore we try to avoid those calls by rejecting handles >= FD_SETSIZE very early. This patch should keep most problems away. However it does not actually ensure we don't corrupt memory. That check has been skipped for performance reasons. Keeping in mind that the use of select(2) is already deprecated.
999 lines
26 KiB
C
999 lines
26 KiB
C
/* 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-2020, 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>
|
|
#elif HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "common/net/sock.h"
|
|
#include "common/thread/thread.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;
|
|
rwlock_t rwlock;
|
|
bool prefer_inet6;
|
|
listensocket_t **sock;
|
|
int *sockref;
|
|
size_t sock_len;
|
|
void (*sockcount_cb)(size_t count, void *userdata);
|
|
void *sockcount_userdata;
|
|
};
|
|
struct listensocket_tag {
|
|
refobject_base_t __base;
|
|
size_t sockrefc;
|
|
mutex_t lock;
|
|
rwlock_t listener_rwlock;
|
|
listener_t *listener;
|
|
listener_t *listener_update;
|
|
sock_t sock;
|
|
};
|
|
|
|
static int listensocket_container_configure__unlocked(listensocket_container_t *self, const ice_config_t *config);
|
|
static int listensocket_container_setup__unlocked(listensocket_container_t *self);
|
|
static ssize_t listensocket_container_sockcount__unlocked(listensocket_container_t *self);
|
|
static listensocket_t * listensocket_new(const listener_t *listener);
|
|
static int listensocket_apply_config(listensocket_t *self);
|
|
static int listensocket_apply_config__unlocked(listensocket_t *self);
|
|
static int listensocket_set_update(listensocket_t *self, const listener_t *listener);
|
|
static int listensocket_refsock(listensocket_t *self, bool prefer_inet6);
|
|
static int listensocket_unrefsock(listensocket_t *self);
|
|
#ifdef HAVE_POLL
|
|
static inline int listensocket__poll_fill(listensocket_t *self, struct pollfd *p);
|
|
#else
|
|
static inline int listensocket__select_set(listensocket_t *self, fd_set *set, int *max);
|
|
static inline int listensocket__select_isset(listensocket_t *self, fd_set *set);
|
|
#endif
|
|
|
|
static inline const char * __string_default(const char *str, const char *def)
|
|
{
|
|
return str != NULL ? str : def;
|
|
}
|
|
|
|
static inline int __socket_listen(sock_t serversock, const listener_t *listener)
|
|
{
|
|
int listen_backlog = listener->listen_backlog;
|
|
|
|
if (listen_backlog < 1)
|
|
listen_backlog = ICECAST_LISTEN_QUEUE;
|
|
if (listen_backlog > 128) {
|
|
listen_backlog = 128;
|
|
ICECAST_LOG_WARN("Listen backlog for listen socket on %s port %i is set insanely high. Limiting to sane range.", __string_default(listener->bind_address, "<ANY>"), listener->port);
|
|
}
|
|
|
|
return sock_listen(serversock, listen_backlog);
|
|
}
|
|
|
|
static inline int __listener_cmp(const listener_t *a, const listener_t *b)
|
|
{
|
|
if (a == b)
|
|
return 1;
|
|
|
|
if (a->port != b->port)
|
|
return 0;
|
|
|
|
if ((a->bind_address == NULL && b->bind_address != NULL) ||
|
|
(a->bind_address != NULL && b->bind_address == NULL))
|
|
return 0;
|
|
|
|
|
|
if (a->bind_address != NULL && b->bind_address != NULL && strcmp(a->bind_address, b->bind_address) != 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static inline void __call_sockcount_cb(listensocket_container_t *self)
|
|
{
|
|
if (self->sockcount_cb == NULL)
|
|
return;
|
|
|
|
self->sockcount_cb(listensocket_container_sockcount__unlocked(self), self->sockcount_userdata);
|
|
}
|
|
|
|
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;
|
|
|
|
__call_sockcount_cb(self);
|
|
}
|
|
|
|
|
|
static void __listensocket_container_free(refobject_t self, void **userdata)
|
|
{
|
|
listensocket_container_t *container = REFOBJECT_TO_TYPE(self, listensocket_container_t *);
|
|
thread_rwlock_wlock(&container->rwlock);
|
|
__listensocket_container_clear_sockets(container);
|
|
thread_rwlock_unlock(&container->rwlock);
|
|
thread_rwlock_destroy(&container->rwlock);
|
|
}
|
|
|
|
int __listensocket_container_new(refobject_t self, const refobject_type_t *type, va_list ap)
|
|
{
|
|
listensocket_container_t *ret = REFOBJECT_TO_TYPE(self, listensocket_container_t*);
|
|
|
|
ret->sock = NULL;
|
|
ret->sock_len = 0;
|
|
ret->sockcount_cb = NULL;
|
|
ret->sockcount_userdata = NULL;
|
|
|
|
thread_rwlock_create(&ret->rwlock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
REFOBJECT_DEFINE_TYPE(listensocket_container_t,
|
|
REFOBJECT_DEFINE_TYPE_FREE(__listensocket_container_free),
|
|
REFOBJECT_DEFINE_TYPE_NEW(__listensocket_container_new)
|
|
);
|
|
|
|
static inline void __find_matching_entry(listensocket_container_t *self, const listener_t *listener, listensocket_t ***found, int **ref)
|
|
{
|
|
const listener_t *b;
|
|
int test;
|
|
size_t i;
|
|
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
if (self->sock[i] != NULL) {
|
|
if (self->sockref[i]) {
|
|
b = listensocket_get_listener(self->sock[i]);
|
|
test = __listener_cmp(listener, b);
|
|
listensocket_release_listener(self->sock[i]);
|
|
if (test == 1) {
|
|
*found = &(self->sock[i]);
|
|
*ref = &(self->sockref[i]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*found = NULL;
|
|
*ref = NULL;
|
|
}
|
|
|
|
int listensocket_container_configure(listensocket_container_t *self, const ice_config_t *config)
|
|
{
|
|
int ret;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_rwlock_wlock(&self->rwlock);
|
|
ret = listensocket_container_configure__unlocked(self, config);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int listensocket_container_configure__unlocked(listensocket_container_t *self, const ice_config_t *config)
|
|
{
|
|
listensocket_t **n;
|
|
listensocket_t **match;
|
|
listener_t *cur;
|
|
int *r;
|
|
int *m;
|
|
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) {
|
|
__find_matching_entry(self, cur, &match, &m);
|
|
if (match) {
|
|
n[i] = *match;
|
|
r[i] = 1;
|
|
*match = NULL;
|
|
*m = 0;
|
|
listensocket_set_update(n[i], cur);
|
|
} else {
|
|
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_configure_and_setup(listensocket_container_t *self, const ice_config_t *config)
|
|
{
|
|
void (*cb)(size_t count, void *userdata);
|
|
int ret;
|
|
bool prefer_inet6;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
prefer_inet6 = sock_is_ipv4_mapped_supported(); /* test before we enter lock to minimise locked time */
|
|
|
|
thread_rwlock_wlock(&self->rwlock);
|
|
self->prefer_inet6 = prefer_inet6;
|
|
cb = self->sockcount_cb;
|
|
self->sockcount_cb = NULL;
|
|
|
|
if (listensocket_container_configure__unlocked(self, config) == 0) {
|
|
ret = listensocket_container_setup__unlocked(self);
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
self->sockcount_cb = cb;
|
|
__call_sockcount_cb(self);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int listensocket_container_setup(listensocket_container_t *self)
|
|
{
|
|
int ret;
|
|
bool prefer_inet6;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
prefer_inet6 = sock_is_ipv4_mapped_supported(); /* test before we enter lock to minimise locked time */
|
|
|
|
thread_rwlock_wlock(&self->rwlock);
|
|
self->prefer_inet6 = prefer_inet6;
|
|
ret = listensocket_container_setup__unlocked(self);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return ret;
|
|
}
|
|
static int listensocket_container_setup__unlocked(listensocket_container_t *self)
|
|
{
|
|
listener_type_t type;
|
|
size_t i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
listensocket_apply_config(self->sock[i]);
|
|
|
|
type = listensocket_get_type(self->sock[i]);
|
|
if (self->sockref[i] && type == LISTENER_TYPE_VIRTUAL) {
|
|
if (listensocket_unrefsock(self->sock[i]) == 0) {
|
|
self->sockref[i] = 0;
|
|
}
|
|
} else if (!self->sockref[i] && type != LISTENER_TYPE_VIRTUAL) {
|
|
if (listensocket_refsock(self->sock[i], self->prefer_inet6) == 0) {
|
|
self->sockref[i] = 1;
|
|
} else {
|
|
ICECAST_LOG_DEBUG("Can not ref socket.");
|
|
ret = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
__call_sockcount_cb(self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static listensocket_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 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;
|
|
__call_sockcount_cb(self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 self->sock[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
connection_t * listensocket_container_accept(listensocket_container_t *self, int timeout)
|
|
{
|
|
listensocket_t *ls;
|
|
connection_t *ret;
|
|
|
|
if (!self)
|
|
return NULL;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
ls = listensocket_container_accept__inner(self, timeout);
|
|
refobject_ref(ls);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
ret = listensocket_accept(ls, self);
|
|
refobject_unref(ls);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int listensocket_container_set_sockcount_cb(listensocket_container_t *self, void (*cb)(size_t count, void *userdata), void *userdata)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_rwlock_wlock(&self->rwlock);
|
|
self->sockcount_cb = cb;
|
|
self->sockcount_userdata = userdata;
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t listensocket_container_sockcount(listensocket_container_t *self)
|
|
{
|
|
ssize_t ret;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
ret = listensocket_container_sockcount__unlocked(self);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t listensocket_container_sockcount__unlocked(listensocket_container_t *self)
|
|
{
|
|
ssize_t count = 0;
|
|
size_t i;
|
|
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
if (self->sockref[i]) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
listensocket_t * listensocket_container_get_by_id(listensocket_container_t *self, const char *id)
|
|
{
|
|
size_t i;
|
|
const listener_t *listener;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
if (self->sock[i] != NULL) {
|
|
listener = listensocket_get_listener(self->sock[i]);
|
|
if (listener) {
|
|
if (listener->id != NULL && strcmp(listener->id, id) == 0) {
|
|
if (refobject_ref(self->sock[i]) == 0) {
|
|
listensocket_release_listener(self->sock[i]);
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
return self->sock[i];
|
|
}
|
|
}
|
|
listensocket_release_listener(self->sock[i]);
|
|
}
|
|
}
|
|
}
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* may return NULL if there is no default */
|
|
listensocket_t * listensocket_container_get_default(listensocket_container_t *self)
|
|
{
|
|
listensocket_t *ret = NULL;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
if (self->sock_len == 1) {
|
|
if (refobject_ref(self->sock[0]) == 0) {
|
|
ret = self->sock[0];
|
|
}
|
|
}
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
listensocket_t ** listensocket_container_list_sockets(listensocket_container_t *self)
|
|
{
|
|
listensocket_t **res;
|
|
size_t idx = 0;
|
|
size_t i;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
res = calloc(self->sock_len + 1, sizeof(*res));
|
|
if (!res) {
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
if (self->sock[i] != NULL) {
|
|
refobject_ref(res[idx++] = self->sock[i]);
|
|
}
|
|
}
|
|
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool listensocket_container_is_family_included(listensocket_container_t *self, sock_family_t family)
|
|
{
|
|
size_t i;
|
|
|
|
thread_rwlock_rlock(&self->rwlock);
|
|
for (i = 0; i < self->sock_len; i++) {
|
|
if (self->sock[i] != NULL) {
|
|
if (listensocket_get_family(self->sock[i]) == family) {
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
thread_rwlock_unlock(&self->rwlock);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------- */
|
|
|
|
static void __listensocket_free(refobject_t self, void **userdata)
|
|
{
|
|
listensocket_t *listensocket = REFOBJECT_TO_TYPE(self, listensocket_t *);
|
|
|
|
thread_mutex_lock(&listensocket->lock);
|
|
|
|
if (listensocket->sockrefc) {
|
|
ICECAST_LOG_ERROR("BUG: listensocket->sockrefc == 0 && listensocket->sockrefc == %zu", listensocket->sockrefc);
|
|
listensocket->sockrefc = 1;
|
|
listensocket_unrefsock(listensocket);
|
|
}
|
|
|
|
while ((listensocket->listener_update = config_clear_listener(listensocket->listener_update)));
|
|
thread_rwlock_wlock(&listensocket->listener_rwlock);
|
|
while ((listensocket->listener = config_clear_listener(listensocket->listener)));
|
|
thread_rwlock_unlock(&listensocket->listener_rwlock);
|
|
thread_rwlock_destroy(&listensocket->listener_rwlock);
|
|
thread_mutex_unlock(&listensocket->lock);
|
|
thread_mutex_destroy(&listensocket->lock);
|
|
}
|
|
|
|
REFOBJECT_DEFINE_TYPE(listensocket_t,
|
|
REFOBJECT_DEFINE_TYPE_FREE(__listensocket_free)
|
|
);
|
|
|
|
static listensocket_t * listensocket_new(const listener_t *listener) {
|
|
listensocket_t *self;
|
|
|
|
if (listener == NULL)
|
|
return NULL;
|
|
|
|
self = refobject_new__new(listensocket_t, NULL, NULL, NULL);
|
|
if (!self)
|
|
return NULL;
|
|
|
|
self->sock = SOCK_ERROR;
|
|
|
|
thread_mutex_create(&self->lock);
|
|
thread_rwlock_create(&self->listener_rwlock);
|
|
|
|
self->listener = config_copy_listener_one(listener);
|
|
if (self->listener == NULL) {
|
|
refobject_unref(self);
|
|
return NULL;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static int listensocket_apply_config(listensocket_t *self)
|
|
{
|
|
int ret;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
ret = listensocket_apply_config__unlocked(self);
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int listensocket_apply_config__unlocked(listensocket_t *self)
|
|
{
|
|
const listener_t *listener;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_rwlock_wlock(&self->listener_rwlock);
|
|
if (self->listener_update) {
|
|
if (__listener_cmp(self->listener, self->listener_update) != 1) {
|
|
ICECAST_LOG_ERROR("Tried to apply incomplete configuration to listensocket: bind address mismatch: have %s:%i, got %s:%i",
|
|
__string_default(self->listener->bind_address, "<ANY>"),
|
|
self->listener->port,
|
|
__string_default(self->listener_update->bind_address, "<ANY>"),
|
|
self->listener_update->port
|
|
);
|
|
thread_rwlock_unlock(&self->listener_rwlock);
|
|
return -1;
|
|
}
|
|
|
|
listener = self->listener_update;
|
|
} else {
|
|
listener = self->listener;
|
|
}
|
|
|
|
if (self->sock != SOCK_ERROR) {
|
|
if (listener->so_sndbuf)
|
|
sock_set_send_buffer(self->sock, listener->so_sndbuf);
|
|
|
|
sock_set_blocking(self->sock, 0);
|
|
|
|
__socket_listen(self->sock, listener);
|
|
}
|
|
|
|
if (self->listener_update) {
|
|
while ((self->listener = config_clear_listener(self->listener)));
|
|
self->listener = self->listener_update;
|
|
self->listener_update = NULL;
|
|
}
|
|
|
|
thread_rwlock_unlock(&self->listener_rwlock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int listensocket_set_update(listensocket_t *self, const listener_t *listener)
|
|
{
|
|
listener_t *n;
|
|
|
|
if (!self || !listener)
|
|
return -1;
|
|
|
|
n = config_copy_listener_one(listener);
|
|
if (n == NULL)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
while ((self->listener_update = config_clear_listener(self->listener_update)));
|
|
self->listener_update = n;
|
|
thread_mutex_unlock(&self->lock);
|
|
return 0;
|
|
}
|
|
|
|
static int listensocket_refsock(listensocket_t *self, bool prefer_inet6)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
if (self->sockrefc) {
|
|
self->sockrefc++;
|
|
thread_mutex_unlock(&self->lock);
|
|
return 0;
|
|
}
|
|
|
|
thread_rwlock_rlock(&self->listener_rwlock);
|
|
self->sock = sock_get_server_socket(self->listener->port, self->listener->bind_address, self->listener->bind_address ? false : prefer_inet6);
|
|
thread_rwlock_unlock(&self->listener_rwlock);
|
|
if (self->sock == SOCK_ERROR) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
|
|
#ifndef HAVE_POLL
|
|
if (self->sock >= FD_SETSIZE) {
|
|
sock_close(self->sock);
|
|
self->sock = SOCK_ERROR;
|
|
thread_mutex_unlock(&self->lock);
|
|
ICECAST_LOG_ERROR("Can not listen on socket: %s port %i: System filehandle set overflow", __string_default(self->listener->bind_address, "<ANY>"), self->listener->port);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if (__socket_listen(self->sock, self->listener) == 0) {
|
|
sock_close(self->sock);
|
|
self->sock = SOCK_ERROR;
|
|
thread_rwlock_rlock(&self->listener_rwlock);
|
|
ICECAST_LOG_ERROR("Can not listen on socket: %s port %i", __string_default(self->listener->bind_address, "<ANY>"), self->listener->port);
|
|
thread_rwlock_unlock(&self->listener_rwlock);
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
|
|
if (listensocket_apply_config__unlocked(self) == -1) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
|
|
self->sockrefc++;
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int listensocket_unrefsock(listensocket_t *self)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
self->sockrefc--;
|
|
if (self->sockrefc) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return 0;
|
|
}
|
|
|
|
if (self->sock == SOCK_ERROR) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return 0;
|
|
}
|
|
|
|
sock_close(self->sock);
|
|
self->sock = SOCK_ERROR;
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
connection_t * listensocket_accept(listensocket_t *self, listensocket_container_t *container)
|
|
{
|
|
connection_t *con;
|
|
listensocket_t *effective = NULL;
|
|
sock_t sock;
|
|
char *ip;
|
|
|
|
if (!self)
|
|
return NULL;
|
|
|
|
ip = calloc(MAX_ADDR_LEN, 1);
|
|
if (!ip)
|
|
return NULL;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
sock = sock_accept(self->sock, ip, MAX_ADDR_LEN);
|
|
thread_mutex_unlock(&self->lock);
|
|
if (sock == SOCK_ERROR) {
|
|
free(ip);
|
|
return NULL;
|
|
}
|
|
|
|
ICECAST_LOG_DEBUG("Client (sock=%R, ip=%#H) on socket %p (%#H).", sock, ip, self, self->listener->id);
|
|
|
|
if (strncmp(ip, "::ffff:", 7) == 0) {
|
|
memmove(ip, ip+7, strlen(ip+7)+1);
|
|
}
|
|
|
|
if (self->listener->on_behalf_of) {
|
|
ICECAST_LOG_DEBUG("This socket is acting on behalf of %#H", self->listener->on_behalf_of);
|
|
effective = listensocket_container_get_by_id(container, self->listener->on_behalf_of);
|
|
if (!effective) {
|
|
ICECAST_LOG_ERROR("Can not find listen socket with ID %#H. Will continue on behalf of myself.", self->listener->on_behalf_of);
|
|
}
|
|
}
|
|
|
|
if (!effective) {
|
|
effective = self;
|
|
refobject_ref(effective);
|
|
}
|
|
|
|
con = connection_create(sock, self, effective, ip);
|
|
|
|
refobject_unref(effective);
|
|
|
|
if (con == NULL) {
|
|
sock_close(sock);
|
|
free(ip);
|
|
return NULL;
|
|
}
|
|
|
|
return con;
|
|
}
|
|
|
|
const listener_t * listensocket_get_listener(listensocket_t *self)
|
|
{
|
|
const listener_t *ret;
|
|
|
|
if (!self)
|
|
return NULL;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
thread_rwlock_rlock(&self->listener_rwlock);
|
|
ret = self->listener;
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int listensocket_release_listener(listensocket_t *self)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
/* This is safe with no self->lock holding as unref requires a wlock.
|
|
* A wlock can not be acquired when someone still holds the rlock.
|
|
* In fact this must be done in unlocked state as otherwise we could end up in a
|
|
* dead lock with some 3rd party holding the self->lock for an unrelated operation
|
|
* waiting for a wlock to be come available.
|
|
* -- ph3-der-loewe, 2018-05-11
|
|
*/
|
|
thread_rwlock_unlock(&self->listener_rwlock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
listener_type_t listensocket_get_type(listensocket_t *self)
|
|
{
|
|
listener_type_t ret;
|
|
|
|
if (!self)
|
|
return LISTENER_TYPE_ERROR;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
ret = self->listener->type;
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
sock_family_t listensocket_get_family(listensocket_t *self)
|
|
{
|
|
sock_family_t ret;
|
|
|
|
if (!self)
|
|
return SOCK_FAMILY__ERROR;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
ret = sock_get_family(self->sock);
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_POLL
|
|
static inline int listensocket__poll_fill(listensocket_t *self, struct pollfd *p)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
if (self->sock == SOCK_ERROR) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
|
|
memset(p, 0, sizeof(*p));
|
|
p->fd = self->sock;
|
|
p->events = POLLIN;
|
|
p->revents = 0;
|
|
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static inline int listensocket__select_set(listensocket_t *self, fd_set *set, int *max)
|
|
{
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
if (self->sock == SOCK_ERROR) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
|
|
if (*max < self->sock)
|
|
*max = self->sock;
|
|
|
|
FD_SET(self->sock, set);
|
|
thread_mutex_unlock(&self->lock);
|
|
|
|
return 0;
|
|
}
|
|
static inline int listensocket__select_isset(listensocket_t *self, fd_set *set)
|
|
{
|
|
int ret;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
thread_mutex_lock(&self->lock);
|
|
if (self->sock == SOCK_ERROR) {
|
|
thread_mutex_unlock(&self->lock);
|
|
return -1;
|
|
}
|
|
ret = FD_ISSET(self->sock, set);
|
|
thread_mutex_unlock(&self->lock);
|
|
return ret;
|
|
|
|
}
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------------- */
|
|
|
|
const char * listensocket_type_to_string(listener_type_t type)
|
|
{
|
|
switch (type) {
|
|
case LISTENER_TYPE_ERROR:
|
|
return NULL;
|
|
break;
|
|
case LISTENER_TYPE_NORMAL:
|
|
return "normal";
|
|
break;
|
|
case LISTENER_TYPE_VIRTUAL:
|
|
return "virtual";
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char * listensocket_tlsmode_to_string(tlsmode_t mode)
|
|
{
|
|
switch (mode) {
|
|
case ICECAST_TLSMODE_DISABLED:
|
|
return "disabled";
|
|
break;
|
|
case ICECAST_TLSMODE_AUTO:
|
|
return "auto";
|
|
break;
|
|
case ICECAST_TLSMODE_AUTO_NO_PLAIN:
|
|
return "auto_no_plain";
|
|
break;
|
|
case ICECAST_TLSMODE_RFC2817:
|
|
return "rfc2817";
|
|
break;
|
|
case ICECAST_TLSMODE_RFC2818:
|
|
return "rfc2818";
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|