Based on 'rtorrent-0.8.5-ip_filter_no_boost-fast.patch' by Richard Monk and 'yb' Adapted for rtorrent-0.8.6 by Denis Fateyev Adapted for rtorrent-0.8.7 by James Cuzella Adapted for rtorrent-0.8.9 by Denis Fateyev Rev.2 (BSD/MacOSX support provided) See http://libtorrent.rakshasa.no/ticket/239 for more details. --- a/configure.ac +++ b/configure.ac @@ -4,6 +4,7 @@ AM_CONFIG_HEADER(config.h) AM_PATH_CPPUNIT(1.9.6) +AC_CHECK_FUNCS(getline) AC_PROG_CXX AC_PROG_LIBTOOL --- a/src/command_network.cc +++ b/src/command_network.cc @@ -36,6 +36,11 @@ #include "config.h" +#include +#include +#include +#include + #include #include #include @@ -65,6 +70,10 @@ #include "control.h" #include "command_helpers.h" +#include "utils/pattern.h" +#include "core/ip_filter.h" + + torrent::Object apply_throttle(const torrent::Object::list_type& args, bool up) { torrent::Object::list_const_iterator argItr = args.begin(); @@ -206,6 +215,58 @@ return torrent::Object(); } + torrent::Object +apply_ip_filter(const torrent::Object::list_type& args) { + + std::list files; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + std::string file( itr->as_string() ); + utils::trim( file ); + if( access(file.c_str(),F_OK | R_OK) ) + throw torrent::input_error("IpFilter file '" + file + "' does not exist or not readable. Filter could not be loaded"); + files.push_back( file ); + } + + std::stringstream logMsg; + if( files.empty() ) { + logMsg << "IpFilter is empty"; + control->core()->push_log( logMsg.str().c_str() ); + } + else { + core::IpFilter* f = new core::IpFilter(); + logMsg << "IpFilter is initialized with files: "; + int entries = 0; + clock_t time_start = clock(); + for( std::list::iterator itr = files.begin(); itr != files.end(); itr++) { + std::cout << "Loading IP filters from '" << *itr << "'..."; + std::cout.flush(); + if( itr != files.begin() ) + logMsg << ", "; + logMsg << *itr; + int merges = f->add_from_file( *itr ); + if( merges < 0 ) { + std::cout << "error" << std::endl; + std::cout.flush(); + throw torrent::input_error("IpFilter could not load file '" + *itr + "'"); + } + std::cout << "done. Loaded " << (f->size()-entries) << " ranges. " << merges << " ranges were merged." << std::endl; + std::cout.flush(); + entries = f->size(); + } + control->core()->push_log( logMsg.str().c_str() ); + std::stringstream logMsg2("IpFilter loaded with "); + logMsg2 << f->size() << " ranges total. " << f->get_merges() << " ranges were merged."; + control->core()->push_log( logMsg2.str().c_str() ); + std::cout << logMsg2.str() << std::endl; + std::cout << "IP_Filters loaded in " << (double)(clock()-time_start)/CLOCKS_PER_SEC << " seconds" << std::endl; + std::cout.flush(); + control->core()->set_ip_filter( f ); + } + + return torrent::Object(); +} + torrent::Object apply_tos(const torrent::Object::string_type& arg) { rpc::command_base::value_type value; @@ -615,6 +676,10 @@ torrent::FileManager* fileManager = torrent::file_manager(); core::CurlStack* httpStack = control->core()->http_stack(); + CMD2_ANY_V ("reload_ip_filter", std::tr1::bind(&core::Manager::reload_ip_filter, control->core())); + CMD2_ANY_LIST ("ip_filter", std::tr1::bind(&apply_ip_filter, std::tr1::placeholders::_2)); + + CMD2_VAR_BOOL ("log.handshake", false); CMD2_VAR_STRING ("log.tracker", ""); --- a/src/core/getline.cc +++ b/src/core/getline.cc @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace core { + +/* getline.c --- Implementation of replacement getline function. + Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005 Free + Software Foundation, Inc. + +/* Ported from glibc by Simon Josefsson. */ + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif +#ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) +#endif +#if !HAVE_FLOCKFILE +# undef flockfile +# define flockfile(x) ((void) 0) +#endif +#if !HAVE_FUNLOCKFILE +# undef funlockfile +# define funlockfile(x) ((void) 0) +#endif + +/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and + NUL-terminate it). *LINEPTR is a pointer returned from malloc (or + NULL), pointing to *N characters of space. It is realloc'ed as + necessary. Returns the number of characters read (not including + the null terminator), or -1 on error or EOF. */ + +ssize_t getline (char **lineptr, size_t *n, FILE *fp) +{ + ssize_t result; + size_t cur_len = 0; + + if (lineptr == NULL || n == NULL || fp == NULL) + { + errno = EINVAL; + return -1; + } + + flockfile (fp); + + if (*lineptr == NULL || *n == 0) + { + *n = 120; + *lineptr = (char *) malloc (*n); + if (*lineptr == NULL) +{ + result = -1; + goto unlock_return; +} + } + + for (;;) + { + int i; + + i = getc (fp); + if (i == EOF) +{ + result = -1; + break; +} + + /* Make enough space for len+1 (for final NUL) bytes. */ + if (cur_len + 1 >= *n) +{ + size_t needed_max = + SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; + size_t needed = 2 * *n + 1; /* Be generous. */ + char *new_lineptr; + + if (needed_max < needed) + needed = needed_max; + if (cur_len + 1 >= needed) + { + result = -1; + goto unlock_return; + } + + new_lineptr = (char *) realloc (*lineptr, needed); + if (new_lineptr == NULL) + { + result = -1; + + + + goto unlock_return; + } + + *lineptr = new_lineptr; + *n = needed; +} + + (*lineptr)[cur_len] = i; + cur_len++; + + if (i == '\n') +break; + } + (*lineptr)[cur_len] = '\0'; + result = cur_len ? cur_len : result; + + unlock_return: + funlockfile (fp); + return result; +} + +} --- a/src/core/getline.h +++ b/src/core/getline.h @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace core { + +/* getline.c --- Implementation of replacement getline function. + Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005 Free + Software Foundation, Inc. + +/* Ported from glibc by Simon Josefsson. */ + +/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and + NUL-terminate it). *LINEPTR is a pointer returned from malloc (or + NULL), pointing to *N characters of space. It is realloc'ed as + necessary. Returns the number of characters read (not including + the null terminator), or -1 on error or EOF. */ + +ssize_t getline (char **lineptr, size_t *n, FILE *fp); + +} --- a/src/core/ip_address.cc +++ b/src/core/ip_address.cc @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "ip_address.h" +#include "utils/pattern.h" + +namespace core { + +std::pair IpAddress::to_int( const std::string& address ) { + uint32_t a; + int r = inet_pton( AF_INET, address.c_str(), &a); + if( r ) + a = ntohl( a ); + return std::pair( (r!=0), a ); +} + +std::string IpAddress::to_string() const { + char buf[128] = ""; + uint32_t a = htonl( m_address ); + inet_ntop( AF_INET, &a, buf, sizeof(buf) ); + return std::string( buf ); +} + +} --- a/src/core/ip_address.h +++ b/src/core/ip_address.h @@ -0,0 +1,65 @@ +#ifndef IPADDRESS_H +#define IPADDRESS_H + +#include +#include + +#include "printable.h" +#include "utils/pattern.h" +#include "regex_namespace.h" + +namespace core { + +class IpAddress : public Printable { + friend class IpRange; + + private: // constants + static const std::string PATTERN_IP_EXPRESSION; + static const std::string PATTERN_IP_BYTES_EXPRESSION; + static const regex::Pattern PATTERN_IP_BYTES; + + static const int GRP_IP_FIRST_BYTE; + static const int GRP_IP_BYTES_COUNT; + + private: // fields + uint32_t m_address; + + private: // static methods + + private: // dynamic methods + IpAddress() : m_address(0) {} + + void copy( const IpAddress& addr ) { m_address = addr.m_address;} + + public: // static methods + static std::pair to_int( const std::string& strAddress ); + static IpAddress* parse( const std::string& strAddress ) { + std::pair result = to_int( strAddress ); + return ( !result.first ) ? NULL : new IpAddress( result.second ); + } + + public: // dynamic methods + IpAddress( uint32_t address ) : m_address(address) {} + IpAddress( const IpAddress& addr ) { copy( addr ); } + IpAddress& operator= ( const IpAddress& addr ) { copy( addr ); return *this; } + + operator uint32_t() const { return m_address; } + + bool operator>= ( const IpAddress& ip ) const { return (m_address >= ip.m_address); } + bool operator<= ( const IpAddress& ip ) const { return (m_address <= ip.m_address); } + bool operator< ( const IpAddress& ip ) const { return (m_address < ip.m_address); } + bool operator> ( const IpAddress& ip ) const { return (m_address > ip.m_address); } + bool operator== ( const IpAddress& ip ) const { return (m_address == ip.m_address); } + bool operator!= ( const IpAddress& ip ) const { return (m_address != ip.m_address); } + + bool operator>= ( uint32_t ip ) const { return (m_address >= ip); } + bool operator<= ( uint32_t ip ) const { return (m_address <= ip); } + bool operator< ( uint32_t ip ) const { return (m_address < ip); } + bool operator> ( uint32_t ip ) const { return (m_address > ip); } + bool operator== ( uint32_t ip ) const { return (m_address == ip); } + bool operator!= ( uint32_t ip ) const { return (m_address != ip); } + + std::string to_string() const; + }; +} +#endif --- a/src/core/ip_filter.cc +++ b/src/core/ip_filter.cc @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef __linux__ +#ifndef HAVE_GETLINE +#include "getline.h" +#endif +#endif + +#include "ip_filter.h" + +namespace core { + +int IpFilter::merge_and_insert( range_map* rs, IpRange* r ) { + if( !r || !r->get_from() ) + return 0; + + std::pair p( *r->get_from(), IpRange::ptr(r) ); + std::pair duo = rs->insert( p ); + + range_itr idx = duo.first; + bool wasInserted = duo.second; + IpRange* curr = NULL; + int mergeCount = 0; + + if( !wasInserted ) { // exactly the same start address already exists + curr = idx->second; + if( *curr->get_to() < *r->get_to() ) + curr->set_to( r->get_to() ); + delete r; + r = curr; + mergeCount++; + } + else { + if( idx != rs->begin() ) { + --idx; + curr = idx->second; // previous + if( *r->get_from() <= *curr->get_to() ) + r = curr; + else + ++idx; + } + } + + if( idx != rs->end() ) + ++idx; + + while( idx != rs->end() ) { + curr = idx->second; + if( *r->get_to() < *curr->get_from() ) + break; + + std::string d = r->get_description(); + d += " / " + curr->get_description(); + r->set_description( d ); + if( *r->get_to() < *curr->get_to() ) + r->set_to( curr->get_to() ); + rs->erase( idx++ ); + delete curr; + mergeCount++; + } + return mergeCount; +} + +int IpFilter::add_from_file( const std::string& fileName, range_map* rs, str_list* files ) { + FILE *f = fopen(fileName.c_str(),"r"); + int mergeCount = 0; + if (f==0) return -1; + char *line = (char *)malloc(64); + size_t sz=64; + int charsread = 0; + int linesread=0; + while( (charsread=getline(&line,&sz,f)) >=0 ) { + if( (line[0] == '#' ) || ( charsread <= 1 ) ) + continue; + + IpRange* ir = IpRange::parse( line, charsread ); + if( !ir || !ir->get_from() || !ir->get_to() ) + continue; + + mergeCount += merge_and_insert( rs, ir ); + } + free(line); + files->push_back( std::string(fileName) ); + fclose(f); + m_merges += mergeCount; + return mergeCount; +} + +int IpFilter::add_from_file( const std::string& fileName ) { + if( !m_ranges ) + m_ranges = new range_map(); + if( !m_loadedFiles ) + m_loadedFiles = new std::list(); + + return add_from_file( fileName, m_ranges, m_loadedFiles ); +} + +int IpFilter::reload() { + if( !m_loadedFiles || m_loadedFiles->empty() ) + return 0; + + range_map* rs = new range_map(); + str_list* files = new str_list(); + int mergeCount = 0; + for( str_list::const_iterator it = m_loadedFiles->begin(), end = m_loadedFiles->end(); it != end; it++ ) + mergeCount += add_from_file( *it, rs, files ); + + range_map* rsOld = m_ranges; + m_ranges = rs; + if( rsOld ) { + clear( rsOld ); + delete rsOld; + } + + str_list* filesOld = m_loadedFiles; + m_loadedFiles = files; + if( filesOld ) { + clear( filesOld ); + delete filesOld; + } + + m_merges = mergeCount; + return mergeCount; +} + +IpRange* IpFilter::find_range( uint32_t ip ) const { + if( (ip >= 0) && m_ranges && !m_ranges->empty() ) { + range_itr idx = m_ranges->upper_bound( ip ); + if( idx != m_ranges->begin() ) + --idx; + IpRange* curr = idx->second; + if( curr->includes( ip ) ) + return curr; + } + return NULL; +} + +std::string IpFilter::to_string() const { + std::stringstream result; + if( !m_ranges ) + result << "NULL" << std::endl; + else { + for( range_map::const_iterator it = m_ranges->begin() ; it != m_ranges->end(); it++ ) { + const IpAddress a = it->first; + IpRange* ir = it->second; + result << a << ": " << *ir << std::endl; + } + } + return result.str(); +} + +void IpFilter::clear( range_map* map ) { + if( map ) { + for( range_itr i = map->begin(), j = map->end(); i != j; i++ ) + delete i->second; + map->clear(); + } +} + +void IpFilter::clear( str_list* list ) { + if( list ) + list->clear(); +} + +} --- a/src/core/ip_filter.h +++ b/src/core/ip_filter.h @@ -0,0 +1,85 @@ +#ifndef IPFILTER_H +#define IPFILTER_H + +#include +#include +#include + +#include "printable.h" +#include "ip_address.h" +#include "ip_range.h" + +namespace core { + +typedef std::map range_map; +typedef range_map::iterator range_itr; +typedef std::list str_list; + +class IpFilter : public Printable { + private: // fields + int m_merges; + range_map* m_ranges; + str_list* m_loadedFiles; + + private: // static methods + static void clear( range_map* map ); + static void clear( str_list* list ); + + private: // dynamic methods + void init_members(void) { // to avoid long constructor lines for every ctor + m_ranges = NULL; + m_loadedFiles = NULL; + m_merges = 0; + } + int merge_and_insert( range_map* rs, IpRange* r ); + int add_from_file( const std::string& fileName, range_map* rs, str_list* files ); + + public: // static methods + + public: // dynamic methods + IpFilter() { init_members(); } + ~IpFilter() { + clear(); + if( m_ranges ) delete m_ranges; + if( m_loadedFiles ) delete m_loadedFiles; + m_ranges = NULL; + m_loadedFiles = NULL; + } + IpFilter( std::string* files, int size ) { + init_members(); + for( int i = 0; i < size; i++, files++ ) + add_from_file( *files ); + } + IpFilter( str_list& files ) { + init_members(); + for( str_list::const_iterator i = files.begin(), last = files.end(); i != last; i++ ) + add_from_file( *i ); + } + IpFilter( IpFilter& f ) { + init_members(); + m_ranges = new range_map( *f.m_ranges ); + m_loadedFiles = new str_list( *f.m_loadedFiles ); + } + + int reload(); + int add_from_file( const std::string& fileName ); + int add_from_file( char* fileName ) { std::string s( fileName ); return add_from_file(s); } + void clear() { clear( m_ranges ); clear( m_loadedFiles ); } + + IpRange* find_range( uint32_t ip ) const; + + bool is_filtered( uint32_t ip ) const { return (find_range( ip ) != NULL); } + bool is_filtered( std::string ip ) const { + static std::pair ipInt = IpAddress::to_int( ip ); + return (!ipInt.first ? false : is_filtered( ipInt.second )); + } + + std::string to_string() const; + + int size(void) { return ( m_ranges ? m_ranges->size() : 0 ); } + int get_merges(void) { return m_merges; } + void set_files( str_list& files) { m_loadedFiles = new str_list( files ); } +}; + +} +#endif --- a/src/core/ip_filter_statics.cc +++ b/src/core/ip_filter_statics.cc @@ -0,0 +1,21 @@ +#include "ip_address.h" +#include "ip_range.h" +#include "utils/pattern.h" + +namespace core { + +const std::string IpAddress::PATTERN_IP_EXPRESSION = "(([0-9]{1,3}\\.){3}[0-9]{1,3})"; +const std::string IpAddress::PATTERN_IP_BYTES_EXPRESSION = "([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})"; +const regex::Pattern IpAddress::PATTERN_IP_BYTES = PATTERN_IP_BYTES_EXPRESSION; + +const int IpAddress::GRP_IP_FIRST_BYTE = 1; +const int IpAddress::GRP_IP_BYTES_COUNT = 4; + +const std::string IpRange::PATTERN_RANGE_EXPRESSION = "[[:space:]]*(.*)[[:space:]]*:[[:space:]]*" + IpAddress::PATTERN_IP_EXPRESSION + "[[:space:]]*-[[:space:]]*" + IpAddress::PATTERN_IP_EXPRESSION + "[[:space:]]*"; +const regex::Pattern IpRange::PATTERN_RANGE = PATTERN_RANGE_EXPRESSION; + +const int IpRange::GRP_DESCRIPTION = 1; +const int IpRange::GRP_FIRST_IP = 2; +const int IpRange::GRP_SECOND_IP = 4; + +} --- a/src/core/ip_range.cc +++ b/src/core/ip_range.cc @@ -0,0 +1,112 @@ +#include +#include + +#include "ip_range.h" +#include "utils/pattern.h" +#include "regex_namespace.h" + +namespace core { + +IpRange* IpRange::parse( const std::string& s ) { + regex::Match m = PATTERN_RANGE.match( s ); + + if( !m.matches() ) { + std::cout << "!! range format is invalid: '" << s << "'" << std::endl; + return NULL; + } + + std::string description = m.group( GRP_DESCRIPTION ); + std::string ip1 = m.group( GRP_FIRST_IP ); + std::string ip2 = m.group( GRP_SECOND_IP ); + IpAddress* from = IpAddress::parse( ip1 ); + IpAddress* to = IpAddress::parse( ip2 ); + + if( !from ) { + std::cout << "!! from is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; + return NULL; + } + if( !to ) { + std::cout << "!! to is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; + return NULL; + } + +// if( !from || !to || (*to < *from) ) +// return NULL; + + IpRange* r = new IpRange(); + + r->m_description = description; + r->m_from = from; + r->m_to = to; + + if( to && from && (*to < *from) ) { + std::cout << "!! to < from: " << r->to_string() << std::endl; + delete r; + return NULL; + } + + return r; +} + +//fast version +IpRange* IpRange::parse( const char *s, const int size ){ + static char description[256]; + static char ip1[24], ip2[24]; + int pos=0, post=0, enddesc=size-1; + while (enddesc>0 && s[enddesc]!=':') enddesc--; //find last ':' in the line + while((pos' '){ + if (post<23) ip2[post++]=s[pos]; + pos++; + } + ip2[post]=0; + } else ip2[0]=0; + + IpAddress* from = IpAddress::parse(ip1); + IpAddress* to = IpAddress::parse(ip2); + + if( !from ) { + std::cout << "!! from is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; + return NULL; + } + if( !to ) { + std::cout << "!! to is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; + return NULL; + } + + IpRange* r = new IpRange(); + r->m_description = description; + r->m_from = from; + r->m_to = to; + + if( (*to < *from) ) { + std::cout << "!! to < from: " << r->to_string() << std::endl; + delete r; + return NULL; + } + + return r; +} + +std::string IpRange::to_string() const { + std::stringstream result; + result << m_description << ": [" << m_from->to_string() << " - " << m_to->to_string() << ']'; + return result.str(); +} + +} --- a/src/core/ip_range.h +++ b/src/core/ip_range.h @@ -0,0 +1,67 @@ +#ifndef IPRANGE_H +#define IPRANGE_H + +#include + +#include "printable.h" +#include "ip_address.h" +#include "utils/pattern.h" +#include "regex_namespace.h" + +namespace core { + +class IpRange : public Printable { + public: // constants + static const std::string PATTERN_RANGE_EXPRESSION; + static const regex::Pattern PATTERN_RANGE; + + static const int GRP_DESCRIPTION; + static const int GRP_FIRST_IP; + static const int GRP_SECOND_IP; + + private: // fields + std::string m_description; + const IpAddress* m_from; + const IpAddress* m_to; + + private: // dynamic methods + IpRange() : m_description(), m_from(NULL), m_to(NULL) {} + + public: // static methods + typedef IpRange* ptr; + static IpRange* parse( const std::string& s ); + static IpRange* parse( const char *s, const int size ); + + public: // dynamic methods + IpRange( IpRange& rng ) { copy(rng); } + IpRange& operator= ( IpRange& rng ) { copy(rng); return *this; } + + void copy( IpRange& rng ) { + m_description = rng.m_description; + m_from = (!rng.m_from) ? NULL : new IpAddress( *rng.m_from ); + m_to = (!rng.m_to) ? NULL : new IpAddress( *rng.m_to ); + } + + const std::string& get_description ( void ) const { return m_description; } + const IpAddress* get_from ( void ) const { return m_from; } + const IpAddress* get_to ( void ) const { return m_to; } + + void set_description ( const std::string& description ) { m_description = description; } + void set_from ( const IpAddress* from ) { if( m_from ) delete m_from; m_from = new IpAddress( *from ); } + void set_to ( const IpAddress* to ) { if( m_to ) delete m_to; m_to = new IpAddress( *to ); } + + bool includes( const IpAddress& ip ) const { return includes((uint32_t)ip); } + bool includes( uint32_t ip ) const { return (*m_from <= ip) && (*m_to >= ip); } + + ~IpRange() { + delete m_from; + m_from = NULL; + delete m_to; + m_to = NULL; + } + + std::string to_string() const; +}; + +} +#endif --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -36,6 +36,17 @@ view.cc \ view.h \ view_manager.cc \ - view_manager.h + view_manager.h \ + ip_address.cc \ + ip_address.h \ + ip_filter.cc \ + ip_filter.h \ + ip_range.cc \ + ip_range.h \ + printable.h \ + regex_namespace.h \ + ip_filter_statics.cc \ + getline.h \ + getline.cc INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) --- a/src/core/Makefile.in +++ b/src/core/Makefile.in @@ -63,7 +63,12 @@ manager.$(OBJEXT) poll_manager.$(OBJEXT) \ poll_manager_epoll.$(OBJEXT) poll_manager_kqueue.$(OBJEXT) \ poll_manager_select.$(OBJEXT) view.$(OBJEXT) \ - view_manager.$(OBJEXT) + view_manager.$(OBJEXT) \ + ip_address.$(OBJEXT) \ + ip_filter.$(OBJEXT) \ + ip_range.$(OBJEXT) \ + ip_filter_statics.$(OBJEXT) \ + getline.$(OBJEXT) libsub_core_a_OBJECTS = $(am_libsub_core_a_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -259,7 +264,18 @@ view.cc \ view.h \ view_manager.cc \ - view_manager.h + view_manager.h \ + ip_address.cc \ + ip_address.h \ + ip_filter.cc \ + ip_filter.h \ + ip_range.cc \ + ip_range.h \ + printable.h \ + regex_namespace.h \ + ip_filter_statics.cc \ + getline.cc \ + getline.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) all: all-am @@ -327,6 +343,10 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/poll_manager_select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_address.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_range.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter_statics.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getline.Po@am__quote@ .cc.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -153,6 +153,24 @@ } } +uint32_t +Manager::filter_ip(const sockaddr* sa) { + IpRange* r = NULL; + // if something's wrong with filter or address it's gonna be allowed + if( m_ipFilter && sa ) { + const rak::socket_address* socketAddress = rak::socket_address::cast_from(sa); + if( socketAddress->is_valid() && (socketAddress->family() == rak::socket_address::af_inet) ) + r = m_ipFilter->find_range( socketAddress->sa_inet()->address_h() ); + if( r ) + m_logComplete.push_front("Address '" + socketAddress->address_str() + "' is rejected by IP filter range '" + r->to_string()); + else + if( rpc::call_command_value("get_handshake_log") ) + m_logComplete.push_front("IP Filter allowed connection with '" + socketAddress->address_str() + "'"); + } + return (r==NULL); +} + + void Manager::push_log(const char* msg) { m_logImportant.push_front(msg); @@ -160,7 +178,8 @@ } Manager::Manager() : - m_hashingView(NULL) + m_hashingView(NULL), + m_ipFilter(NULL) // m_pollManager(NULL) { { m_downloadStore = new DownloadStore(); @@ -181,6 +200,8 @@ delete m_downloadStore; delete m_httpQueue; delete m_fileStatusCache; + + set_ip_filter( NULL ); } void @@ -226,6 +247,7 @@ CurlStack::global_init(); torrent::connection_manager()->set_signal_handshake_log(sigc::mem_fun(this, &Manager::handshake_log)); + torrent::connection_manager()->set_filter(sigc::mem_fun(this, &Manager::filter_ip)); } void @@ -585,4 +607,14 @@ } } +void Manager::reload_ip_filter(void) { + if( m_ipFilter ) { + push_log("Reloading IP filter"); + m_ipFilter->reload(); + std::stringstream logMsg("IpFilter reloaded with "); + logMsg << m_ipFilter->size() << " ranges total. " << m_ipFilter->get_merges() << " ranges were merged."; + push_log( logMsg.str().c_str() ); + } +} + } --- a/src/core/manager.h +++ b/src/core/manager.h @@ -47,6 +47,8 @@ #include "range_map.h" #include "log.h" +#include "ip_filter.h" + namespace torrent { class Bencode; } @@ -118,6 +120,15 @@ void handshake_log(const sockaddr* sa, int msg, int err, const torrent::HashString* hash); + uint32_t filter_ip(const sockaddr* sa); + + void set_ip_filter( IpFilter* ipFilter ) { + IpFilter* old = m_ipFilter; + m_ipFilter = ipFilter; + if( old ) delete old; + } + void reload_ip_filter(void); + static const int create_start = 0x1; static const int create_tied = 0x2; static const int create_quiet = 0x4; @@ -154,6 +165,8 @@ Log m_logImportant; Log m_logComplete; + + IpFilter* m_ipFilter; }; // Meh, cleanup. --- a/src/core/printable.h +++ b/src/core/printable.h @@ -0,0 +1,16 @@ +#ifndef PRINTABLE_H +#define PRINTABLE_H + +#include + +class Printable { + public: + virtual std::string to_string() const = 0; +}; + +template inline std::basic_ostream<_CharT,_Traits>& + operator<<( std::basic_ostream<_CharT,_Traits>& out, const Printable& val) { + return out << val.to_string(); +} + +#endif --- a/src/core/regex_namespace.h +++ b/src/core/regex_namespace.h @@ -0,0 +1,6 @@ +#ifndef REGEXNAMESPACE_H +#define REGEXNAMESPACE_H + +namespace regex = utils; + +#endif --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -9,6 +9,8 @@ lockfile.cc \ lockfile.h \ socket_fd.cc \ - socket_fd.h + socket_fd.h \ + pattern.cc \ + pattern.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) --- a/src/utils/Makefile.in +++ b/src/utils/Makefile.in @@ -58,7 +58,7 @@ libsub_utils_a_LIBADD = am_libsub_utils_a_OBJECTS = directory.$(OBJEXT) \ file_status_cache.$(OBJEXT) lockfile.$(OBJEXT) \ - socket_fd.$(OBJEXT) + socket_fd.$(OBJEXT) pattern.$(OBJEXT) libsub_utils_a_OBJECTS = $(am_libsub_utils_a_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -227,7 +227,9 @@ lockfile.cc \ lockfile.h \ socket_fd.cc \ - socket_fd.h + socket_fd.h \ + pattern.cc \ + pattern.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) all: all-am @@ -282,6 +284,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_status_cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lockfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_fd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pattern.Po@am__quote@ .cc.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< --- a/src/utils/pattern.cc +++ b/src/utils/pattern.cc @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include "pattern.h" + +namespace utils { + +int Pattern::countGroups( const std::string& str ) { + int count1 = 0; + int count2 = 0; + + for( size_t index = -1; (index = str.find( '(', index+1 )) != std::string::npos; ) + count1++; + for( size_t index = -1; (index = str.find( ')', index+1 )) != std::string::npos; ) + count2++; + + return (count1 < count2) ? count1 : count2; +} + +Pattern::Pattern( const std::string& pattern, Flags flags ) : lastResult(-1), + preg(NULL) { + int regFlags = REG_EXTENDED | REG_ICASE | REG_NEWLINE; + if( !(flags & CASE_SENSITIVE) ) + regFlags ^= REG_ICASE; + if( (flags & DOT_MATCH_NEWLINE) ) + regFlags ^= REG_NEWLINE; + + preg = new regex_t; + numGroups = countGroups( pattern ) + 1; + + lastResult = regcomp( preg, pattern.c_str(), regFlags ); +} + +Pattern::~Pattern() { + regfree( preg ); + delete( preg ); +} + +std::string Pattern::getLastError() const { + char errBuf[1024]; + regerror( lastResult, preg, errBuf, sizeof(errBuf) ); + return std::string(errBuf); +} + +Match Pattern::match( const std::string& expression ) const { + + regmatch_t* pmatch = new regmatch_t[numGroups]; + int res = regexec( preg, expression.c_str(), numGroups, pmatch, 0 ); + return Match( expression, numGroups, pmatch, res, getLastError() ); +} + +Match::Match( const std::string& expr, int ngroups, regmatch_t* groups, int result, const std::string& message ) : + expression( expr ), + nmatch( ngroups ), + pmatch( groups ), + matchResult( result ), + matchMessage( message ) { +} + +std::string Match::group( int i ) { + if( (i >= nmatch) || (pmatch[i].rm_so < 0) ) + return ""; + + return expression.substr( pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so ); +} + +std::string& trim( std::string& str ) { + std::string::iterator it; + for( it = str.begin(); (it < str.end()) && ( isspace(*it) || (*it == 0) ) ; it++ ); + str.erase( str.begin(), it ); + for( it = str.end()-1; (it >= str.begin()) && ( isspace(*it) || (*it == 0) ) ; it-- ); + str.erase( ++it, str.end() ); + return str; +} + +} + --- a/src/utils/pattern.h +++ b/src/utils/pattern.h @@ -0,0 +1,59 @@ +#ifndef PATTERN_H +#define PATTERN_H + +#include +#include +#include +#include + +namespace utils { + +class Match { + public: + Match( const std::string& expr, int ngroups, regmatch_t* pmatch, int matchResult, const std::string& matchMessage ); + ~Match() { delete[] pmatch; } + std::string group( int i ); + bool found() { return (matchResult == 0); } + bool matches() { return found(); } + std::string& getMatchMessage() { return matchMessage; } + + private: + std::string expression; + int nmatch; + regmatch_t* pmatch; + int matchResult; + std::string matchMessage; +}; + +class Pattern { + public: + enum Flags { + DEFAULT = 0, // REG_EXTENDED | REG_ICASE | REG_NEWLINE + CASE_SENSITIVE, + DOT_MATCH_NEWLINE + }; + + public: + Pattern( const std::string& pattern, Flags f = Pattern::DEFAULT ); + ~Pattern(); + bool isSuccess() { return (lastResult == 0); } + std::string getLastError() const; + Match match( const std::string& expression ) const; + + private: + int countGroups( const std::string& str ); + + private: + regex_t* preg; + int lastResult; + int numGroups; +}; + + +std::string& trim( std::string& str ); + +} + +// end of ifdef PATTERN_H +#endif +