diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index 59dc50363..3f00d572d 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -91,6 +91,7 @@ TESTS = \ bencode-test \ clients-test \ json-test \ + rpc-test \ test-fastset \ test-peer-id \ utils-test @@ -117,6 +118,8 @@ clients_test_SOURCES = clients-test.c clients_test_LDADD = $(APPS_LDADD) json_test_SOURCES = json-test.c json_test_LDADD = $(APPS_LDADD) +rpc_test_SOURCES = rpc-test.c +rpc_test_LDADD = $(APPS_LDADD) test_fastset_SOURCES = test-fastset.c test_fastset_LDADD = $(APPS_LDADD) test_peer_id_SOURCES = test-peer-id.c diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index 7d1450f84..558284ec3 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -11,6 +11,9 @@ */ #include +#include /* isdigit */ +#include +#include /* strtol */ #include #include @@ -218,13 +221,61 @@ testACL( const char * s ) return NULL; } -int -tr_rpcSetACL( tr_rpc_server * server, const char * acl, char ** setme_errmsg ) +/* 192.*.*.* --> 192.0.0.0/8 + 192.64.*.* --> 192.64.0.0/16 + 192.64.1.* --> 192.64.1.0/24 + 192.64.1.2 --> 192.64.1.2/32 */ +static void +cidrizeOne( const char * in, int len, struct evbuffer * out ) { - const int isRunning = server->ctx != NULL; - int ret = 0; - char * errmsg = testACL( acl ); + int stars = 0; + const char * pch; + const char * end; + char zero = '0'; + char huh = '?'; + for( pch=in, end=pch+len; pch!=end; ++pch ) { + if( stars && isdigit(*pch) ) + evbuffer_add( out, &huh, 1 ); + else if( *pch!='*' ) + evbuffer_add( out, pch, 1 ); + else { + evbuffer_add( out, &zero, 1 ); + ++stars; + } + } + + evbuffer_add_printf( out, "/%d", (32-(stars*8))); +} + +char* +cidrize( const char * acl ) +{ + int len; + const char * walk = acl; + char * ret; + struct evbuffer * out = evbuffer_new( ); + + FOR_EACH_WORD_IN_LIST( walk, len ) + { + cidrizeOne( walk, len, out ); + evbuffer_add_printf( out, ", " ); + } + + /* the -2 is to eat the final ", " */ + ret = tr_strndup( (char*) EVBUFFER_DATA(out), EVBUFFER_LENGTH(out)-2 ); + evbuffer_free( out ); + return ret; +} + +int +tr_rpcTestACL( const tr_rpc_server * server UNUSED, + const char * acl, + char ** setme_errmsg ) +{ + int ret = 0; + char * cidr = cidrize( acl ); + char * errmsg = testACL( cidr ); if( errmsg ) { if( setme_errmsg ) @@ -233,13 +284,27 @@ tr_rpcSetACL( tr_rpc_server * server, const char * acl, char ** setme_errmsg ) tr_free( errmsg ); ret = -1; } - else + tr_free( cidr ); + return ret; +} + +int +tr_rpcSetACL( tr_rpc_server * server, + const char * acl, + char ** setme_errmsg ) +{ + char * cidr = cidrize( acl ); + const int ret = tr_rpcTestACL( server, cidr, setme_errmsg ); + + if( ret ) { + const int isRunning = server->ctx != NULL; + if( isRunning ) stopServer( server ); tr_free( server->acl ); - server->acl = tr_strdup( acl ); + server->acl = tr_strdup( cidr ); if( isRunning ) startServer( server ); diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index b452e8377..f86d53382 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -32,6 +32,10 @@ void tr_rpcSetPort ( tr_rpc_server * server, int tr_rpcGetPort ( const tr_rpc_server * server ); +int tr_rpcSetTest ( const tr_rpc_server * server, + const char * acl, + char ** allocme_errmsg ); + int tr_rpcSetACL ( tr_rpc_server * server, const char * acl, char ** allocme_errmsg ); diff --git a/libtransmission/rpc-test.c b/libtransmission/rpc-test.c new file mode 100644 index 000000000..fabb601eb --- /dev/null +++ b/libtransmission/rpc-test.c @@ -0,0 +1,75 @@ +#include /* fprintf */ +#include /* strcmp */ +#include "transmission.h" +#include "utils.h" + +#define VERBOSE 0 + +int test = 0; + +#define check(A) { \ + ++test; \ + if (A) { \ + if( VERBOSE ) \ + fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \ + } else { \ + if( VERBOSE ) \ + fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \ + return test; \ + } \ +} + +extern char* cidrize( const char * in ); + +extern int tr_rpcTestACL( const void * unused, + const char * acl, + char ** setme_errmsg ); + +static int +testWildcard( const char * in, const char * expected ) +{ + int ok; + char * str = cidrize( in ); +/*fprintf( stderr, "in [%s] out [%s] should be [%s]\n", in, str, expected );*/ + ok = expected ? !strcmp( expected, str ) : !str; + tr_free( str ); + return ok; +} + +static int +test_acl( void ) +{ + int err; + char * errmsg = NULL; + + check( testWildcard( "192.*.*.*", "192.0.0.0/8" ) ); + check( testWildcard( "192.64.*.*", "192.64.0.0/16" ) ); + check( testWildcard( "192.64.0.*", "192.64.0.0/24" ) ); + check( testWildcard( "192.64.0.1", "192.64.0.1/32" ) ); + check( testWildcard( "+192.*.*.*, -192.64.*.*", "+192.0.0.0/8, -192.64.0.0/16" ) ); + + err = tr_rpcTestACL( NULL, "+192.*.*.*", &errmsg ); + check( !err ); + check( !errmsg ); + err = tr_rpcTestACL( NULL, "+192.*.8.*", &errmsg ); + check( err ); + check( errmsg ); + tr_free( errmsg ); + errmsg = NULL; + err = tr_rpcTestACL( NULL, "+192.*.*.*, -192.168.*.*", &errmsg ); + check( !err ); + check( !errmsg ); + + return 0; +} + +int +main( void ) +{ + int i; + + if(( i = test_acl( ))) + return i; + + return 0; +} diff --git a/libtransmission/session.c b/libtransmission/session.c index 977efade1..a850b09bc 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -775,6 +775,14 @@ tr_sessionSetRPCCallback( tr_handle * session, session->rpc_func_user_data = user_data; } +int +tr_sessionTestRPCACL( const tr_handle * session, + const char * acl, + char ** allocme_errmsg ) +{ + return tr_rpcTestACL( session->rpcServer, acl, allocme_errmsg ); +} + int tr_sessionSetRPCACL( tr_handle * session, const char * acl, diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index d276d1127..0d88cd039 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -292,21 +292,45 @@ void tr_sessionSetRPCPort( tr_handle *, int port ); int tr_sessionGetRPCPort( const tr_handle * ); /** - * Specify access control list (ACL). ACL is a comma separated list - * of IP subnets, each subnet is prepended by a '-' or '+' sign. - * Plus means allow, minus means deny. If the subnet mask is omitted, - * like * "-1.2.3.4", it means a single IP address. The mask may vary - * from 0 to 32 inclusive. + * @brief test an ACL's syntax without modifying the RPC settings. + * + * ACL is a comma separated list of IP subnets, each subnet is prepended + * by a '+' or '-' sign to denote 'allow' or 'deny'. If the subnet mask + * is omitted, like "-1.2.3.4", it means a single IP address. The mask + * may vary from 0 to 32 inclusive. A simple primer on x.x.x.x/n notation + * can be found at . * - * The default string is "+127.0.0.1" - * - * @param acl the new ACL to use. + * Since wildcards are more familiar to most users than netmasks, + * libtransmission supports a wildcard notation that it + * converts into cidr required by the embedded http server. + * So, notation like "+192.168.*.*" is accepted by libtransmission and is + * converted to "+192.168.0.0/16" before it reaches the server. + + * @param acl the ACL to test * @param allocme_errmsg If the ACL can't be parsed, this is set to a * newly-allocated error string describing the problem. * The client should tr_free() this string when done. - * * @return 0 on success, -1 on failure due to an unparseable ACL. + */ +int tr_sessionTestRPCACL( const tr_handle * session, + const char * acl, + char ** allocme_errmsg ); + +/** + * @brief Specify access control list (ACL). + ACL is a comma separated list + * of IP subnets, each subnet is prepended by a '-' or '+' sign. + * Plus means allow, minus means deny. If the subnet mask is omitted, + * like "-1.2.3.4", it means a single IP address. The mask may vary + * from 0 to 32 inclusive. * + * http://25yearsofprogramming.com/blog/20070803.htm has a simple + * primer on x.x.x.x/n notation for those interested. + * + * The parameters and return value follow the same behavior as + * tr_sessionTestRPCACL(). + * + * @see tr_sessionTestRPCACL * @see tr_sessionInitFull * @see tr_sessionGetRPCACL */