diff --git a/src/Makefile.am b/src/Makefile.am index dc36e8e3..e0974e03 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ INCLUDES = -I./common/ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \ global.h util.h curl.h slave.h source.h stats.h refbuf.h client.h playlist.h \ - compat.h fserve.h xslt.h yp.h md5.h \ + compat.h fserve.h xslt.h yp.h md5.h matchfile.h \ event.h event_log.h event_exec.h event_url.h \ acl.h auth.h \ format.h format_ogg.h format_mp3.h format_ebml.h \ @@ -18,7 +18,7 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \ format_kate.h format_skeleton.h format_opus.h icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \ util.c curl.c slave.c source.c stats.c refbuf.c client.c playlist.c \ - xslt.c fserve.c admin.c md5.c \ + xslt.c fserve.c admin.c md5.c matchfile.c \ format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \ format_kate.c format_skeleton.c format_opus.c \ event.c event_log.c event_exec.c \ diff --git a/src/connection.c b/src/connection.c index d6d467c1..f84eed02 100644 --- a/src/connection.c +++ b/src/connection.c @@ -25,7 +25,6 @@ #include #endif #include -#include #ifndef _WIN32 #include @@ -59,6 +58,7 @@ #include "format_mp3.h" #include "admin.h" #include "auth.h" +#include "matchfile.h" #define CATMODULE "connection" @@ -91,14 +91,6 @@ typedef struct _thread_queue_tag { struct _thread_queue_tag *next; } thread_queue_t; -typedef struct -{ - char *filename; - time_t file_recheck; - time_t file_mtime; - avl_tree *contents; -} cache_file_contents; - static spin_t _connection_lock; // protects _current_id, _con_queue, _con_queue_tail static volatile unsigned long _current_id = 0; static int _initialized = 0; @@ -111,28 +103,12 @@ static SSL_CTX *ssl_ctx; #endif /* filtering client connection based on IP */ -static cache_file_contents banned_ip, allowed_ip; +static matchfile_t *banned_ip, *allowed_ip; rwlock_t _source_shutdown_rwlock; static void _handle_connection(void); -static int compare_ip (void *arg, void *a, void *b) -{ - const char *ip = (const char *)a; - const char *pattern = (const char *)b; - - return strcmp (pattern, ip); -} - - -static int free_filtered_ip (void*x) -{ - free (x); - return 1; -} - - void connection_initialize(void) { if (_initialized) @@ -147,12 +123,6 @@ void connection_initialize(void) _con_queue = NULL; _con_queue_tail = &_con_queue; - banned_ip.contents = NULL; - banned_ip.file_mtime = 0; - - allowed_ip.contents = NULL; - allowed_ip.file_mtime = 0; - _initialized = 1; } @@ -164,9 +134,9 @@ void connection_shutdown(void) #ifdef HAVE_OPENSSL SSL_CTX_free (ssl_ctx); #endif - if (banned_ip.contents) avl_tree_free (banned_ip.contents, free_filtered_ip); - if (allowed_ip.contents) avl_tree_free (allowed_ip.contents, free_filtered_ip); - + matchfile_release(banned_ip); + matchfile_release(allowed_ip); + thread_cond_destroy(&global.shutdown_cond); thread_rwlock_destroy(&_source_shutdown_rwlock); thread_spin_destroy (&_connection_lock); @@ -305,86 +275,23 @@ static int connection_send(connection_t *con, const void *buf, size_t len) } -/* function to handle the re-populating of the avl tree containing IP addresses - * for deciding whether a connection of an incoming request is to be dropped. - */ -static void recheck_ip_file(cache_file_contents *cache) -{ - time_t now = time(NULL); - if (now >= cache->file_recheck) { - struct stat file_stat; - FILE *file = NULL; - int count = 0; - avl_tree *new_ips; - char line[MAX_LINE_LEN]; - - cache->file_recheck = now + 10; - if (cache->filename == NULL) { - if (cache->contents) { - avl_tree_free (cache->contents, free_filtered_ip); - cache->contents = NULL; - } - return; - } - if (stat(cache->filename, &file_stat) < 0) { - ICECAST_LOG_WARN("Failed to check status of \"%s\": %s", cache->filename, strerror(errno)); - return; - } - if (file_stat.st_mtime == cache->file_mtime) - return; /* common case, no update to file */ - - cache->file_mtime = file_stat.st_mtime; - - file = fopen (cache->filename, "r"); - if (file == NULL) { - ICECAST_LOG_WARN("Failed to open file \"%s\": %s", cache->filename, strerror (errno)); - return; - } - - new_ips = avl_tree_new(compare_ip, NULL); - - while (get_line(file, line, MAX_LINE_LEN)) { - char *str; - if(!line[0] || line[0] == '#') - continue; - count++; - str = strdup (line); - if (str) - avl_insert(new_ips, str); - } - fclose (file); - ICECAST_LOG_INFO("%d entries read from file \"%s\"", count, cache->filename); - - if (cache->contents) - avl_tree_free(cache->contents, free_filtered_ip); - cache->contents = new_ips; - } -} - - /* return 0 if the passed ip address is not to be handled by icecast, non-zero otherwise */ -static int accept_ip_address(char *ip) -{ - void *result; - - recheck_ip_file(&banned_ip); - recheck_ip_file(&allowed_ip); - - if (banned_ip.contents) { - if (avl_get_by_key (banned_ip.contents, ip, &result) == 0) { - ICECAST_LOG_DEBUG("%s is banned", ip); - return 0; - } +static int accept_ip_address(char *ip) { + if (matchfile_match(banned_ip, ip) > 0) { + ICECAST_LOG_DEBUG("%s is banned", ip); + return 0; } - if (allowed_ip.contents) { - if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0) { - ICECAST_LOG_DEBUG("%s is allowed", ip); - return 1; - } else { - ICECAST_LOG_DEBUG("%s is not allowed", ip); - return 0; - } + + if (matchfile_match(allowed_ip, ip) > 0) { + ICECAST_LOG_DEBUG("%s is allowed", ip); + return 1; + } else if (allowed_ip) { + /* we are not on allow list but there is one, so reject */ + ICECAST_LOG_DEBUG("%s is not allowed", ip); + return 0; } + + /* default: allow */ return 1; } @@ -1522,11 +1429,6 @@ int connection_setup_sockets (ice_config_t *config) int count = 0; listener_t *listener, **prev; - free (banned_ip.filename); - banned_ip.filename = NULL; - free (allowed_ip.filename); - allowed_ip.filename = NULL; - global_lock(); if (global.serversock) { for (; count < global.server_sockets; count++) @@ -1540,11 +1442,17 @@ int connection_setup_sockets (ice_config_t *config) } /* setup the banned/allowed IP filenames from the xml */ - if (config->banfile) - banned_ip.filename = strdup(config->banfile); + if (config->banfile) { + matchfile_release(banned_ip); + banned_ip = matchfile_new(config->banfile); + if (!banned_ip) + ICECAST_LOG_ERROR("Can not create ban object, bad!"); + } - if (config->allowfile) - allowed_ip.filename = strdup(config->allowfile); + if (config->allowfile) { + matchfile_release(allowed_ip); + allowed_ip = matchfile_new(config->allowfile); + } count = 0; global.serversock = calloc(config->listen_sock_count, sizeof(sock_t)); diff --git a/src/matchfile.c b/src/matchfile.c new file mode 100644 index 00000000..d30cd921 --- /dev/null +++ b/src/matchfile.c @@ -0,0 +1,160 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2015, Philipp "ph3-der-loewe" Schafft + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "matchfile.h" +#include "logging.h" +#include "util.h" /* for MAX_LINE_LEN and get_line() */ +#include "common/avl/avl.h" +#define CATMODULE "matchfile" + +struct matchfile_tag { + /* reference counter */ + size_t refcount; + + /* filename of input file */ + char *filename; + + time_t file_recheck; + time_t file_mtime; + avl_tree *contents; +}; + +static int __func_free(void *x) { + free(x); + return 1; +} + +static int __func_compare (void *arg, void *a, void *b) { + return strcmp(b, a); +} + +static void __func_recheck(matchfile_t *file) { + time_t now = time(NULL); + struct stat file_stat; + FILE *input = NULL; + avl_tree *new_contents; + char line[MAX_LINE_LEN]; + + if (now < file->file_recheck) + return; + + file->file_recheck = now + 10; + + if (stat(file->filename, &file_stat) < 0) { + ICECAST_LOG_WARN("failed to check status of \"%s\": %s", file->filename, strerror(errno)); + return; + } + + if (file_stat.st_mtime == file->file_mtime) + return; /* common case, no update to file */ + + file->file_mtime = file_stat.st_mtime; + + input = fopen(file->filename, "r"); + if (!input) { + ICECAST_LOG_WARN("Failed to open file \"%s\": %s", file->filename, strerror(errno)); + return; + } + + new_contents = avl_tree_new(__func_compare, NULL); + + while (get_line(input, line, MAX_LINE_LEN)) { + char *str; + + if(!line[0] || line[0] == '#') + continue; + str = strdup(line); + if (str) + avl_insert(new_contents, str); + } + + fclose(input); + + if (file->contents) avl_tree_free(file->contents, __func_free); + file->contents = new_contents; +} + +matchfile_t *matchfile_new(const char *filename) { + matchfile_t *ret; + + if (!filename) + return NULL; + + ret = calloc(1, sizeof(matchfile_t)); + if (!ret) + return NULL; + + ret->refcount = 1; + ret->filename = strdup(filename); + ret->file_mtime = 0; + ret->file_recheck = 0; + + if (!ret->filename) { + matchfile_release(ret); + return NULL; + } + + /* load initial database */ + __func_recheck(ret); + + return ret; +} + +int matchfile_addref(matchfile_t *file) { + if (!file) + return -1; + + file->refcount++; + + return 0; +} + +int matchfile_release(matchfile_t *file) { + if (!file) + return -1; + + file->refcount--; + + if (file->refcount) + return 0; + + if (file->contents) + avl_tree_free(file->contents, __func_free); + free(file->filename); + free(file); + + return 0; +} + +/* we are not const char *key because of avl_get_by_key()... */ +int matchfile_match(matchfile_t *file, char *key) { + void *result; + + if (!file) + return -1; + + /* reload database if needed */ + __func_recheck(file); + + if (!file->contents) + return 0; + + return avl_get_by_key(file->contents, (void*)key, &result) == 0 ? 1 : 0; +} diff --git a/src/matchfile.h b/src/matchfile.h new file mode 100644 index 00000000..f926ea7b --- /dev/null +++ b/src/matchfile.h @@ -0,0 +1,20 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2015, Philipp "ph3-der-loewe" Schafft + */ + +#ifndef __MATCHFILE_H__ +#define __MATCHFILE_H__ + +struct matchfile_tag; +typedef struct matchfile_tag matchfile_t; + +matchfile_t *matchfile_new(const char *filename); +int matchfile_addref(matchfile_t *file); +int matchfile_release(matchfile_t *file); +int matchfile_match(matchfile_t *file, char *key); + +#endif /* __MATCHFILE_H__ */