2006-03-17 19:27:22 +03:00
/*
Unix SMB / CIFS implementation .
SMB torture tester
Copyright ( C ) Andrew Tridgell 1997 - 2003
Copyright ( C ) Jelmer Vernooij 2006
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# include "lib/cmdline/popt_common.h"
# include "system/time.h"
# include "system/wait.h"
# include "system/filesys.h"
# include "libcli/libcli.h"
# include "lib/ldb/include/ldb.h"
# include "lib/events/events.h"
# include "torture/torture.h"
2006-03-25 22:12:08 +03:00
# include "torture/ui.h"
2006-03-17 19:27:22 +03:00
# include "build.h"
# include "dlinklist.h"
2006-03-18 18:42:57 +03:00
# include "librpc/rpc/dcerpc.h"
2006-03-17 19:27:22 +03:00
# define MAX_COLS 80 /* FIXME: Determine this at run-time */
/****************************************************************************
run a specified test or " ALL "
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-03-25 22:12:08 +03:00
static BOOL run_test ( struct torture_context * torture , const char * name )
2006-03-17 19:27:22 +03:00
{
BOOL ret = True ;
struct torture_op * o ;
BOOL matched = False ;
if ( strequal ( name , " ALL " ) ) {
for ( o = torture_ops ; o ; o = o - > next ) {
2006-03-25 22:12:08 +03:00
if ( ! run_test ( torture , o - > name ) ) {
2006-03-17 19:27:22 +03:00
ret = False ;
}
}
return ret ;
}
for ( o = torture_ops ; o ; o = o - > next ) {
if ( gen_fnmatch ( name , o - > name ) = = 0 ) {
double t ;
matched = True ;
init_iconv ( ) ;
printf ( " Running %s \n " , o - > name ) ;
if ( o - > multi_fn ) {
BOOL result = False ;
t = torture_create_procs ( o - > multi_fn ,
& result ) ;
if ( ! result ) {
ret = False ;
printf ( " TEST %s FAILED! \n " , o - > name ) ;
}
} else {
struct timeval tv = timeval_current ( ) ;
2006-03-25 22:12:08 +03:00
if ( ! o - > fn ( torture ) ) {
2006-03-17 19:27:22 +03:00
ret = False ;
printf ( " TEST %s FAILED! \n " , o - > name ) ;
}
t = timeval_elapsed ( & tv ) ;
}
printf ( " %s took %g secs \n \n " , o - > name , t ) ;
}
}
if ( ! matched ) {
printf ( " Unknown torture operation '%s' \n " , name ) ;
2006-03-30 16:16:25 +04:00
ret = False ;
2006-03-17 19:27:22 +03:00
}
return ret ;
}
static void parse_dns ( const char * dns )
{
char * userdn , * basedn , * secret ;
char * p , * d ;
/* retrievieng the userdn */
p = strchr_m ( dns , ' # ' ) ;
if ( ! p ) {
lp_set_cmdline ( " torture:ldap_userdn " , " " ) ;
lp_set_cmdline ( " torture:ldap_basedn " , " " ) ;
lp_set_cmdline ( " torture:ldap_secret " , " " ) ;
return ;
}
userdn = strndup ( dns , p - dns ) ;
lp_set_cmdline ( " torture:ldap_userdn " , userdn ) ;
/* retrieve the basedn */
d = p + 1 ;
p = strchr_m ( d , ' # ' ) ;
if ( ! p ) {
lp_set_cmdline ( " torture:ldap_basedn " , " " ) ;
lp_set_cmdline ( " torture:ldap_secret " , " " ) ;
return ;
}
basedn = strndup ( d , p - d ) ;
lp_set_cmdline ( " torture:ldap_basedn " , basedn ) ;
/* retrieve the secret */
p = p + 1 ;
if ( ! p ) {
lp_set_cmdline ( " torture:ldap_secret " , " " ) ;
return ;
}
secret = strdup ( p ) ;
lp_set_cmdline ( " torture:ldap_secret " , secret ) ;
printf ( " %s - %s - %s \n " , userdn , basedn , secret ) ;
}
static void usage ( poptContext pc )
{
struct torture_op * o ;
2006-04-19 07:06:50 +04:00
char last_prefix [ 64 ] ;
2006-03-17 19:27:22 +03:00
int i ;
poptPrintUsage ( pc , stdout , 0 ) ;
printf ( " \n " ) ;
printf ( " The binding format is: \n \n " ) ;
printf ( " TRANSPORT:host[flags] \n \n " ) ;
2006-05-27 16:16:48 +04:00
printf ( " where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP \n " ) ;
printf ( " or ncalrpc for local connections. \n \n " ) ;
2006-03-17 19:27:22 +03:00
printf ( " 'host' is an IP or hostname or netbios name. If the binding string \n " ) ;
printf ( " identifies the server side of an endpoint, 'host' may be an empty \n " ) ;
printf ( " string. \n \n " ) ;
printf ( " 'flags' can include a SMB pipe name if using the ncacn_np transport or \n " ) ;
printf ( " a TCP port number if using the ncacn_ip_tcp transport, otherwise they \n " ) ;
printf ( " will be auto-determined. \n \n " ) ;
printf ( " other recognised flags are: \n \n " ) ;
printf ( " sign : enable ntlmssp signing \n " ) ;
printf ( " seal : enable ntlmssp sealing \n " ) ;
printf ( " connect : enable rpc connect level auth (auth, but no sign or seal) \n " ) ;
printf ( " validate: enable the NDR validator \n " ) ;
printf ( " print: enable debugging of the packets \n " ) ;
printf ( " bigendian: use bigendian RPC \n " ) ;
printf ( " padcheck: check reply data for non-zero pad bytes \n \n " ) ;
printf ( " For example, these all connect to the samr pipe: \n \n " ) ;
printf ( " ncacn_np:myserver \n " ) ;
printf ( " ncacn_np:myserver[samr] \n " ) ;
printf ( " ncacn_np:myserver[ \\ pipe \\ samr] \n " ) ;
printf ( " ncacn_np:myserver[/pipe/samr] \n " ) ;
printf ( " ncacn_np:myserver[samr,sign,print] \n " ) ;
printf ( " ncacn_np:myserver[ \\ pipe \\ samr,sign,seal,bigendian] \n " ) ;
printf ( " ncacn_np:myserver[/pipe/samr,seal,validate] \n " ) ;
printf ( " ncacn_np: \n " ) ;
printf ( " ncacn_np:[/pipe/samr] \n \n " ) ;
printf ( " ncacn_ip_tcp:myserver \n " ) ;
printf ( " ncacn_ip_tcp:myserver[1024] \n " ) ;
printf ( " ncacn_ip_tcp:myserver[1024,sign,seal] \n \n " ) ;
2006-05-27 16:16:48 +04:00
printf ( " ncalrpc: \n \n " ) ;
2006-04-19 07:06:50 +04:00
printf ( " The UNC format is: \n \n " ) ;
2006-03-17 19:27:22 +03:00
2006-04-19 07:06:50 +04:00
printf ( " //server/share \n \n " ) ;
2006-03-17 19:27:22 +03:00
2006-04-19 07:06:50 +04:00
printf ( " Tests are: " ) ;
2006-03-17 19:27:22 +03:00
i = 0 ;
2006-04-19 07:06:50 +04:00
last_prefix [ 0 ] = ' \0 ' ;
2006-03-17 19:27:22 +03:00
for ( o = torture_ops ; o ; o = o - > next ) {
2006-04-19 07:06:50 +04:00
const char * sep ;
if ( ( sep = strchr ( o - > name , ' - ' ) ) ) {
if ( strncmp ( o - > name , last_prefix , sep - o - > name ) ! = 0 ) {
strncpy ( last_prefix , o - > name ,
MIN ( sizeof ( last_prefix ) ,
sep - o - > name ) ) ;
printf ( " \n \n " ) ;
i = 0 ;
}
}
if ( i + strlen ( o - > name ) > = ( MAX_COLS - 2 ) ) {
printf ( " \n " ) ;
2006-03-17 19:27:22 +03:00
i = 0 ;
}
i + = printf ( " %s " , o - > name ) ;
}
printf ( " \n \n " ) ;
2006-04-19 07:06:50 +04:00
printf ( " The default test is ALL. \n " ) ;
2006-03-17 19:27:22 +03:00
exit ( 1 ) ;
}
static BOOL is_binding_string ( const char * binding_string )
{
TALLOC_CTX * mem_ctx = talloc_init ( " is_binding_string " ) ;
struct dcerpc_binding * binding_struct ;
NTSTATUS status ;
status = dcerpc_parse_binding ( mem_ctx , binding_string , & binding_struct ) ;
talloc_free ( mem_ctx ) ;
return NT_STATUS_IS_OK ( status ) ;
}
static void max_runtime_handler ( int sig )
{
DEBUG ( 0 , ( " maximum runtime exceeded for smbtorture - terminating \n " ) ) ;
exit ( 1 ) ;
}
2006-03-25 22:12:08 +03:00
static void simple_test_start ( struct torture_test * test )
{
printf ( " Testing %s... \n " , test - > name ) ;
}
2006-05-22 22:59:56 +04:00
static void simple_test_result ( struct torture_test * test , enum torture_result res , const char * reason )
2006-03-25 22:12:08 +03:00
{
2006-05-22 22:59:56 +04:00
printf ( " \t %d: %s \n " , res , reason ? reason : " " ) ;
2006-03-25 22:12:08 +03:00
}
static void simple_comment ( struct torture_test * test , const char * comment )
{
printf ( " # %s \n " , comment ) ;
}
const static struct torture_ui_ops std_ui_ops = {
. comment = simple_comment ,
. test_start = simple_test_start ,
. test_result = simple_test_result
} ;
2006-06-12 16:24:33 +04:00
static void subunit_test_start ( struct torture_test * test )
{
printf ( " test: %s \n " , test - > name ) ;
}
static void subunit_test_result ( struct torture_test * test , enum torture_result res , const char * reason )
{
switch ( res ) {
case TORTURE_OK :
printf ( " success: %s \n " , test - > name ) ;
break ;
case TORTURE_FAIL :
printf ( " failure: %s \n " , test - > name ) ;
break ;
case TORTURE_TODO :
printf ( " todo: %s \n " , test - > name ) ;
break ;
case TORTURE_SKIP :
printf ( " skip: %s \n " , test - > name ) ;
break ;
}
}
static void subunit_comment ( struct torture_test * test , const char * comment )
{
printf ( " # %s \n " , comment ) ;
}
const static struct torture_ui_ops subunit_ui_ops = {
. comment = subunit_comment ,
. test_start = subunit_test_start ,
. test_result = subunit_test_result
} ;
2006-03-17 19:27:22 +03:00
/****************************************************************************
main program
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int main ( int argc , char * argv [ ] )
{
int opt , i ;
char * p ;
BOOL correct = True ;
int max_runtime = 0 ;
int argc_new ;
2006-03-25 22:12:08 +03:00
struct torture_context * torture ;
2006-03-17 19:27:22 +03:00
char * * argv_new ;
poptContext pc ;
2006-06-12 16:24:33 +04:00
static char * ui_ops_name = " simple " ;
2006-03-17 19:27:22 +03:00
enum { OPT_LOADFILE = 1000 , OPT_UNCLIST , OPT_TIMELIMIT , OPT_DNS ,
2006-05-05 13:52:12 +04:00
OPT_DANGEROUS , OPT_SMB_PORTS , OPT_ASYNC } ;
2006-03-17 19:27:22 +03:00
struct poptOption long_options [ ] = {
POPT_AUTOHELP
2006-06-12 16:24:33 +04:00
{ " format " , 0 , POPT_ARG_STRING , & ui_ops_name , 0 , " Output format (one of: simple, subunit) " , NULL } ,
2006-03-17 19:27:22 +03:00
{ " smb-ports " , ' p ' , POPT_ARG_STRING , NULL , OPT_SMB_PORTS , " SMB ports " , NULL } ,
{ " seed " , 0 , POPT_ARG_INT , & torture_seed , 0 , " seed " , NULL } ,
{ " num-progs " , 0 , POPT_ARG_INT , & torture_nprocs , 0 , " num progs " , NULL } ,
{ " num-ops " , 0 , POPT_ARG_INT , & torture_numops , 0 , " num ops " , NULL } ,
{ " entries " , 0 , POPT_ARG_INT , & torture_entries , 0 , " entries " , NULL } ,
{ " use-oplocks " , ' L ' , POPT_ARG_NONE , & use_oplocks , 0 , " use oplocks " , NULL } ,
{ " show-all " , 0 , POPT_ARG_NONE , & torture_showall , 0 , " show all " , NULL } ,
{ " loadfile " , 0 , POPT_ARG_STRING , NULL , OPT_LOADFILE , " loadfile " , NULL } ,
{ " unclist " , 0 , POPT_ARG_STRING , NULL , OPT_UNCLIST , " unclist " , NULL } ,
{ " timelimit " , ' t ' , POPT_ARG_STRING , NULL , OPT_TIMELIMIT , " timelimit " , NULL } ,
{ " failures " , ' f ' , POPT_ARG_INT , & torture_failures , 0 , " failures " , NULL } ,
{ " parse-dns " , ' D ' , POPT_ARG_STRING , NULL , OPT_DNS , " parse-dns " , NULL } ,
2006-05-05 13:52:12 +04:00
{ " dangerous " , ' X ' , POPT_ARG_NONE , NULL , OPT_DANGEROUS ,
" run dangerous tests (eg. wiping out password database) " , NULL } ,
{ " async " , ' a ' , POPT_ARG_NONE , NULL , OPT_ASYNC ,
" run async tests " , NULL } ,
{ " num-async " , 0 , POPT_ARG_INT , & torture_numasync , 0 ,
" number of simultaneous async requests " , NULL } ,
2006-03-17 19:27:22 +03:00
{ " maximum-runtime " , 0 , POPT_ARG_INT , & max_runtime , 0 ,
" set maximum time for smbtorture to live " , " seconds " } ,
POPT_COMMON_SAMBA
POPT_COMMON_CONNECTION
POPT_COMMON_CREDENTIALS
POPT_COMMON_VERSION
POPT_TABLEEND
} ;
# ifdef HAVE_SETBUFFER
setbuffer ( stdout , NULL , 0 ) ;
# endif
/* we are never interested in SIGPIPE */
BlockSignals ( True , SIGPIPE ) ;
pc = poptGetContext ( " smbtorture " , argc , ( const char * * ) argv , long_options ,
POPT_CONTEXT_KEEP_FIRST ) ;
poptSetOtherOptionHelp ( pc , " <binding>|<unc> TEST1 TEST2 ... " ) ;
while ( ( opt = poptGetNextOpt ( pc ) ) ! = - 1 ) {
switch ( opt ) {
case OPT_LOADFILE :
lp_set_cmdline ( " torture:loadfile " , poptGetOptArg ( pc ) ) ;
break ;
case OPT_UNCLIST :
lp_set_cmdline ( " torture:unclist " , poptGetOptArg ( pc ) ) ;
break ;
case OPT_TIMELIMIT :
lp_set_cmdline ( " torture:timelimit " , poptGetOptArg ( pc ) ) ;
break ;
case OPT_DNS :
parse_dns ( poptGetOptArg ( pc ) ) ;
break ;
case OPT_DANGEROUS :
lp_set_cmdline ( " torture:dangerous " , " Yes " ) ;
break ;
2006-05-05 13:52:12 +04:00
case OPT_ASYNC :
lp_set_cmdline ( " torture:async " , " Yes " ) ;
break ;
2006-03-17 19:27:22 +03:00
case OPT_SMB_PORTS :
lp_set_cmdline ( " smb ports " , poptGetOptArg ( pc ) ) ;
break ;
default :
d_printf ( " Invalid option %s: %s \n " ,
poptBadOption ( pc , 0 ) , poptStrerror ( opt ) ) ;
2006-04-07 22:02:51 +04:00
torture_init ( ) ;
2006-03-17 19:27:22 +03:00
usage ( pc ) ;
exit ( 1 ) ;
}
}
if ( max_runtime ) {
/* this will only work if nobody else uses alarm(),
which means it won ' t work for some tests , but we
can ' t use the event context method we use for smbd
as so many tests create their own event
context . This will at least catch most cases . */
signal ( SIGALRM , max_runtime_handler ) ;
alarm ( max_runtime ) ;
}
2006-04-07 22:02:51 +04:00
torture_init ( ) ;
2006-03-17 19:27:22 +03:00
ldb_global_init ( ) ;
if ( torture_seed = = 0 ) {
torture_seed = time ( NULL ) ;
}
printf ( " Using seed %d \n " , torture_seed ) ;
srandom ( torture_seed ) ;
argv_new = discard_const_p ( char * , poptGetArgs ( pc ) ) ;
argc_new = argc ;
for ( i = 0 ; i < argc ; i + + ) {
if ( argv_new [ i ] = = NULL ) {
argc_new = i ;
break ;
}
}
if ( argc_new < 3 ) {
usage ( pc ) ;
exit ( 1 ) ;
}
for ( p = argv_new [ 1 ] ; * p ; p + + ) {
if ( * p = = ' \\ ' )
* p = ' / ' ;
}
/* see if its a RPC transport specifier */
if ( is_binding_string ( argv_new [ 1 ] ) ) {
lp_set_cmdline ( " torture:binding " , argv_new [ 1 ] ) ;
} else {
char * binding = NULL ;
char * host = NULL , * share = NULL ;
if ( ! smbcli_parse_unc ( argv_new [ 1 ] , NULL , & host , & share ) ) {
d_printf ( " Invalid option: %s is not a valid torture target (share or binding string) \n \n " , argv_new [ 1 ] ) ;
usage ( pc ) ;
}
lp_set_cmdline ( " torture:host " , host ) ;
lp_set_cmdline ( " torture:share " , share ) ;
asprintf ( & binding , " ncacn_np:%s " , host ) ;
lp_set_cmdline ( " torture:binding " , binding ) ;
}
2006-03-25 22:12:08 +03:00
torture = talloc_zero ( NULL , struct torture_context ) ;
2006-06-12 16:24:33 +04:00
if ( ! strcmp ( ui_ops_name , " simple " ) ) {
torture - > ui_ops = & std_ui_ops ;
} else if ( ! strcmp ( ui_ops_name , " subunit " ) ) {
torture - > ui_ops = & subunit_ui_ops ;
} else {
printf ( " Unknown output format '%s' \n " , ui_ops_name ) ;
exit ( 1 ) ;
}
2006-03-25 22:12:08 +03:00
2006-03-17 19:27:22 +03:00
if ( argc_new = = 0 ) {
printf ( " You must specify a test to run, or 'ALL' \n " ) ;
} else {
for ( i = 2 ; i < argc_new ; i + + ) {
2006-03-25 22:12:08 +03:00
if ( ! run_test ( torture , argv_new [ i ] ) ) {
2006-03-17 19:27:22 +03:00
correct = False ;
}
}
}
2006-03-25 22:12:08 +03:00
talloc_free ( torture ) ;
2006-03-17 19:27:22 +03:00
if ( correct ) {
return ( 0 ) ;
} else {
return ( 1 ) ;
}
}