mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Cleanup: moved ban and allow file support into generic implementation
ban and allow file support has been moved into a generic implementation that can be re-used by later code such as proxy matching or other blacklisting. See: #1959
This commit is contained in:
parent
3653a1f812
commit
d24dda61d0
@ -10,7 +10,7 @@ INCLUDES = -I./common/
|
|||||||
|
|
||||||
noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
|
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 \
|
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 \
|
event.h event_log.h event_exec.h event_url.h \
|
||||||
acl.h auth.h \
|
acl.h auth.h \
|
||||||
format.h format_ogg.h format_mp3.h format_ebml.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
|
format_kate.h format_skeleton.h format_opus.h
|
||||||
icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
|
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 \
|
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.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 \
|
format_kate.c format_skeleton.c format_opus.c \
|
||||||
event.c event_log.c event_exec.c \
|
event.c event_log.c event_exec.c \
|
||||||
|
148
src/connection.c
148
src/connection.c
@ -25,7 +25,6 @@
|
|||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@ -59,6 +58,7 @@
|
|||||||
#include "format_mp3.h"
|
#include "format_mp3.h"
|
||||||
#include "admin.h"
|
#include "admin.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
#include "matchfile.h"
|
||||||
|
|
||||||
#define CATMODULE "connection"
|
#define CATMODULE "connection"
|
||||||
|
|
||||||
@ -91,14 +91,6 @@ typedef struct _thread_queue_tag {
|
|||||||
struct _thread_queue_tag *next;
|
struct _thread_queue_tag *next;
|
||||||
} thread_queue_t;
|
} 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 spin_t _connection_lock; // protects _current_id, _con_queue, _con_queue_tail
|
||||||
static volatile unsigned long _current_id = 0;
|
static volatile unsigned long _current_id = 0;
|
||||||
static int _initialized = 0;
|
static int _initialized = 0;
|
||||||
@ -111,28 +103,12 @@ static SSL_CTX *ssl_ctx;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* filtering client connection based on IP */
|
/* 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;
|
rwlock_t _source_shutdown_rwlock;
|
||||||
|
|
||||||
static void _handle_connection(void);
|
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)
|
void connection_initialize(void)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
@ -147,12 +123,6 @@ void connection_initialize(void)
|
|||||||
_con_queue = NULL;
|
_con_queue = NULL;
|
||||||
_con_queue_tail = &_con_queue;
|
_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;
|
_initialized = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +134,8 @@ void connection_shutdown(void)
|
|||||||
#ifdef HAVE_OPENSSL
|
#ifdef HAVE_OPENSSL
|
||||||
SSL_CTX_free (ssl_ctx);
|
SSL_CTX_free (ssl_ctx);
|
||||||
#endif
|
#endif
|
||||||
if (banned_ip.contents) avl_tree_free (banned_ip.contents, free_filtered_ip);
|
matchfile_release(banned_ip);
|
||||||
if (allowed_ip.contents) avl_tree_free (allowed_ip.contents, free_filtered_ip);
|
matchfile_release(allowed_ip);
|
||||||
|
|
||||||
thread_cond_destroy(&global.shutdown_cond);
|
thread_cond_destroy(&global.shutdown_cond);
|
||||||
thread_rwlock_destroy(&_source_shutdown_rwlock);
|
thread_rwlock_destroy(&_source_shutdown_rwlock);
|
||||||
@ -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 */
|
/* return 0 if the passed ip address is not to be handled by icecast, non-zero otherwise */
|
||||||
static int accept_ip_address(char *ip)
|
static int accept_ip_address(char *ip) {
|
||||||
{
|
if (matchfile_match(banned_ip, ip) > 0) {
|
||||||
void *result;
|
ICECAST_LOG_DEBUG("%s is banned", ip);
|
||||||
|
return 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (allowed_ip.contents) {
|
|
||||||
if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0) {
|
if (matchfile_match(allowed_ip, ip) > 0) {
|
||||||
ICECAST_LOG_DEBUG("%s is allowed", ip);
|
ICECAST_LOG_DEBUG("%s is allowed", ip);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else if (allowed_ip) {
|
||||||
ICECAST_LOG_DEBUG("%s is not allowed", ip);
|
/* we are not on allow list but there is one, so reject */
|
||||||
return 0;
|
ICECAST_LOG_DEBUG("%s is not allowed", ip);
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* default: allow */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1522,11 +1429,6 @@ int connection_setup_sockets (ice_config_t *config)
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
listener_t *listener, **prev;
|
listener_t *listener, **prev;
|
||||||
|
|
||||||
free (banned_ip.filename);
|
|
||||||
banned_ip.filename = NULL;
|
|
||||||
free (allowed_ip.filename);
|
|
||||||
allowed_ip.filename = NULL;
|
|
||||||
|
|
||||||
global_lock();
|
global_lock();
|
||||||
if (global.serversock) {
|
if (global.serversock) {
|
||||||
for (; count < global.server_sockets; count++)
|
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 */
|
/* setup the banned/allowed IP filenames from the xml */
|
||||||
if (config->banfile)
|
if (config->banfile) {
|
||||||
banned_ip.filename = strdup(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)
|
if (config->allowfile) {
|
||||||
allowed_ip.filename = strdup(config->allowfile);
|
matchfile_release(allowed_ip);
|
||||||
|
allowed_ip = matchfile_new(config->allowfile);
|
||||||
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
global.serversock = calloc(config->listen_sock_count, sizeof(sock_t));
|
global.serversock = calloc(config->listen_sock_count, sizeof(sock_t));
|
||||||
|
160
src/matchfile.c
Normal file
160
src/matchfile.c
Normal file
@ -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 <lion@lion.leolix.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
20
src/matchfile.h
Normal file
20
src/matchfile.h
Normal file
@ -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 <lion@lion.leolix.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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__ */
|
Loading…
Reference in New Issue
Block a user