diff --git a/src/Makefile.am b/src/Makefile.am index 0f4255ee..61e94508 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/admin.c b/src/admin.c index e2bbea4f..eca2837c 100644 --- a/src/admin.c +++ b/src/admin.c @@ -747,7 +747,6 @@ static void command_buildm3u(client_t *client, source_t *source, admin_format_t const char *mount = source->mount; const char *username = NULL; const char *password = NULL; - ice_config_t *config; ssize_t ret; COMMAND_REQUIRE(client, "username", username); @@ -766,17 +765,7 @@ static void command_buildm3u(client_t *client, source_t *source, admin_format_t } - config = config_get_config(); - snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, - "Content-Disposition: attachment; filename=listen.m3u\r\n\r\n" - "http://%s:%s@%s:%d%s\r\n", - username, - password, - config->hostname, - config->port, - mount - ); - config_release_config(); + client_get_baseurl(client, NULL, client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, username, password, "Content-Disposition: attachment; filename=listen.m3u\r\n\r\n", mount, "\r\n"); client->respcode = 200; client->refbuf->len = strlen (client->refbuf->data); @@ -1131,7 +1120,7 @@ static void command_stats(client_t *client, source_t *source, admin_format_t res ICECAST_LOG_DEBUG("Stats request, sending xml stats"); - doc = stats_get_xml(1, mount, client->mode); + doc = stats_get_xml(1, mount, client); admin_send_response(doc, client, response, STATS_HTML_REQUEST); xmlFreeDoc(doc); return; diff --git a/src/cfgfile.c b/src/cfgfile.c index dc1feb43..87e46883 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -10,7 +10,7 @@ * and others (see AUTHORS for details). * Copyright 2011, Dave 'justdave' Miller . * Copyright 2011-2014, Thomas B. "dm8tbr" Ruecker , - * Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft , + * Copyright 2011-2018, Philipp "ph3-der-loewe" Schafft , */ #ifdef HAVE_CONFIG_H @@ -172,6 +172,33 @@ operation_mode config_str_to_omode(const char *str) } } +static listener_type_t config_str_to_listener_type(const char *str) +{ + if (!str || !*str) { + return LISTENER_TYPE_NORMAL; + } else if (strcasecmp(str, "normal") == 0) { + return LISTENER_TYPE_NORMAL; + } else if (strcasecmp(str, "virtual") == 0) { + return LISTENER_TYPE_VIRTUAL; + } else { + ICECAST_LOG_ERROR("Unknown listener type \"%s\", falling back to NORMAL.", str); + return LISTENER_TYPE_NORMAL; + } +} + +char * config_href_to_id(const char *href) +{ + if (!href || !*href) + return NULL; + + if (*href != '#') { + ICECAST_LOG_ERROR("Can not convert string \"%H\" to ID.", href); + return NULL; + } + + return strdup(href+1); +} + static void create_locks(void) { thread_mutex_create(&_locks.relay_lock); @@ -569,6 +596,7 @@ static void config_clear_resource(resource_t *resource) xmlFree(resource->vhost); xmlFree(resource->module); xmlFree(resource->handler); + free(resource->listen_socket); free(resource); resource = nextresource; } @@ -580,6 +608,8 @@ listener_t *config_clear_listener(listener_t *listener) if (listener) { next = listener->next; + if (listener->id) xmlFree(listener->id); + if (listener->on_behalf_of) free(listener->on_behalf_of); if (listener->bind_address) xmlFree(listener->bind_address); if (listener->shoutcast_mount) xmlFree(listener->shoutcast_mount); free (listener); @@ -999,7 +1029,7 @@ static void _parse_root(xmlDocPtr doc, xmlFree(configuration->mimetypes_fn); configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); } else if (xmlStrcmp(node->name, XMLSTR("listen-socket")) == 0) { - _parse_listen_socket(doc, node->xmlChildrenNode, configuration); + _parse_listen_socket(doc, node, configuration); } else if (xmlStrcmp(node->name, XMLSTR("port")) == 0) { tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (tmp && *tmp) { @@ -1744,6 +1774,20 @@ static void _parse_listen_socket(xmlDocPtr doc, return; listener->port = 8000; + listener->id = (char *)xmlGetProp(node, XMLSTR("id")); + + tmp = (char*)xmlGetProp(node, XMLSTR("on-behalf-of")); + if (tmp) { + listener->on_behalf_of = config_href_to_id(tmp); + xmlFree(tmp); + } + + tmp = (char *)xmlGetProp(node, XMLSTR("type")); + listener->type = config_str_to_listener_type(tmp); + xmlFree(tmp); + + node = node->xmlChildrenNode; + do { if (node == NULL) break; @@ -1964,6 +2008,12 @@ static void _parse_resource(xmlDocPtr doc, resource->bind_address = (char *)xmlGetProp(node, XMLSTR("bind-address")); + temp = (char *)xmlGetProp(node, XMLSTR("listen-socket")); + if (temp) { + resource->listen_socket = config_href_to_id(temp); + xmlFree(temp); + } + resource->vhost = (char *)xmlGetProp(node, XMLSTR("vhost")); resource->module = (char *)xmlGetProp(node, XMLSTR("module")); @@ -2473,24 +2523,28 @@ 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++; - } + 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->type = listener->type; + n->id = (char*)xmlStrdup(XMLSTR(listener->id)); + if (listener->on_behalf_of) { + n->on_behalf_of = strdup(listener->on_behalf_of); } - return listener; + 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; } diff --git a/src/cfgfile.h b/src/cfgfile.h index 66117a9d..e7f0b071 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -9,7 +9,7 @@ * Karl Heyes * and others (see AUTHORS for details). * Copyright 2011, Dave 'justdave' Miller . - * Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft , + * Copyright 2011-2018, Philipp "ph3-der-loewe" Schafft , */ #ifndef __CFGFILE_H__ @@ -139,6 +139,7 @@ typedef struct _resource { char *destination; int port; char *bind_address; + char *listen_socket; char *vhost; char *module; char *handler; @@ -147,8 +148,17 @@ typedef struct _resource { struct _resource *next; } resource_t; +typedef enum _listener_type_tag { + LISTENER_TYPE_ERROR, + LISTENER_TYPE_NORMAL, + LISTENER_TYPE_VIRTUAL +} listener_type_t; + typedef struct _listener_t { struct _listener_t *next; + char *id; + char *on_behalf_of; + listener_type_t type; int port; int so_sndbuf; char *bind_address; @@ -262,7 +272,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); diff --git a/src/client.c b/src/client.c index dbf4cea3..bfcf4844 100644 --- a/src/client.c +++ b/src/client.c @@ -48,6 +48,7 @@ #include "util.h" #include "acl.h" +#include "listensocket.h" /* for ADMIN_COMMAND_ERROR */ #include "admin.h" @@ -110,7 +111,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 */ @@ -766,3 +767,91 @@ client_slurp_result_t client_body_skip(client_t *client) break; } } + +ssize_t client_get_baseurl(client_t *client, listensocket_t *listensocket, char *buf, size_t len, const char *user, const char *pw, const char *prefix, const char *suffix0, const char *suffix1) +{ + const listener_t *listener = NULL; + const ice_config_t *config = NULL; + const char *host = NULL; + const char *proto = "http"; + int port = 0; + ssize_t ret; + tlsmode_t tlsmode = ICECAST_TLSMODE_AUTO; + protocol_t protocol = ICECAST_PROTOCOL_HTTP; + + if (!buf || !len) + return -1; + + if (!prefix) + prefix = ""; + + if (!suffix0) + suffix0 = ""; + + if (!suffix1) + suffix1 = ""; + + if (client) { + host = httpp_getvar(client->parser, "host"); + + /* at least a couple of players (fb2k/winamp) are reported to send a + * host header but without the port number. So if we are missing the + * port then lets treat it as if no host line was sent */ + if (host && strchr(host, ':') == NULL) + host = NULL; + + listensocket = client->con->listensocket_effective; + tlsmode = client->con->tlsmode; + protocol = client->protocol; + } + + if (!host && listensocket) { + listener = listensocket_get_listener(listensocket); + if (listener) { + host = listener->bind_address; + port = listener->port; + if (!client) + tlsmode = listener->tls; + } + } + + if (!host) { + config = config_get_config(); + host = config->hostname; + if (!port) + port = config->port; + } + + switch (tlsmode) { + case ICECAST_TLSMODE_DISABLED: + case ICECAST_TLSMODE_AUTO: + switch (protocol) { + case ICECAST_PROTOCOL_HTTP: proto = "http"; break; + case ICECAST_PROTOCOL_SHOUTCAST: proto = "icy"; break; + } + break; + case ICECAST_TLSMODE_AUTO_NO_PLAIN: + case ICECAST_TLSMODE_RFC2817: + case ICECAST_TLSMODE_RFC2818: + switch (protocol) { + case ICECAST_PROTOCOL_HTTP: proto = "https"; break; + case ICECAST_PROTOCOL_SHOUTCAST: proto = "icys"; break; + } + break; + } + + if (host && port) { + ret = snprintf(buf, len, "%s%s://%s%s%s%s%s:%i%s%s", prefix, proto, user ? user : "", pw ? ":" : "", pw ? pw : "", (user || pw) ? "@" : "", host, port, suffix0, suffix1); + } else if (host) { + ret = snprintf(buf, len, "%s%s://%s%s%s%s%s%s%s", prefix, proto, user ? user : "", pw ? ":" : "", pw ? pw : "", (user || pw) ? "@" : "", host, suffix0, suffix1); + } else { + ret = -1; + } + + if (config) + config_release_config(); + if (listener) + listensocket_release_listener(listensocket); + + return ret; +} diff --git a/src/client.h b/src/client.h index 9d426c44..e3c0bb58 100644 --- a/src/client.h +++ b/src/client.h @@ -151,5 +151,6 @@ ssize_t client_body_read(client_t *client, void *buf, size_t len); int client_body_eof(client_t *client); client_slurp_result_t client_body_slurp(client_t *client, void *buf, size_t *len); client_slurp_result_t client_body_skip(client_t *client); +ssize_t client_get_baseurl(client_t *client, listensocket_t *listensocket, char *buf, size_t len, const char *user, const char *pw, const char *prefix, const char *suffix0, const char *suffix1); #endif /* __CLIENT_H__ */ diff --git a/src/connection.c b/src/connection.c index d480a05f..02ec6dfd 100644 --- a/src/connection.c +++ b/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,7 @@ void connection_shutdown(void) void connection_reread_config(ice_config_t *config) { get_tls_certificate(config); + listensocket_container_configure_and_setup(global.listensockets, config); } static unsigned long _next_connection_id(void) @@ -245,13 +248,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 +366,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 +604,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,7 +623,7 @@ static client_queue_t *create_client_node(client_t *client) node->shoutcast_mount = strdup(listener->shoutcast_mount); } - config_release_config(); + listensocket_release_listener(client->con->listensocket_effective); return node; } @@ -776,7 +674,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 +1132,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 +1144,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; @@ -1278,6 +1176,9 @@ static int _handle_resources(client_t *client, char **uri) if (resource->bind_address != NULL && serverhost != NULL && strcmp(resource->bind_address, serverhost) != 0) continue; + if (resource->listen_socket != NULL && (listen_sock->id == NULL || strcmp(resource->listen_socket, listen_sock->id) != 0)) + continue; + /* Check for the vhost to match. */ if (resource->vhost != NULL && vhost != NULL && strcmp(resource->vhost, vhost) != 0) continue; @@ -1320,6 +1221,7 @@ static int _handle_resources(client_t *client, char **uri) break; } + listensocket_release_listener(client->con->listensocket_effective); config_release_config(); if (new_uri) { @@ -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,15 +1411,18 @@ static void __prepare_shoutcast_admin_cgi_request(client_t *client) return; } + /* Why do we acquire a global lock here? -- ph3-der-loewe, 2018-05-11 */ global_lock(); config = config_get_config(); sc_mount = config->shoutcast_mount; - listener = config_get_listen_sock(config, client->con); + listener = listensocket_get_listener(client->con->listensocket_effective); if (listener && listener->shoutcast_mount) sc_mount = listener->shoutcast_mount; httpp_set_query_param(client->parser, "mount", sc_mount); + listensocket_release_listener(client->con->listensocket_effective); + httpp_setvar(client->parser, HTTPP_VAR_PROTOCOL, "ICY"); client->password = strdup(pass); config_release_config(); @@ -1732,22 +1637,27 @@ static void _handle_connection(void) } -/* called when listening thread is not checking for incoming connections */ -int connection_setup_sockets (ice_config_t *config) +static void __on_sock_count(size_t count, void *userdata) { - int count = 0; - listener_t *listener, **prev; + (void)userdata; - global_lock(); - if (global.serversock) { - for (; count < global.server_sockets; count++) - sock_close (global.serversock [count]); - free (global.serversock); - global.serversock = NULL; + ICECAST_LOG_DEBUG("Listen socket count is now %zu.", count); + + if (count == 0 && global.running == ICECAST_RUNNING) { + ICECAST_LOG_INFO("No more listen sockets. Exiting."); + global.running = ICECAST_HALTING; } +} + +/* called when listening thread is not checking for incoming connections */ +void connection_setup_sockets (ice_config_t *config) +{ + global_lock(); + refobject_unref(global.listensockets); + if (config == NULL) { global_unlock(); - return 0; + return; } /* setup the banned/allowed IP filenames from the xml */ @@ -1763,57 +1673,13 @@ 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_set_sockcount_cb(global.listensockets, __on_sock_count, NULL); + listensocket_container_setup(global.listensockets);; } @@ -1829,5 +1695,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); } diff --git a/src/connection.h b/src/connection.h index 3782b4ae..34795857 100644 --- a/src/connection.h +++ b/src/connection.h @@ -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); diff --git a/src/fserve.c b/src/fserve.c index e7158449..f526c0db 100644 --- a/src/fserve.c +++ b/src/fserve.c @@ -452,16 +452,9 @@ int fserve_client_create (client_t *httpclient, const char *path) if (m3u_requested && m3u_file_available == 0) { - const char *host = httpp_getvar (httpclient->parser, "host"); char *sourceuri = strdup (path); char *dot = strrchr(sourceuri, '.'); - /* at least a couple of players (fb2k/winamp) are reported to send a - * host header but without the port number. So if we are missing the - * port then lets treat it as if no host line was sent */ - if (host && strchr (host, ':') == NULL) - host = NULL; - *dot = 0; httpclient->respcode = 200; ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, @@ -473,24 +466,7 @@ int fserve_client_create (client_t *httpclient, const char *path) free(sourceuri); return -1; } - if (host == NULL) - { - config = config_get_config(); - snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret, - "http://%s:%d%s\r\n", - config->hostname, config->port, - sourceuri - ); - config_release_config(); - } - else - { - snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret, - "http://%s%s\r\n", - host, - sourceuri - ); - } + client_get_baseurl(httpclient, NULL, httpclient->refbuf->data + ret, BUFSIZE - ret, NULL, NULL, NULL, sourceuri, "\r\n"); httpclient->refbuf->len = strlen (httpclient->refbuf->data); fserve_add_client (httpclient, NULL); free (sourceuri); @@ -504,7 +480,7 @@ int fserve_client_create (client_t *httpclient, const char *path) char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; - doc = stats_get_xml (0, reference, httpclient->mode); + doc = stats_get_xml (0, reference, httpclient); free (reference); admin_send_response (doc, httpclient, ADMIN_FORMAT_HTML, xslt_playlist_requested); xmlFreeDoc(doc); diff --git a/src/global.c b/src/global.c index 3b61081a..db126887 100644 --- a/src/global.c +++ b/src/global.c @@ -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; diff --git a/src/global.h b/src/global.h index bc02de8d..5329f85c 100644 --- a/src/global.h +++ b/src/global.h @@ -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; diff --git a/src/icecasttypes.h b/src/icecasttypes.h index 1792af0c..40f4d57f 100644 --- a/src/icecasttypes.h +++ b/src/icecasttypes.h @@ -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; diff --git a/src/listensocket.c b/src/listensocket.c new file mode 100644 index 00000000..6aa17607 --- /dev/null +++ b/src/listensocket.c @@ -0,0 +1,838 @@ +/* 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 , + */ + +/** + * Listen socket operations. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_POLL +#include +#else +#include +#endif + +#include + +#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; + mutex_t lock; + 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 listensocket_t * listensocket_container_get_by_id(listensocket_container_t *self, const char *id); +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); +#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 __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_mutex_lock(&container->lock); + __listensocket_container_clear_sockets(container); + thread_mutex_unlock(&container->lock); + thread_mutex_destroy(&container->lock); +} + +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; + self->sockcount_cb = NULL; + self->sockcount_userdata = NULL; + + thread_mutex_create(&self->lock); + + return self; +} + +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_mutex_lock(&self->lock); + ret = listensocket_container_configure__unlocked(self, config); + thread_mutex_unlock(&self->lock); + + 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; + + if (!self) + return -1; + + thread_mutex_lock(&self->lock); + 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_mutex_unlock(&self->lock); + + return ret; +} + +int listensocket_container_setup(listensocket_container_t *self) +{ + int ret; + + if (!self) + return -1; + + thread_mutex_lock(&self->lock); + ret = listensocket_container_setup__unlocked(self); + thread_mutex_unlock(&self->lock); + + 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]) == 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_mutex_lock(&self->lock); + ls = listensocket_container_accept__inner(self, timeout); + refobject_ref(ls); + thread_mutex_unlock(&self->lock); + + 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_mutex_lock(&self->lock); + self->sockcount_cb = cb; + self->sockcount_userdata = userdata; + thread_mutex_unlock(&self->lock); + + return 0; +} + +ssize_t listensocket_container_sockcount(listensocket_container_t *self) +{ + ssize_t ret; + + if (!self) + return -1; + + thread_mutex_lock(&self->lock); + ret = listensocket_container_sockcount__unlocked(self); + thread_mutex_unlock(&self->lock); + + 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; +} + +static listensocket_t * listensocket_container_get_by_id(listensocket_container_t *self, const char *id) +{ + size_t i; + const listener_t *listener; + + for (i = 0; i < self->sock_len; i++) { + if (self->sock[i] != NULL) { + listener = listensocket_get_listener(self->sock[i]); + if (listener) { + if (strcmp(listener->id, id) == 0) { + listensocket_release_listener(self->sock[i]); + if (refobject_ref(self->sock[i]) == 0) { + return self->sock[i]; + } + } + listensocket_release_listener(self->sock[i]); + } + } + } + + return NULL; +} + +/* ---------------------------------------------------------------------------- */ + +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); +} + +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; + + 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 missmatch: have %s:%i, got %s:%i", + __string_default(self->listener->bind_address, ""), + self->listener->port, + __string_default(self->listener_update->bind_address, ""), + 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); + } + + 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; +} + +int listensocket_refsock(listensocket_t *self) +{ + 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); + thread_rwlock_unlock(&self->listener_rwlock); + if (self->sock == SOCK_ERROR) { + thread_mutex_unlock(&self->lock); + return -1; + } + + if (sock_listen(self->sock, ICECAST_LISTEN_QUEUE) == 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, ""), 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; +} + +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; + } + + if (strncmp(ip, "::ffff:", 7) == 0) { + memmove(ip, ip+7, strlen(ip+7)+1); + } + + ICECAST_LOG_DEBUG("Client on socket \"%H\".", self->listener->id); + + 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; +} + +#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 diff --git a/src/listensocket.h b/src/listensocket.h new file mode 100644 index 00000000..4991892d --- /dev/null +++ b/src/listensocket.h @@ -0,0 +1,30 @@ +/* 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 , + */ + +#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_configure_and_setup(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_container_set_sockcount_cb(listensocket_container_t *self, void (*cb)(size_t count, void *userdata), void *userdata); +ssize_t listensocket_container_sockcount(listensocket_container_t *self); + +int listensocket_refsock(listensocket_t *self); +int listensocket_unrefsock(listensocket_t *self); +connection_t * listensocket_accept(listensocket_t *self, listensocket_container_t *container); +const listener_t * listensocket_get_listener(listensocket_t *self); +int listensocket_release_listener(listensocket_t *self); +listener_type_t listensocket_get_type(listensocket_t *self); + +#endif diff --git a/src/main.c b/src/main.c index 74277778..0f997b88 100644 --- a/src/main.c +++ b/src/main.c @@ -77,6 +77,7 @@ #include "yp.h" #include "auth.h" #include "event.h" +#include "listensocket.h" #include @@ -318,20 +319,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,11 +376,10 @@ static int _server_proc_init(void) { ice_config_t *config = config_get_config_unlocked(); - if (connection_setup_sockets (config) < 1) - return 0; + connection_setup_sockets(config); - if (!_start_listening()) { - _fatal_error("Failed trying to listen on server socket"); + if (listensocket_container_sockcount(global.listensockets) < 1) { + ICECAST_LOG_ERROR("Can not listen on any sockets."); return 0; } diff --git a/src/slave.c b/src/slave.c index e5ede252..d1353295 100644 --- a/src/slave.c +++ b/src/slave.c @@ -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 diff --git a/src/source.c b/src/source.c index 7a7e0a65..2887322c 100644 --- a/src/source.c +++ b/src/source.c @@ -616,19 +616,8 @@ static FILE * source_open_dumpfile(const char * filename) { */ static void source_init (source_t *source) { - ice_config_t *config = config_get_config(); - char *listenurl; + char listenurl[512]; const char *str; - int listen_url_size; - - /* 6 for max size of port */ - listen_url_size = strlen("http://") + strlen(config->hostname) + - strlen(":") + 6 + strlen(source->mount) + 1; - - listenurl = malloc (listen_url_size); - snprintf (listenurl, listen_url_size, "http://%s:%d%s", - config->hostname, config->port, source->mount); - config_release_config(); str = httpp_getvar(source->parser, "ice-audio-info"); source->audio_info = util_dict_new(); @@ -638,10 +627,9 @@ static void source_init (source_t *source) stats_event (source->mount, "audio_info", str); } + client_get_baseurl(NULL, NULL, listenurl, sizeof(listenurl), NULL, NULL, NULL, NULL, NULL); stats_event (source->mount, "listenurl", listenurl); - free(listenurl); - if (source->dumpfilename != NULL) { source->dumpfile = source_open_dumpfile (source->dumpfilename); diff --git a/src/stats.c b/src/stats.c index 07cf534c..0f9d9ce9 100644 --- a/src/stats.c +++ b/src/stats.c @@ -829,7 +829,7 @@ static inline void __add_authstack (auth_stack_t *stack, xmlNodePtr parent) { auth_stack_next(&stack); } } -static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden) { +static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden, client_t *client) { avl_node *avlnode; xmlNodePtr ret = NULL; ice_config_t *config; @@ -870,7 +870,13 @@ static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, i while (avlnode2) { stats_node_t *stat = avlnode2->key; - xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); + if (client && strcmp(stat->name, "listenurl") == 0) { + char buf[512]; + client_get_baseurl(client, NULL, buf, sizeof(buf), NULL, NULL, NULL, source->source, NULL); + xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(buf)); + } else { + xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); + } avlnode2 = avl_get_next (avlnode2); } @@ -1022,7 +1028,7 @@ void stats_transform_xslt(client_t *client, const char *uri) char *xslpath = util_get_path_from_normalised_uri(uri); const char *mount = httpp_get_param(client->parser, "mount"); - doc = stats_get_xml(0, mount, client->mode); + doc = stats_get_xml(0, mount, client); xslt_transform(doc, xslpath, client, 200); @@ -1053,7 +1059,7 @@ static void __add_metadata(xmlNodePtr node, const char *tag) { free(name); } -xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, operation_mode mode) +xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, client_t *client) { xmlDocPtr doc; xmlNodePtr node; @@ -1063,12 +1069,12 @@ xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, operation_mode node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL); xmlDocSetRootElement(doc, node); - node = _dump_stats_to_doc (node, show_mount, show_hidden); + node = _dump_stats_to_doc(node, show_mount, show_hidden, client); if (show_mount && node) { avl_tree_rlock(global.source_tree); source = source_find_mount_raw(show_mount); - admin_add_listeners_to_mount(source, node, mode); + admin_add_listeners_to_mount(source, node, client->mode); avl_tree_unlock(global.source_tree); } diff --git a/src/stats.h b/src/stats.h index 3ec72214..d004782f 100644 --- a/src/stats.h +++ b/src/stats.h @@ -18,7 +18,6 @@ #include #include "icecasttypes.h" -#include "cfgfile.h" #include "refbuf.h" typedef struct _stats_node_tag @@ -95,7 +94,7 @@ void stats_callback (client_t *client, void *notused); void stats_transform_xslt(client_t *client, const char *uri); void stats_sendxml(client_t *client); -xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, operation_mode mode); +xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, client_t *client); char *stats_get_value(const char *source, const char *name); #endif /* __STATS_H__ */ diff --git a/src/xslt.c b/src/xslt.c index 366ed4c2..821660ef 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -53,6 +53,7 @@ #include "stats.h" #include "fserve.h" #include "util.h" +#include "cfgfile.h" #define CATMODULE "xslt"