mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Merge branch 'matchfile'
This commit is contained in:
commit
85b709da04
@ -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 \
|
||||
|
146
src/connection.c
146
src/connection.c
@ -25,7 +25,6 @@
|
||||
#include <sys/poll.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
@ -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,8 +134,8 @@ 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);
|
||||
@ -304,91 +274,6 @@ static int connection_send(connection_t *con, const void *buf, size_t len)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
|
||||
{
|
||||
connection_t *con;
|
||||
@ -522,8 +407,8 @@ static connection_t *_accept_connection(int duration)
|
||||
if (strncmp(ip, "::ffff:", 7) == 0)
|
||||
memmove(ip, ip+7, strlen (ip+7)+1);
|
||||
|
||||
if (accept_ip_address(ip))
|
||||
con = connection_create(sock, serversock, ip);
|
||||
if (matchfile_match_allow_deny(allowed_ip, banned_ip, ip))
|
||||
con = connection_create (sock, serversock, ip);
|
||||
if (con)
|
||||
return con;
|
||||
sock_close(sock);
|
||||
@ -1522,11 +1407,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 +1420,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));
|
||||
|
186
src/matchfile.c
Normal file
186
src/matchfile.c
Normal file
@ -0,0 +1,186 @@
|
||||
/* 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 <errno.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, const 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;
|
||||
}
|
||||
|
||||
int matchfile_match_allow_deny(matchfile_t *allow, matchfile_t *deny, const char *key) {
|
||||
if (!allow && !deny)
|
||||
return 1;
|
||||
|
||||
if (!key)
|
||||
return 0;
|
||||
|
||||
if (matchfile_match(deny, key) > 0) {
|
||||
ICECAST_LOG_DEBUG("%s is banned", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (matchfile_match(allow, key) > 0) {
|
||||
ICECAST_LOG_DEBUG("%s is allowed", key);
|
||||
return 1;
|
||||
} else if (allow) {
|
||||
/* we are not on allow list but there is one, so reject */
|
||||
ICECAST_LOG_DEBUG("%s is not allowed", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* default: allow */
|
||||
return 1;
|
||||
}
|
23
src/matchfile.h
Normal file
23
src/matchfile.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* 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, const char *key);
|
||||
|
||||
/* returns 1 for allow or pass and 0 for deny */
|
||||
int matchfile_match_allow_deny(matchfile_t *allow, matchfile_t *deny, const char *key);
|
||||
|
||||
#endif /* __MATCHFILE_H__ */
|
Loading…
Reference in New Issue
Block a user