1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-04 10:38:13 +00:00

Add iterator function for bencoded lists.

This commit is contained in:
Josh Elsasser 2007-03-30 00:12:39 +00:00
parent 0c1c57a62a
commit 978b5cec11
5 changed files with 239 additions and 138 deletions

View file

@ -24,33 +24,33 @@
#include "transmission.h"
#define LIST_SIZE 20
/* setting to 1 to help expose bugs with tr_bencListAdd and tr_bencDictAdd */
#define LIST_SIZE 20 /* number of items to increment list/dict buffer by */
static int makeroom( benc_val_t * val, int count )
{
int len;
int len;
void * new;
assert( TYPE_LIST == val->type || TYPE_DICT == val->type );
len = val->val.l.alloc;
while( val->val.l.count + count >= len )
if( val->val.l.count + count <= val->val.l.alloc )
{
len += LIST_SIZE;
return 1;
}
if( len > val->val.l.alloc )
/* We need a bigger boat */
len = val->val.l.alloc + count + ( count % LIST_SIZE ? 0 : LIST_SIZE );
new = realloc( val->val.l.vals, len * sizeof( benc_val_t ) );
if( NULL == new )
{
/* We need a bigger boat */
new = realloc( val->val.l.vals, len * sizeof( benc_val_t ) );
if( NULL == new )
{
return 1;
}
val->val.l.alloc = len;
val->val.l.vals = new;
return 1;
}
val->val.l.alloc = len;
val->val.l.vals = new;
return 0;
}
@ -69,10 +69,10 @@ int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
end = &foo;
}
val->begin = buf;
if( buf[0] == 'i' )
{
int64_t num;
e = memchr( &buf[1], 'e', len - 1 );
if( NULL == e )
{
@ -80,16 +80,16 @@ int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
}
/* Integer: i1242e */
val->type = TYPE_INT;
*e = '\0';
val->val.i = strtoll( &buf[1], &p, 10 );
*e = 'e';
*e = '\0';
num = strtoll( &buf[1], &p, 10 );
*e = 'e';
if( p != e )
{
return 1;
}
tr_bencInitInt( val, num );
val->end = p + 1;
}
else if( buf[0] == 'l' || buf[0] == 'd' )
@ -102,13 +102,10 @@ int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
char is_dict;
char str_expected;
is_dict = ( buf[0] == 'd' );
val->type = is_dict ? TYPE_DICT : TYPE_LIST;
val->val.l.alloc = LIST_SIZE;
val->val.l.count = 0;
val->val.l.vals = malloc( LIST_SIZE * sizeof( benc_val_t ) );
cur = &buf[1];
str_expected = 1;
is_dict = ( buf[0] == 'd' );
cur = &buf[1];
str_expected = 1;
tr_bencInit( val, ( is_dict ? TYPE_DICT : TYPE_LIST ) );
while( cur - buf < len && cur[0] != 'e' )
{
if( makeroom( val, 1 ) ||
@ -140,6 +137,9 @@ int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
}
else
{
int slen;
char * sbuf;
e = memchr( buf, ':', len );
if( NULL == e )
{
@ -147,24 +147,29 @@ int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
}
/* String: 12:whateverword */
val->type = TYPE_STR;
e[0] = '\0';
val->val.s.i = strtol( buf, &p, 10 );
e[0] = ':';
e[0] = '\0';
slen = strtol( buf, &p, 10 );
e[0] = ':';
if( p != e || 0 > val->val.s.i ||
val->val.s.i > len - ((p + 1) - buf) )
if( p != e || 0 > slen || len - ( ( p + 1 ) - buf ) < slen )
{
return 1;
}
val->val.s.s = malloc( val->val.s.i + 1 );
val->val.s.s[val->val.s.i] = 0;
memcpy( val->val.s.s, p + 1, val->val.s.i );
sbuf = malloc( slen + 1 );
if( NULL == sbuf )
{
return 1;
}
memcpy( sbuf, p + 1, slen );
sbuf[slen] = '\0';
tr_bencInitStr( val, sbuf, slen, 0 );
val->end = p + 1 + val->val.s.i;
}
val->begin = buf;
*end = val->end;
return 0;
@ -238,15 +243,23 @@ void tr_bencFree( benc_val_t * val )
benc_val_t * tr_bencDictFind( benc_val_t * val, const char * key )
{
int i;
int len, i;
if( val->type != TYPE_DICT )
{
return NULL;
}
len = strlen( key );
for( i = 0; i < val->val.l.count; i += 2 )
{
if( !strcmp( val->val.l.vals[i].val.s.s, key ) )
if( TYPE_STR != val->val.l.vals[i].type ||
len != val->val.l.vals[i].val.s.i )
{
continue;
}
if( 0 == memcmp(val->val.l.vals[i].val.s.s, key, len ) )
{
return &val->val.l.vals[i+1];
}
@ -275,6 +288,30 @@ benc_val_t * tr_bencDictFindFirst( benc_val_t * val, ... )
return ret;
}
benc_val_t * tr_bencListIter( benc_val_t * list, int * pos )
{
assert( TYPE_LIST == list->type );
if( NULL == list->val.l.vals )
{
return NULL;
}
if( 0 > *pos )
{
*pos = 0;
}
if( list->val.l.count <= *pos )
{
return NULL;
}
(*pos)++;
return &list->val.l.vals[ (*pos) - 1 ];
}
char * tr_bencStealStr( benc_val_t * val )
{
assert( TYPE_STR == val->type );

View file

@ -60,6 +60,8 @@ void tr_bencFree( benc_val_t * val );
benc_val_t * tr_bencDictFind( benc_val_t * val, const char * key );
benc_val_t * tr_bencDictFindFirst( benc_val_t * val, ... );
benc_val_t * tr_bencListIter( benc_val_t * list, int * pos );
/* marks a string as 'do not free' and returns it */
char * tr_bencStealStr( benc_val_t * val );

View file

@ -36,9 +36,11 @@ static char * readtorrent( const char * path, const char * hash,
static int savetorrent( const char * hash, const char * tag,
const uint8_t * buf, size_t buflen );
static int getfile( char * buf, int size,
const char * prefix, const benc_val_t * name );
const char * prefix, benc_val_t * name );
static int getannounce( tr_info_t * inf, benc_val_t * meta );
static char * announceToScrape( const char * announce );
static int parseFiles( tr_info_t * inf, benc_val_t * name,
benc_val_t * files, benc_val_t * length );
static void strcatUTF8( char *, int, const char *, int );
/***********************************************************************
@ -50,7 +52,7 @@ int tr_metainfoParse( tr_info_t * inf, const char * tag, const char * path,
const char * savedHash, int saveCopy )
{
char * buf;
benc_val_t meta, * beInfo, * list, * val, * val2;
benc_val_t meta, * beInfo, * val, * val2;
int i;
size_t len;
@ -173,60 +175,13 @@ int tr_metainfoParse( tr_info_t * inf, const char * tag, const char * path,
/* get file or top directory name */
val = tr_bencDictFindFirst( beInfo, "name.utf-8", "name", NULL );
if( NULL == val || TYPE_STR != val->type )
if( parseFiles( inf, tr_bencDictFindFirst( beInfo,
"name.utf-8", "name", NULL ),
tr_bencDictFind( beInfo, "files" ),
tr_bencDictFind( beInfo, "length" ) ) )
{
tr_err( "%s \"name\" string", ( val ? "Invalid" : "Missing" ) );
goto fail;
}
strcatUTF8( inf->name, sizeof( inf->name ), val->val.s.s, 1 );
if( '\0' == inf->name[0] )
{
tr_err( "Invalid \"name\" string" );
goto fail;
}
inf->totalSize = 0;
if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
{
/* Multi-file mode */
inf->multifile = 1;
inf->fileCount = list->val.l.count;
inf->files = calloc( inf->fileCount, sizeof( tr_file_t ) );
for( i = 0; i < list->val.l.count; i++ )
{
val = tr_bencDictFindFirst( &list->val.l.vals[i],
"path.utf-8", "path", NULL );
if( getfile( inf->files[i].name, sizeof( inf->files[i].name ),
inf->name, val ) )
{
tr_err( "%s \"path\" entry", ( val ? "Invalid" : "Missing" ) );
goto fail;
}
val = tr_bencDictFind( &list->val.l.vals[i], "length" );
inf->files[i].length = val->val.i;
inf->totalSize += val->val.i;
}
}
else
{
/* Single-file mode */
inf->multifile = 0;
inf->fileCount = 1;
inf->files = calloc( sizeof( tr_file_t ), 1 );
strcatUTF8( inf->files[0].name, sizeof( inf->files[0].name),
val->val.s.s, 1 );
val = tr_bencDictFind( beInfo, "length" );
if( NULL == val || TYPE_INT != val->type )
{
tr_err( "%s \"length\" entry", ( val ? "Invalid" : "Missing" ) );
goto fail;
}
inf->files[0].length = val->val.i;
inf->totalSize += val->val.i;
}
if( (uint64_t) inf->pieceCount !=
( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
@ -271,11 +226,11 @@ void tr_metainfoFree( tr_info_t * inf )
}
static int getfile( char * buf, int size,
const char * prefix, const benc_val_t * name )
const char * prefix, benc_val_t * name )
{
const benc_val_t * dir;
const char ** list;
int ii, jj;
benc_val_t * dir;
const char ** list;
int ii, jj;
if( TYPE_LIST != name->type )
{
@ -288,13 +243,13 @@ static int getfile( char * buf, int size,
return 1;
}
for( ii = jj = 0; name->val.l.count > ii; ii++ )
ii = jj = 0;
while( NULL != ( dir = tr_bencListIter( name, &ii ) ) )
{
if( TYPE_STR != name->val.l.vals[ii].type )
if( TYPE_STR != dir->type )
{
continue;
}
dir = &name->val.l.vals[ii];
if( 0 == strcmp( "..", dir->val.s.s ) )
{
if( 0 < jj )
@ -327,11 +282,10 @@ static int getfile( char * buf, int size,
static int getannounce( tr_info_t * inf, benc_val_t * meta )
{
benc_val_t * val, * subval, * urlval;
char * address, * announce;
int ii, jj, port, random;
benc_val_t * val, * subval, * urlval;
char * address, * announce;
int ii, jj, port, random, subcount;
tr_tracker_info_t * sublist;
int subcount;
void * swapping;
/* Announce-list */
@ -343,9 +297,9 @@ static int getannounce( tr_info_t * inf, benc_val_t * meta )
val->val.l.count );
/* iterate through the announce-list's tiers */
for( ii = 0; ii < val->val.l.count; ii++ )
ii = 0;
while( NULL != ( subval = tr_bencListIter( val, &ii ) ) )
{
subval = &val->val.l.vals[ii];
if( TYPE_LIST != subval->type || 0 >= subval->val.l.count )
{
continue;
@ -354,9 +308,9 @@ static int getannounce( tr_info_t * inf, benc_val_t * meta )
sublist = calloc( sizeof( sublist[0] ), subval->val.l.count );
/* iterate through the tier's items */
for( jj = 0; jj < subval->val.l.count; jj++ )
jj = 0;
while( NULL != ( urlval = tr_bencListIter( subval, &jj ) ) )
{
urlval = &subval->val.l.vals[jj];
if( TYPE_STR != urlval->type ||
tr_httpParseUrl( urlval->val.s.s, urlval->val.s.i,
&address, &port, &announce ) )
@ -377,7 +331,7 @@ static int getannounce( tr_info_t * inf, benc_val_t * meta )
subcount++;
}
/* just use sublist as is if it's full */
/* just use sublist as-is if it's full */
if( subcount == subval->val.l.count )
{
inf->trackerList[inf->trackerTiers].list = sublist;
@ -621,6 +575,90 @@ int savetorrent( const char * hash, const char * tag,
return 0;
}
int
parseFiles( tr_info_t * inf, benc_val_t * name,
benc_val_t * files, benc_val_t * length )
{
benc_val_t * item, * path;
int ii;
if( NULL == name || TYPE_STR != name->type )
{
tr_err( "%s \"name\" string", ( name ? "Invalid" : "Missing" ) );
return 1;
}
strcatUTF8( inf->name, sizeof( inf->name ), name->val.s.s, 1 );
if( '\0' == inf->name[0] )
{
tr_err( "Invalid \"name\" string" );
return 1;
}
inf->totalSize = 0;
if( files && TYPE_LIST == files->type )
{
/* Multi-file mode */
inf->multifile = 1;
inf->fileCount = files->val.l.count;
inf->files = calloc( inf->fileCount, sizeof( inf->files[0] ) );
if( NULL == inf->files )
{
return 1;
}
item = NULL;
ii = 0;
while( NULL != ( item = tr_bencListIter( files, &ii ) ) )
{
path = tr_bencDictFindFirst( item, "path.utf-8", "path", NULL );
if( getfile( inf->files[ii-1].name, sizeof( inf->files[ii-1].name ),
inf->name, path ) )
{
tr_err( "%s \"path\" entry",
( path ? "Invalid" : "Missing" ) );
return 1;
}
length = tr_bencDictFind( item, "length" );
if( NULL == length || TYPE_INT != length->type )
{
tr_err( "%s \"length\" entry",
( length ? "Invalid" : "Missing" ) );
return 1;
}
inf->files[ii-1].length = length->val.i;
inf->totalSize += length->val.i;
}
}
else if( NULL != length && TYPE_INT == length->type )
{
/* Single-file mode */
inf->multifile = 0;
inf->fileCount = 1;
inf->files = calloc( 1, sizeof( inf->files[0] ) );
if( NULL == inf->files )
{
return 1;
}
strcatUTF8( inf->files[0].name, sizeof( inf->files[0].name ),
name->val.s.s, 1 );
inf->files[0].length = length->val.i;
inf->totalSize += length->val.i;
}
else
{
tr_err( "%s \"files\" entry and %s \"length\" entry",
( files ? "Invalid" : "Missing" ),
( length ? "invalid" : "missing" ) );
}
return 0;
}
/***********************************************************************
* strcatUTF8
***********************************************************************

View file

@ -382,9 +382,9 @@ parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len )
/* fill bitmask with supported message info */
msgs = tr_bitfieldNew( azmsgCount() );
for( ii = 0; ii < sub->val.l.count; ii++ )
ii = -1;
while( NULL != ( dict = tr_bencListIter( sub, &ii ) ) )
{
dict = &sub->val.l.vals[ii];
if( TYPE_DICT != dict->type )
{
continue;
@ -475,10 +475,9 @@ parseAZPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len )
return TR_OK;
}
used = 0;
for( ii = 0; ii < list->val.l.count; ii++ )
ii = used = 0;
while( NULL != ( pair = tr_bencListIter( list, &ii ) ) )
{
pair = &list->val.l.vals[ii];
if( TYPE_STR == pair->type && 6 == pair->val.s.i )
{
used += tr_torrentAddCompact( tor, TR_PEER_FROM_PEX,

View file

@ -94,6 +94,7 @@ static void readAnswer ( tr_tracker_t * tc, const char *, int,
static void readScrapeAnswer ( tr_tracker_t * tc, const char *, int );
static void killHttp ( tr_http_t ** http );
static int shouldChangePort( tr_tracker_t * tc );
static uint8_t * parseOriginalPeers( benc_val_t * bePeers, int * peerCount );
tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
{
@ -793,38 +794,15 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len,
goto cleanup;
}
if( bePeers->type & TYPE_LIST )
if( TYPE_LIST == bePeers->type )
{
/* Original protocol */
if( bePeers->val.l.count > 0 )
{
struct in_addr addr;
in_port_t port;
peerCount = 0;
peerCompact = malloc( 6 * bePeers->val.l.count );
/* Convert to compact form */
for( i = 0; i < bePeers->val.l.count; i++ )
{
if( !( beFoo = tr_bencDictFind(
&bePeers->val.l.vals[i], "ip" ) ) )
continue;
if( tr_netResolve( beFoo->val.s.s, &addr ) )
continue;
memcpy( &peerCompact[6 * peerCount], &addr, 4 );
if( !( beFoo = tr_bencDictFind(
&bePeers->val.l.vals[i], "port" ) ) )
continue;
port = htons( beFoo->val.i );
memcpy( &peerCompact[6 * peerCount + 4], &port, 2 );
peerCount++;
}
peerCompact = parseOriginalPeers( bePeers, &peerCount );
}
}
else if( bePeers->type & TYPE_STR )
else if( TYPE_STR == bePeers->type )
{
/* "Compact" extension */
if( bePeers->val.s.i >= 6 )
@ -1101,3 +1079,50 @@ static int shouldChangePort( tr_tracker_t * tc )
return ( tor->publicPort != tc->publicPort );
}
/* Convert to compact form */
static uint8_t *
parseOriginalPeers( benc_val_t * bePeers, int * peerCount )
{
struct in_addr addr;
in_port_t port;
uint8_t * peerCompact;
benc_val_t * peer, * addrval, * portval;
int ii, count;
assert( TYPE_LIST == bePeers->type );
count = 0;
peerCompact = malloc( 6 * bePeers->val.l.count );
if( NULL == peerCompact )
{
return NULL;
}
ii = -1;
while( NULL != ( peer = tr_bencListIter( bePeers, &ii ) ) )
{
addrval = tr_bencDictFind( peer, "ip" );
if( NULL == addrval || TYPE_STR != addrval->type ||
tr_netResolve( addrval->val.s.s, &addr ) )
{
continue;
}
memcpy( &peerCompact[6 * count], &addr, 4 );
portval = tr_bencDictFind( peer, "port" );
if( NULL == portval || TYPE_INT != portval->type ||
0 > portval->val.i || 0xffff < portval->val.i )
{
continue;
}
port = htons( portval->val.i );
memcpy( &peerCompact[6 * count + 4], &port, 2 );
count++;
}
*peerCount = count;
return peerCompact;
}