524 lines
14 KiB
C
524 lines
14 KiB
C
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <event2/buffer.h>
|
|
|
|
#include "transmission.h"
|
|
#include "bencode.h"
|
|
#include "utils.h" /* tr_free */
|
|
|
|
// #define VERBOSE
|
|
#include "libtransmission-test.h"
|
|
|
|
#ifndef WIN32
|
|
#define STACK_SMASH_DEPTH (1 * 1000 * 1000)
|
|
#else
|
|
#define STACK_SMASH_DEPTH ( 100 * 1000)
|
|
#endif
|
|
|
|
static int
|
|
testInt( void )
|
|
{
|
|
uint8_t buf[128];
|
|
int64_t val;
|
|
int err;
|
|
const uint8_t * end;
|
|
|
|
/* good int string */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
|
|
err = tr_bencParseInt( buf, buf + 4, &end, &val );
|
|
check_int_eq( 0, err );
|
|
check_int_eq( 64, val );
|
|
check( (buf + 4) == end );
|
|
|
|
/* missing 'e' */
|
|
end = NULL;
|
|
val = 888;
|
|
err = tr_bencParseInt( buf, buf + 3, &end, &val );
|
|
check_int_eq( EILSEQ, err );
|
|
check_int_eq( 888, val );
|
|
check( end == NULL );
|
|
|
|
/* empty buffer */
|
|
err = tr_bencParseInt( buf, buf + 0, &end, &val );
|
|
check_int_eq( EILSEQ, err );
|
|
check_int_eq( 888, val );
|
|
check( end == NULL );
|
|
|
|
/* bad number */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i6z4e" );
|
|
err = tr_bencParseInt( buf, buf + 5, &end, &val );
|
|
check_int_eq( EILSEQ, err );
|
|
check_int_eq( 888, val );
|
|
check( end == NULL );
|
|
|
|
/* negative number */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i-3e" );
|
|
err = tr_bencParseInt( buf, buf + 4, &end, &val );
|
|
check_int_eq( 0, err );
|
|
check_int_eq( -3, val );
|
|
check( (buf + 4) == end );
|
|
|
|
/* zero */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i0e" );
|
|
err = tr_bencParseInt( buf, buf + 4, &end, &val );
|
|
check_int_eq( 0, err );
|
|
check_int_eq( 0, val );
|
|
check( (buf + 3) == end );
|
|
|
|
/* no leading zeroes allowed */
|
|
val = 0;
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i04e" );
|
|
err = tr_bencParseInt( buf, buf + 4, &end, &val );
|
|
check_int_eq( EILSEQ, err );
|
|
check_int_eq( 0, val );
|
|
check( NULL == end );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testStr( void )
|
|
{
|
|
uint8_t buf[128];
|
|
int err;
|
|
const uint8_t * end;
|
|
const uint8_t * str;
|
|
size_t len;
|
|
|
|
/* good string */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "4:boat" );
|
|
err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
|
|
check_int_eq (0, err);
|
|
check_int_eq (4, len);
|
|
check( !strncmp( (char*)str, "boat", len ) );
|
|
check( end == buf + 6 );
|
|
str = NULL;
|
|
end = NULL;
|
|
len = 0;
|
|
|
|
/* string goes past end of buffer */
|
|
err = tr_bencParseStr( buf, buf + 5, &end, &str, &len );
|
|
check_int_eq (EILSEQ, err);
|
|
check_int_eq (0, len);
|
|
check( str == NULL );
|
|
check( end == NULL );
|
|
check( !len );
|
|
|
|
/* empty string */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "0:" );
|
|
err = tr_bencParseStr( buf, buf + 2, &end, &str, &len );
|
|
check_int_eq (0, err);
|
|
check_int_eq (0, len);
|
|
check( !*str );
|
|
check( end == buf + 2 );
|
|
str = NULL;
|
|
end = NULL;
|
|
len = 0;
|
|
|
|
/* short string */
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "3:boat" );
|
|
err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
|
|
check_int_eq (0, err);
|
|
check_int_eq (3, len);
|
|
check( !strncmp( (char*)str, "boa", len ) );
|
|
check( end == buf + 5 );
|
|
str = NULL;
|
|
end = NULL;
|
|
len = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testString( const char * str,
|
|
int isGood )
|
|
{
|
|
tr_benc val;
|
|
const uint8_t * end = NULL;
|
|
char * saved;
|
|
const size_t len = strlen( str );
|
|
int savedLen;
|
|
int err = tr_bencParse( str, str + len, &val, &end );
|
|
|
|
if( !isGood )
|
|
{
|
|
check( err );
|
|
}
|
|
else
|
|
{
|
|
check( !err );
|
|
#if 0
|
|
fprintf( stderr, "in: [%s]\n", str );
|
|
fprintf( stderr, "out:\n%s", tr_bencToStr( &val, TR_FMT_JSON, NULL ) );
|
|
#endif
|
|
check( end == (const uint8_t*)str + len );
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &savedLen );
|
|
check_streq (str, saved);
|
|
check_int_eq (savedLen, len);
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testParse( void )
|
|
{
|
|
tr_benc val;
|
|
tr_benc * child;
|
|
tr_benc * child2;
|
|
uint8_t buf[512];
|
|
const uint8_t * end;
|
|
int err;
|
|
int len;
|
|
int64_t i;
|
|
char * saved;
|
|
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
|
|
err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
|
|
check( !err );
|
|
check( tr_bencGetInt( &val, &i ) );
|
|
check_int_eq (64, i);
|
|
check( end == buf + 4 );
|
|
tr_bencFree( &val );
|
|
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "li64ei32ei16ee" );
|
|
err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
|
|
check( !err );
|
|
check( end == buf + strlen( (char*)buf ) );
|
|
check( val.val.l.count == 3 );
|
|
check( tr_bencGetInt( &val.val.l.vals[0], &i ) );
|
|
check_int_eq (64, i);
|
|
check( tr_bencGetInt( &val.val.l.vals[1], &i ) );
|
|
check_int_eq (32, i);
|
|
check( tr_bencGetInt( &val.val.l.vals[2], &i ) );
|
|
check_int_eq (16, i);
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
|
|
check_streq ((char*)buf, saved);
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "lllee" );
|
|
err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
|
|
check( err );
|
|
check( end == NULL );
|
|
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "le" );
|
|
err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
|
|
check( !err );
|
|
check( end == buf + 2 );
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
|
|
check_streq( "le", saved );
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
|
|
if( ( err = testString( "llleee", true ) ) )
|
|
return err;
|
|
if( ( err = testString( "d3:cow3:moo4:spam4:eggse", true ) ) )
|
|
return err;
|
|
if( ( err = testString( "d4:spaml1:a1:bee", true ) ) )
|
|
return err;
|
|
if( ( err =
|
|
testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee",
|
|
true ) ) )
|
|
return err;
|
|
if( ( err =
|
|
testString(
|
|
"d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee",
|
|
true ) ) )
|
|
return err;
|
|
if( ( err =
|
|
testString(
|
|
"d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e",
|
|
true ) ) )
|
|
return err;
|
|
if( ( err = testString( "d1:ai0e1:be", false ) ) ) /* odd number of children
|
|
*/
|
|
return err;
|
|
if( ( err = testString( "", false ) ) )
|
|
return err;
|
|
if( ( err = testString( " ", false ) ) )
|
|
return err;
|
|
|
|
/* nested containers
|
|
* parse an unsorted dict
|
|
* save as a sorted dict */
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "lld1:bi32e1:ai64eeee" );
|
|
err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
|
|
check( !err );
|
|
check( end == buf + strlen( (const char*)buf ) );
|
|
check( ( child = tr_bencListChild( &val, 0 ) ) );
|
|
check( ( child2 = tr_bencListChild( child, 0 ) ) );
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
|
|
check_streq( "lld1:ai64e1:bi32eeee", saved );
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
|
|
/* too many endings */
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "leee" );
|
|
err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
|
|
check( !err );
|
|
check( end == buf + 2 );
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
|
|
check_streq( "le", saved );
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
|
|
/* no ending */
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "l1:a1:b1:c" );
|
|
err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
|
|
check( err );
|
|
|
|
/* incomplete string */
|
|
end = NULL;
|
|
tr_snprintf( (char*)buf, sizeof( buf ), "1:" );
|
|
err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
|
|
check( err );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
stripWhitespace( char * in )
|
|
{
|
|
char * out;
|
|
|
|
for( out = in; *in; ++in )
|
|
if( !isspace( *in ) )
|
|
*out++ = *in;
|
|
*out = '\0';
|
|
}
|
|
|
|
static int
|
|
testJSONSnippet( const char * benc_str,
|
|
const char * expected )
|
|
{
|
|
tr_benc top;
|
|
char * serialized;
|
|
struct evbuffer * buf;
|
|
|
|
tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL );
|
|
buf = tr_bencToBuf( &top, TR_FMT_JSON );
|
|
serialized = (char*) evbuffer_pullup( buf, -1 );
|
|
stripWhitespace( serialized );
|
|
#if 0
|
|
fprintf( stderr, "benc: %s\n", benc_str );
|
|
fprintf( stderr, "json: %s\n", serialized );
|
|
fprintf( stderr, "want: %s\n", expected );
|
|
#endif
|
|
check_streq (expected, serialized);
|
|
tr_bencFree( &top );
|
|
evbuffer_free( buf );
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testJSON( void )
|
|
{
|
|
int val;
|
|
const char * benc_str;
|
|
const char * expected;
|
|
|
|
benc_str = "i6e";
|
|
expected = "6";
|
|
if( ( val = testJSONSnippet( benc_str, expected ) ) )
|
|
return val;
|
|
|
|
benc_str = "d5:helloi1e5:worldi2ee";
|
|
expected = "{\"hello\":1,\"world\":2}";
|
|
if( ( val = testJSONSnippet( benc_str, expected ) ) )
|
|
return val;
|
|
|
|
benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3eee";
|
|
expected = "{\"foo\":[1,2,3],\"hello\":1,\"world\":2}";
|
|
if( ( val = testJSONSnippet( benc_str, expected ) ) )
|
|
return val;
|
|
|
|
benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eeee";
|
|
expected = "{\"foo\":[1,2,3,{\"a\":0}],\"hello\":1,\"world\":2}";
|
|
if( ( val = testJSONSnippet( benc_str, expected ) ) )
|
|
return val;
|
|
|
|
benc_str = "d4:argsd6:statusle7:status2lee6:result7:successe";
|
|
expected =
|
|
"{\"args\":{\"status\":[],\"status2\":[]},\"result\":\"success\"}";
|
|
if( ( val = testJSONSnippet( benc_str, expected ) ) )
|
|
return val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testMerge( void )
|
|
{
|
|
tr_benc dest, src;
|
|
int64_t i;
|
|
const char * s;
|
|
|
|
/* initial dictionary (default values) */
|
|
tr_bencInitDict( &dest, 10 );
|
|
tr_bencDictAddInt( &dest, "i1", 1 );
|
|
tr_bencDictAddInt( &dest, "i2", 2 );
|
|
tr_bencDictAddInt( &dest, "i4", -35 ); /* remains untouched */
|
|
tr_bencDictAddStr( &dest, "s5", "abc" );
|
|
tr_bencDictAddStr( &dest, "s6", "def" );
|
|
tr_bencDictAddStr( &dest, "s7", "127.0.0.1" ); /* remains untouched */
|
|
|
|
/* new dictionary, will overwrite items in dest */
|
|
tr_bencInitDict( &src, 10 );
|
|
tr_bencDictAddInt( &src, "i1", 1 ); /* same value */
|
|
tr_bencDictAddInt( &src, "i2", 4 ); /* new value */
|
|
tr_bencDictAddInt( &src, "i3", 3 ); /* new key:value */
|
|
tr_bencDictAddStr( &src, "s5", "abc" ); /* same value */
|
|
tr_bencDictAddStr( &src, "s6", "xyz" ); /* new value */
|
|
tr_bencDictAddStr( &src, "s8", "ghi" ); /* new key:value */
|
|
|
|
tr_bencMergeDicts( &dest, /*const*/ &src );
|
|
|
|
check( tr_bencDictFindInt( &dest, "i1", &i ));
|
|
check_int_eq (1, i);
|
|
check( tr_bencDictFindInt( &dest, "i2", &i ));
|
|
check_int_eq (4, i);
|
|
check( tr_bencDictFindInt( &dest, "i3", &i ));
|
|
check_int_eq (3, i);
|
|
check( tr_bencDictFindInt( &dest, "i4", &i ));
|
|
check_int_eq (-35, i);
|
|
check( tr_bencDictFindStr( &dest, "s5", &s ));
|
|
check_streq ("abc", s);
|
|
check( tr_bencDictFindStr( &dest, "s6", &s ));
|
|
check_streq ("xyz", s);
|
|
check( tr_bencDictFindStr( &dest, "s7", &s ));
|
|
check_streq ("127.0.0.1", s);
|
|
check( tr_bencDictFindStr( &dest, "s8", &s ));
|
|
check_streq ("ghi", s);
|
|
|
|
tr_bencFree( &dest );
|
|
tr_bencFree( &src );
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testStackSmash( void )
|
|
{
|
|
int i;
|
|
int len;
|
|
int err;
|
|
uint8_t * in;
|
|
const uint8_t * end;
|
|
tr_benc val;
|
|
char * saved;
|
|
const int depth = STACK_SMASH_DEPTH;
|
|
|
|
in = tr_new( uint8_t, depth * 2 + 1 );
|
|
for( i = 0; i < depth; ++i )
|
|
{
|
|
in[i] = 'l';
|
|
in[depth + i] = 'e';
|
|
}
|
|
in[depth * 2] = '\0';
|
|
err = tr_bencParse( in, in + ( depth * 2 ), &val, &end );
|
|
check( !err );
|
|
check( end == in + ( depth * 2 ) );
|
|
saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
|
|
check_streq ((char*)in, saved);
|
|
tr_free( in );
|
|
tr_free( saved );
|
|
tr_bencFree( &val );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testBool( void )
|
|
{
|
|
tr_benc top;
|
|
int64_t intVal;
|
|
bool boolVal;
|
|
|
|
tr_bencInitDict( &top, 0 );
|
|
|
|
tr_bencDictAddBool( &top, "key1", false );
|
|
tr_bencDictAddBool( &top, "key2", 0 );
|
|
tr_bencDictAddInt ( &top, "key3", true );
|
|
tr_bencDictAddInt ( &top, "key4", 1 );
|
|
check( tr_bencDictFindBool( &top, "key1", &boolVal ) );
|
|
check( !boolVal );
|
|
check( tr_bencDictFindBool( &top, "key2", &boolVal ) );
|
|
check( !boolVal );
|
|
check( tr_bencDictFindBool( &top, "key3", &boolVal ) );
|
|
check( boolVal );
|
|
check( tr_bencDictFindBool( &top, "key4", &boolVal ) );
|
|
check( boolVal );
|
|
check( tr_bencDictFindInt( &top, "key1", &intVal ) );
|
|
check( !intVal);
|
|
check( tr_bencDictFindInt( &top, "key2", &intVal ) );
|
|
check( !intVal );
|
|
check( tr_bencDictFindInt( &top, "key3", &intVal ) );
|
|
check( intVal );
|
|
check( tr_bencDictFindInt( &top, "key4", &intVal ) );
|
|
check( intVal );
|
|
|
|
tr_bencFree( &top );
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testParse2( void )
|
|
{
|
|
tr_benc top;
|
|
tr_benc top2;
|
|
int64_t intVal;
|
|
const char * strVal;
|
|
double realVal;
|
|
bool boolVal;
|
|
int len;
|
|
char * benc;
|
|
const uint8_t * end;
|
|
|
|
tr_bencInitDict( &top, 0 );
|
|
tr_bencDictAddBool( &top, "this-is-a-bool", true );
|
|
tr_bencDictAddInt( &top, "this-is-an-int", 1234 );
|
|
tr_bencDictAddReal( &top, "this-is-a-real", 0.5 );
|
|
tr_bencDictAddStr( &top, "this-is-a-string", "this-is-a-string" );
|
|
|
|
benc = tr_bencToStr( &top, TR_FMT_BENC, &len );
|
|
check( !strcmp( benc, "d14:this-is-a-booli1e14:this-is-a-real8:0.50000016:this-is-a-string16:this-is-a-string14:this-is-an-inti1234ee" ) );
|
|
check( !tr_bencParse( benc, benc+len, &top2, &end ) );
|
|
check( (char*)end == benc + len );
|
|
check( tr_bencIsDict( &top2 ) );
|
|
check( tr_bencDictFindInt( &top, "this-is-an-int", &intVal ) );
|
|
check_int_eq (1234, intVal);
|
|
check( tr_bencDictFindBool( &top, "this-is-a-bool", &boolVal ) );
|
|
check( boolVal == true );
|
|
check( tr_bencDictFindStr( &top, "this-is-a-string", &strVal ) );
|
|
check_streq ("this-is-a-string", strVal);
|
|
check( tr_bencDictFindReal( &top, "this-is-a-real", &realVal ) );
|
|
check_int_eq (50, (int)(realVal*100));
|
|
|
|
tr_bencFree( &top2 );
|
|
tr_free( benc );
|
|
tr_bencFree( &top );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main( void )
|
|
{
|
|
static const testFunc tests[] = {
|
|
testInt, testStr, testParse, testJSON, testMerge, testBool,
|
|
testParse2, testStackSmash,
|
|
};
|
|
|
|
return runTests(tests, NUM_TESTS(tests));
|
|
}
|