From 4b5f00993d8e8752a5ecdf7f4e2d1dd0dcc72acf Mon Sep 17 00:00:00 2001 From: Karl Heyes Date: Tue, 23 Oct 2007 22:25:31 +0000 Subject: [PATCH] Allow for files to be specified that will contain IPs that can be used to accept or deny client connections. svn path=/icecast/trunk/icecast/; revision=14039 --- src/auth_htpasswd.c | 15 ----- src/cfgfile.c | 8 +++ src/cfgfile.h | 2 + src/connection.c | 151 +++++++++++++++++++++++++++++++++++++++++++- src/util.c | 15 +++++ src/util.h | 3 + 6 files changed, 176 insertions(+), 18 deletions(-) diff --git a/src/auth_htpasswd.c b/src/auth_htpasswd.c index 5e04d6fc..e9aa2a57 100644 --- a/src/auth_htpasswd.c +++ b/src/auth_htpasswd.c @@ -66,19 +66,6 @@ static void htpasswd_clear(auth_t *self) { free(state); } -static int get_line(FILE *file, char *buf, int len) -{ - if(fgets(buf, len, file)) { - int len = strlen(buf); - if(len > 0 && buf[len-1] == '\n') { - buf[--len] = 0; - if(len > 0 && buf[len-1] == '\r') - buf[--len] = 0; - } - return 1; - } - return 0; -} /* md5 hash */ static char *get_hash(const char *data, int len) @@ -95,8 +82,6 @@ static char *get_hash(const char *data, int len) return util_bin_to_hex(digest, 16); } -#define MAX_LINE_LEN 512 - static int compare_users (void *arg, void *a, void *b) { diff --git a/src/cfgfile.c b/src/cfgfile.c index f35e1024..6244181c 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -192,6 +192,8 @@ void config_clear(ice_config_t *c) if (c->cert_file) xmlFree(c->cert_file); if (c->pidfile) xmlFree(c->pidfile); + if (c->banfile) xmlFree(c->banfile); + if (c->allowfile) xmlFree(c->allowfile); if (c->playlist_log) xmlFree(c->playlist_log); if (c->access_log) xmlFree(c->access_log); if (c->error_log) xmlFree(c->error_log); @@ -906,6 +908,12 @@ static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, } else if (xmlStrcmp (node->name, XMLSTR("pidfile")) == 0) { if (configuration->pidfile) xmlFree(configuration->pidfile); configuration->pidfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + } else if (xmlStrcmp (node->name, XMLSTR("deny-ip")) == 0) { + if (configuration->banfile) xmlFree(configuration->banfile); + configuration->banfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + } else if (xmlStrcmp (node->name, XMLSTR("allow-ip")) == 0) { + if (configuration->allowfile) xmlFree(configuration->allowfile); + configuration->allowfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); } else if (xmlStrcmp (node->name, XMLSTR("ssl-certificate")) == 0) { if (configuration->cert_file) xmlFree(configuration->cert_file); configuration->cert_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); diff --git a/src/cfgfile.h b/src/cfgfile.h index 3d0fc025..97685629 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -156,6 +156,8 @@ typedef struct ice_config_tag char *base_dir; char *log_dir; char *pidfile; + char *banfile; + char *allowfile; char *cert_file; char *webroot_dir; char *adminroot_dir; diff --git a/src/connection.c b/src/connection.c index 39852606..b1f89f44 100644 --- a/src/connection.c +++ b/src/connection.c @@ -22,6 +22,8 @@ #ifdef HAVE_POLL #include #endif +#include +#include #ifndef _WIN32 #include @@ -93,6 +95,14 @@ 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 mutex_t _connection_mutex; static volatile unsigned long _current_id = 0; static int _initialized = 0; @@ -108,10 +118,29 @@ static int ssl_ok; static SSL_CTX *ssl_ctx; #endif +/* filtering client connection based on IP */ +cache_file_contents banned_ip, allowed_ip; + rwlock_t _source_shutdown_rwlock; static void *_handle_connection(void *arg); +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) return; @@ -127,6 +156,12 @@ 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; } @@ -137,7 +172,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); + thread_cond_destroy(&global.shutdown_cond); thread_rwlock_destroy(&_source_shutdown_rwlock); thread_mutex_destroy(&_con_queue_mutex); @@ -275,6 +312,101 @@ 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) + { + WARN2 ("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) + { + WARN2("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); + INFO2 ("%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) + { + DEBUG1 ("%s is banned", ip); + return 0; + } + } + if (allowed_ip.contents) + { + if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0) + { + DEBUG1 ("%s is allowed", ip); + return 1; + } + else + { + DEBUG1 ("%s is not allowed", ip); + return 0; + } + } + return 1; +} + + connection_t *connection_create (sock_t sock, sock_t serversock, char *ip) { connection_t *con; @@ -392,7 +524,6 @@ static int wait_for_serversock(int timeout) static connection_t *_accept_connection(void) { int sock; - connection_t *con; char *ip; int serversock; @@ -406,11 +537,13 @@ static connection_t *_accept_connection(void) sock = sock_accept(serversock, ip, MAX_ADDR_LEN); if (sock >= 0) { + 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); - con = connection_create (sock, serversock, ip); + if (accept_ip_address (ip)) + con = connection_create (sock, serversock, ip); if (con) return con; sock_close (sock); @@ -1218,6 +1351,11 @@ 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) { @@ -1232,6 +1370,13 @@ int connection_setup_sockets (ice_config_t *config) return 0; } + /* setup the banned/allowed IP filenames from the xml */ + if (config->banfile) + banned_ip.filename = strdup (config->banfile); + + if (config->allowfile) + allowed_ip.filename = strdup (config->allowfile); + count = 0; global.serversock = calloc (config->listen_sock_count, sizeof (sock_t)); diff --git a/src/util.c b/src/util.c index dba88da8..9e92df96 100644 --- a/src/util.c +++ b/src/util.c @@ -673,3 +673,18 @@ char *util_conv_string (const char *string, const char *in_charset, const char * return ret; } + +int get_line(FILE *file, char *buf, size_t siz) +{ + if(fgets(buf, (int)siz, file)) { + size_t len = strlen(buf); + if(len > 0 && buf[len-1] == '\n') { + buf[--len] = 0; + if(len > 0 && buf[len-1] == '\r') + buf[--len] = 0; + } + return 1; + } + return 0; +} + diff --git a/src/util.h b/src/util.h index 855f7264..71241e40 100644 --- a/src/util.h +++ b/src/util.h @@ -19,6 +19,8 @@ #define READ_ENTIRE_HEADER 1 #define READ_LINE 0 +#define MAX_LINE_LEN 512 + int util_timed_wait_for_fd(int fd, int timeout); int util_read_header(int sock, char *buff, unsigned long len, int entire); int util_check_valid_extension(const char *uri); @@ -53,4 +55,5 @@ struct tm *localtime_r (const time_t *timep, struct tm *result); #endif char *util_conv_string (const char *string, const char *in_charset, const char *out_charset); +int get_line(FILE *file, char *buf, size_t siz); #endif /* __UTIL_H__ */