1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-26 09:37:56 +00:00
transmission/libtransmission/bencode.c
Jordan Lee 879a2afcbd Update the copyright year in the source code comments.
The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.
2011-01-19 13:48:47 +00:00

1773 lines
44 KiB
C

/*
* This file Copyright (C) Mnemosyne LLC
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#include <assert.h>
#include <ctype.h> /* isdigit() */
#include <errno.h>
#include <math.h> /* fabs() */
#include <stdio.h>
#include <stdlib.h> /* realpath() */
#include <string.h>
#ifdef WIN32 /* tr_mkstemp() */
#include <fcntl.h>
#define _S_IREAD 256
#define _S_IWRITE 128
#endif
#include <sys/types.h> /* stat() */
#include <sys/stat.h> /* stat() */
#include <locale.h>
#include <unistd.h> /* stat() */
#include <event2/buffer.h>
#include "ConvertUTF.h"
#include "transmission.h"
#include "bencode.h"
#include "fdlimit.h" /* tr_close_file() */
#include "json.h"
#include "list.h"
#include "platform.h" /* TR_PATH_MAX */
#include "ptrarray.h"
#include "utils.h" /* tr_new(), tr_free() */
#ifndef ENODATA
#define ENODATA EIO
#endif
/**
***
**/
static tr_bool
isContainer( const tr_benc * val )
{
return tr_bencIsList( val ) || tr_bencIsDict( val );
}
static tr_bool
isSomething( const tr_benc * val )
{
return isContainer( val ) || tr_bencIsInt( val )
|| tr_bencIsString( val )
|| tr_bencIsReal( val )
|| tr_bencIsBool( val );
}
static void
tr_bencInit( tr_benc * val, char type )
{
memset( val, 0, sizeof( *val ) );
val->type = type;
}
/***
**** tr_bencParse()
**** tr_bencLoad()
***/
/**
* The initial i and trailing e are beginning and ending delimiters.
* You can have negative numbers such as i-3e. You cannot prefix the
* number with a zero such as i04e. However, i0e is valid.
* Example: i3e represents the integer "3"
* NOTE: The maximum number of bit of this integer is unspecified,
* but to handle it as a signed 64bit integer is mandatory to handle
* "large files" aka .torrent for more that 4Gbyte
*/
int
tr_bencParseInt( const uint8_t * buf,
const uint8_t * bufend,
const uint8_t ** setme_end,
int64_t * setme_val )
{
char * endptr;
const void * begin;
const void * end;
int64_t val;
if( buf >= bufend )
return EILSEQ;
if( *buf != 'i' )
return EILSEQ;
begin = buf + 1;
end = memchr( begin, 'e', ( bufend - buf ) - 1 );
if( end == NULL )
return EILSEQ;
errno = 0;
val = evutil_strtoll( begin, &endptr, 10 );
if( errno || ( endptr != end ) ) /* incomplete parse */
return EILSEQ;
if( val && *(const char*)begin == '0' ) /* no leading zeroes! */
return EILSEQ;
*setme_end = (const uint8_t*)end + 1;
*setme_val = val;
return 0;
}
/**
* Byte strings are encoded as follows:
* <string length encoded in base ten ASCII>:<string data>
* Note that there is no constant beginning delimiter, and no ending delimiter.
* Example: 4:spam represents the string "spam"
*/
int
tr_bencParseStr( const uint8_t * buf,
const uint8_t * bufend,
const uint8_t ** setme_end,
const uint8_t ** setme_str,
size_t * setme_strlen )
{
size_t len;
const void * end;
char * endptr;
if( buf >= bufend )
return EILSEQ;
if( !isdigit( *buf ) )
return EILSEQ;
end = memchr( buf, ':', bufend - buf );
if( end == NULL )
return EILSEQ;
errno = 0;
len = strtoul( (const char*)buf, &endptr, 10 );
if( errno || endptr != end )
return EILSEQ;
if( (const uint8_t*)end + 1 + len > bufend )
return EILSEQ;
*setme_end = (const uint8_t*)end + 1 + len;
*setme_str = (const uint8_t*)end + 1;
*setme_strlen = len;
return 0;
}
/* set to 1 to help expose bugs with tr_bencListAdd and tr_bencDictAdd */
#define LIST_SIZE 4 /* number of items to increment list/dict buffer by */
static int
makeroom( tr_benc * val,
size_t count )
{
assert( TR_TYPE_LIST == val->type || TR_TYPE_DICT == val->type );
if( val->val.l.count + count > val->val.l.alloc )
{
/* We need a bigger boat */
const int len = val->val.l.alloc + count +
( count % LIST_SIZE ? LIST_SIZE -
( count % LIST_SIZE ) : 0 );
void * tmp = realloc( val->val.l.vals, len * sizeof( tr_benc ) );
if( !tmp )
return 1;
val->val.l.alloc = len;
val->val.l.vals = tmp;
}
return 0;
}
static tr_benc*
getNode( tr_benc * top,
tr_ptrArray * parentStack,
int type )
{
tr_benc * parent;
assert( top );
assert( parentStack );
if( tr_ptrArrayEmpty( parentStack ) )
return top;
parent = tr_ptrArrayBack( parentStack );
assert( parent );
/* dictionary keys must be strings */
if( ( parent->type == TR_TYPE_DICT )
&& ( type != TR_TYPE_STR )
&& ( !( parent->val.l.count % 2 ) ) )
return NULL;
makeroom( parent, 1 );
return parent->val.l.vals + parent->val.l.count++;
}
/**
* This function's previous recursive implementation was
* easier to read, but was vulnerable to a smash-stacking
* attack via maliciously-crafted bencoded data. (#667)
*/
static int
tr_bencParseImpl( const void * buf_in,
const void * bufend_in,
tr_benc * top,
tr_ptrArray * parentStack,
const uint8_t ** setme_end )
{
int err;
const uint8_t * buf = buf_in;
const uint8_t * bufend = bufend_in;
tr_bencInit( top, 0 );
while( buf != bufend )
{
if( buf > bufend ) /* no more text to parse... */
return 1;
if( *buf == 'i' ) /* int */
{
int64_t val;
const uint8_t * end;
tr_benc * node;
if( ( err = tr_bencParseInt( buf, bufend, &end, &val ) ) )
return err;
node = getNode( top, parentStack, TR_TYPE_INT );
if( !node )
return EILSEQ;
tr_bencInitInt( node, val );
buf = end;
if( tr_ptrArrayEmpty( parentStack ) )
break;
}
else if( *buf == 'l' ) /* list */
{
tr_benc * node = getNode( top, parentStack, TR_TYPE_LIST );
if( !node )
return EILSEQ;
tr_bencInit( node, TR_TYPE_LIST );
tr_ptrArrayAppend( parentStack, node );
++buf;
}
else if( *buf == 'd' ) /* dict */
{
tr_benc * node = getNode( top, parentStack, TR_TYPE_DICT );
if( !node )
return EILSEQ;
tr_bencInit( node, TR_TYPE_DICT );
tr_ptrArrayAppend( parentStack, node );
++buf;
}
else if( *buf == 'e' ) /* end of list or dict */
{
tr_benc * node;
++buf;
if( tr_ptrArrayEmpty( parentStack ) )
return EILSEQ;
node = tr_ptrArrayBack( parentStack );
if( tr_bencIsDict( node ) && ( node->val.l.count % 2 ) )
{
/* odd # of children in dict */
tr_bencFree( &node->val.l.vals[--node->val.l.count] );
return EILSEQ;
}
tr_ptrArrayPop( parentStack );
if( tr_ptrArrayEmpty( parentStack ) )
break;
}
else if( isdigit( *buf ) ) /* string? */
{
const uint8_t * end;
const uint8_t * str;
size_t str_len;
tr_benc * node;
if( ( err = tr_bencParseStr( buf, bufend, &end, &str, &str_len ) ) )
return err;
node = getNode( top, parentStack, TR_TYPE_STR );
if( !node )
return EILSEQ;
tr_bencInitStr( node, str, str_len );
buf = end;
if( tr_ptrArrayEmpty( parentStack ) )
break;
}
else /* invalid bencoded text... march past it */
{
++buf;
}
}
err = !isSomething( top ) || !tr_ptrArrayEmpty( parentStack );
if( !err && setme_end )
*setme_end = buf;
return err;
}
int
tr_bencParse( const void * buf,
const void * end,
tr_benc * top,
const uint8_t ** setme_end )
{
int err;
tr_ptrArray parentStack = TR_PTR_ARRAY_INIT;
top->type = 0; /* set to `uninitialized' */
err = tr_bencParseImpl( buf, end, top, &parentStack, setme_end );
if( err )
tr_bencFree( top );
tr_ptrArrayDestruct( &parentStack, NULL );
return err;
}
int
tr_bencLoad( const void * buf_in,
size_t buflen,
tr_benc * setme_benc,
char ** setme_end )
{
const uint8_t * buf = buf_in;
const uint8_t * end;
const int ret = tr_bencParse( buf, buf + buflen, setme_benc, &end );
if( !ret && setme_end )
*setme_end = (char*) end;
return ret;
}
/***
****
***/
/* returns true if the benc's string was malloced.
* this occurs when the string is too long for our string buffer */
static inline int
stringIsAlloced( const tr_benc * val )
{
return val->val.s.len >= sizeof( val->val.s.str.buf );
}
/* returns a const pointer to the benc's string */
static inline const char*
getStr( const tr_benc* val )
{
return stringIsAlloced(val) ? val->val.s.str.ptr : val->val.s.str.buf;
}
static int
dictIndexOf( const tr_benc * val, const char * key )
{
if( tr_bencIsDict( val ) )
{
size_t i;
const size_t len = strlen( key );
for( i = 0; ( i + 1 ) < val->val.l.count; i += 2 )
{
const tr_benc * child = val->val.l.vals + i;
if( ( child->type == TR_TYPE_STR )
&& ( child->val.s.len == len )
&& !memcmp( getStr(child), key, len ) )
return i;
}
}
return -1;
}
tr_benc *
tr_bencDictFind( tr_benc * val, const char * key )
{
const int i = dictIndexOf( val, key );
return i < 0 ? NULL : &val->val.l.vals[i + 1];
}
static tr_bool
tr_bencDictFindType( tr_benc * dict, const char * key, int type, tr_benc ** setme )
{
return tr_bencIsType( *setme = tr_bencDictFind( dict, key ), type );
}
size_t
tr_bencListSize( const tr_benc * list )
{
return tr_bencIsList( list ) ? list->val.l.count : 0;
}
tr_benc*
tr_bencListChild( tr_benc * val,
size_t i )
{
tr_benc * ret = NULL;
if( tr_bencIsList( val ) && ( i < val->val.l.count ) )
ret = val->val.l.vals + i;
return ret;
}
int
tr_bencListRemove( tr_benc * list, size_t i )
{
if( tr_bencIsList( list ) && ( i < list->val.l.count ) )
{
tr_bencFree( &list->val.l.vals[i] );
tr_removeElementFromArray( list->val.l.vals, i, sizeof( tr_benc ), list->val.l.count-- );
return 1;
}
return 0;
}
static void
tr_benc_warning( const char * err )
{
fprintf( stderr, "warning: %s\n", err );
}
tr_bool
tr_bencGetInt( const tr_benc * val,
int64_t * setme )
{
tr_bool success = FALSE;
if( !success && (( success = tr_bencIsInt( val ))))
if( setme )
*setme = val->val.i;
if( !success && (( success = tr_bencIsBool( val )))) {
tr_benc_warning( "reading bool as an int" );
if( setme )
*setme = val->val.b ? 1 : 0;
}
return success;
}
tr_bool
tr_bencGetStr( const tr_benc * val, const char ** setme )
{
const tr_bool success = tr_bencIsString( val );
if( success )
*setme = getStr( val );
return success;
}
tr_bool
tr_bencGetBool( const tr_benc * val, tr_bool * setme )
{
const char * str;
tr_bool success = FALSE;
if(( success = tr_bencIsBool( val )))
*setme = val->val.b;
if( !success && tr_bencIsInt( val ) )
if(( success = ( val->val.i==0 || val->val.i==1 ) ))
*setme = val->val.i!=0;
if( !success && tr_bencGetStr( val, &str ) )
if(( success = ( !strcmp(str,"true") || !strcmp(str,"false"))))
*setme = !strcmp(str,"true");
return success;
}
tr_bool
tr_bencGetReal( const tr_benc * val, double * setme )
{
tr_bool success = FALSE;
if( !success && (( success = tr_bencIsReal( val ))))
*setme = val->val.d;
if( !success && (( success = tr_bencIsInt( val ))))
*setme = val->val.i;
if( !success && tr_bencIsString(val) )
{
char * endptr;
char locale[128];
double d;
/* the json spec requires a '.' decimal point regardless of locale */
tr_strlcpy( locale, setlocale( LC_NUMERIC, NULL ), sizeof( locale ) );
setlocale( LC_NUMERIC, "POSIX" );
d = strtod( getStr(val), &endptr );
setlocale( LC_NUMERIC, locale );
if(( success = ( getStr(val) != endptr ) && !*endptr ))
*setme = d;
}
return success;
}
tr_bool
tr_bencDictFindInt( tr_benc * dict, const char * key, int64_t * setme )
{
return tr_bencGetInt( tr_bencDictFind( dict, key ), setme );
}
tr_bool
tr_bencDictFindBool( tr_benc * dict, const char * key, tr_bool * setme )
{
return tr_bencGetBool( tr_bencDictFind( dict, key ), setme );
}
tr_bool
tr_bencDictFindReal( tr_benc * dict, const char * key, double * setme )
{
return tr_bencGetReal( tr_bencDictFind( dict, key ), setme );
}
tr_bool
tr_bencDictFindStr( tr_benc * dict, const char * key, const char ** setme )
{
return tr_bencGetStr( tr_bencDictFind( dict, key ), setme );
}
tr_bool
tr_bencDictFindList( tr_benc * dict, const char * key, tr_benc ** setme )
{
return tr_bencDictFindType( dict, key, TR_TYPE_LIST, setme );
}
tr_bool
tr_bencDictFindDict( tr_benc * dict, const char * key, tr_benc ** setme )
{
return tr_bencDictFindType( dict, key, TR_TYPE_DICT, setme );
}
tr_bool
tr_bencDictFindRaw( tr_benc * dict,
const char * key,
const uint8_t ** setme_raw,
size_t * setme_len )
{
tr_benc * child;
const tr_bool found = tr_bencDictFindType( dict, key, TR_TYPE_STR, &child );
if( found ) {
*setme_raw = (uint8_t*) getStr(child);
*setme_len = child->val.s.len;
}
return found;
}
/***
****
***/
void
tr_bencInitRaw( tr_benc * val, const void * src, size_t byteCount )
{
char * setme;
tr_bencInit( val, TR_TYPE_STR );
/* There's no way in benc notation to distinguish between
* zero-terminated strings and raw byte arrays.
* Because of this, tr_bencMergeDicts() and tr_bencListCopy()
* don't know whether or not a TR_TYPE_STR node needs a '\0'.
* Append one, een to the raw arrays, just to be safe. */
if( byteCount < sizeof( val->val.s.str.buf ) )
setme = val->val.s.str.buf;
else
setme = val->val.s.str.ptr = tr_new( char, byteCount + 1 );
memcpy( setme, src, byteCount );
setme[byteCount] = '\0';
val->val.s.len = byteCount;
}
void
tr_bencInitStr( tr_benc * val, const void * str, int len )
{
if( str == NULL )
len = 0;
else if( len < 0 )
len = strlen( str );
tr_bencInitRaw( val, str, len );
}
void
tr_bencInitBool( tr_benc * b, int value )
{
tr_bencInit( b, TR_TYPE_BOOL );
b->val.b = value != 0;
}
void
tr_bencInitReal( tr_benc * b, double value )
{
tr_bencInit( b, TR_TYPE_REAL );
b->val.d = value;
}
void
tr_bencInitInt( tr_benc * b, int64_t value )
{
tr_bencInit( b, TR_TYPE_INT );
b->val.i = value;
}
int
tr_bencInitList( tr_benc * b, size_t reserveCount )
{
tr_bencInit( b, TR_TYPE_LIST );
return tr_bencListReserve( b, reserveCount );
}
int
tr_bencListReserve( tr_benc * b, size_t count )
{
assert( tr_bencIsList( b ) );
return makeroom( b, count );
}
int
tr_bencInitDict( tr_benc * b, size_t reserveCount )
{
tr_bencInit( b, TR_TYPE_DICT );
return tr_bencDictReserve( b, reserveCount );
}
int
tr_bencDictReserve( tr_benc * b, size_t reserveCount )
{
assert( tr_bencIsDict( b ) );
return makeroom( b, reserveCount * 2 );
}
tr_benc *
tr_bencListAdd( tr_benc * list )
{
tr_benc * item;
assert( tr_bencIsList( list ) );
if( list->val.l.count == list->val.l.alloc )
tr_bencListReserve( list, LIST_SIZE );
assert( list->val.l.count < list->val.l.alloc );
item = &list->val.l.vals[list->val.l.count];
list->val.l.count++;
tr_bencInit( item, TR_TYPE_INT );
return item;
}
tr_benc *
tr_bencListAddInt( tr_benc * list, int64_t val )
{
tr_benc * node = tr_bencListAdd( list );
tr_bencInitInt( node, val );
return node;
}
tr_benc *
tr_bencListAddReal( tr_benc * list, double val )
{
tr_benc * node = tr_bencListAdd( list );
tr_bencInitReal( node, val );
return node;
}
tr_benc *
tr_bencListAddBool( tr_benc * list, tr_bool val )
{
tr_benc * node = tr_bencListAdd( list );
tr_bencInitBool( node, val );
return node;
}
tr_benc *
tr_bencListAddStr( tr_benc * list, const char * val )
{
tr_benc * node = tr_bencListAdd( list );
tr_bencInitStr( node, val, -1 );
return node;
}
tr_benc *
tr_bencListAddRaw( tr_benc * list, const uint8_t * val, size_t len )
{
tr_benc * node = tr_bencListAdd( list );
tr_bencInitRaw( node, val, len );
return node;
}
tr_benc*
tr_bencListAddList( tr_benc * list,
size_t reserveCount )
{
tr_benc * child = tr_bencListAdd( list );
tr_bencInitList( child, reserveCount );
return child;
}
tr_benc*
tr_bencListAddDict( tr_benc * list,
size_t reserveCount )
{
tr_benc * child = tr_bencListAdd( list );
tr_bencInitDict( child, reserveCount );
return child;
}
tr_benc *
tr_bencDictAdd( tr_benc * dict,
const char * key )
{
tr_benc * keyval, * itemval;
assert( tr_bencIsDict( dict ) );
if( dict->val.l.count + 2 > dict->val.l.alloc )
makeroom( dict, 2 );
assert( dict->val.l.count + 2 <= dict->val.l.alloc );
keyval = dict->val.l.vals + dict->val.l.count++;
tr_bencInitStr( keyval, key, -1 );
itemval = dict->val.l.vals + dict->val.l.count++;
tr_bencInit( itemval, TR_TYPE_INT );
return itemval;
}
static tr_benc*
dictFindOrAdd( tr_benc * dict, const char * key, int type )
{
tr_benc * child;
/* see if it already exists, and if so, try to reuse it */
if(( child = tr_bencDictFind( dict, key ))) {
if( !tr_bencIsType( child, type ) ) {
tr_bencDictRemove( dict, key );
child = NULL;
}
}
/* if it doesn't exist, create it */
if( child == NULL )
child = tr_bencDictAdd( dict, key );
return child;
}
tr_benc*
tr_bencDictAddInt( tr_benc * dict,
const char * key,
int64_t val )
{
tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_INT );
tr_bencInitInt( child, val );
return child;
}
tr_benc*
tr_bencDictAddBool( tr_benc * dict, const char * key, tr_bool val )
{
tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_BOOL );
tr_bencInitBool( child, val );
return child;
}
tr_benc*
tr_bencDictAddReal( tr_benc * dict, const char * key, double val )
{
tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_REAL );
tr_bencInitReal( child, val );
return child;
}
tr_benc*
tr_bencDictAddStr( tr_benc * dict, const char * key, const char * val )
{
tr_benc * child;
/* see if it already exists, and if so, try to reuse it */
if(( child = tr_bencDictFind( dict, key ))) {
if( tr_bencIsString( child ) ) {
if( stringIsAlloced( child ) )
tr_free( child->val.s.str.ptr );
} else {
tr_bencDictRemove( dict, key );
child = NULL;
}
}
/* if it doesn't exist, create it */
if( child == NULL )
child = tr_bencDictAdd( dict, key );
/* set it */
tr_bencInitStr( child, val, -1 );
return child;
}
tr_benc*
tr_bencDictAddRaw( tr_benc * dict,
const char * key,
const void * src,
size_t len )
{
tr_benc * child;
/* see if it already exists, and if so, try to reuse it */
if(( child = tr_bencDictFind( dict, key ))) {
if( tr_bencIsString( child ) ) {
if( stringIsAlloced( child ) )
tr_free( child->val.s.str.ptr );
} else {
tr_bencDictRemove( dict, key );
child = NULL;
}
}
/* if it doesn't exist, create it */
if( child == NULL )
child = tr_bencDictAdd( dict, key );
/* set it */
tr_bencInitRaw( child, src, len );
return child;
}
tr_benc*
tr_bencDictAddList( tr_benc * dict,
const char * key,
size_t reserveCount )
{
tr_benc * child = tr_bencDictAdd( dict, key );
tr_bencInitList( child, reserveCount );
return child;
}
tr_benc*
tr_bencDictAddDict( tr_benc * dict,
const char * key,
size_t reserveCount )
{
tr_benc * child = tr_bencDictAdd( dict, key );
tr_bencInitDict( child, reserveCount );
return child;
}
int
tr_bencDictRemove( tr_benc * dict,
const char * key )
{
int i = dictIndexOf( dict, key );
if( i >= 0 )
{
const int n = dict->val.l.count;
tr_bencFree( &dict->val.l.vals[i] );
tr_bencFree( &dict->val.l.vals[i + 1] );
if( i + 2 < n )
{
dict->val.l.vals[i] = dict->val.l.vals[n - 2];
dict->val.l.vals[i + 1] = dict->val.l.vals[n - 1];
}
dict->val.l.count -= 2;
}
return i >= 0; /* return true if found */
}
/***
**** BENC WALKING
***/
struct KeyIndex
{
const char * key;
int index;
};
static int
compareKeyIndex( const void * va,
const void * vb )
{
const struct KeyIndex * a = va;
const struct KeyIndex * b = vb;
return strcmp( a->key, b->key );
}
struct SaveNode
{
const tr_benc * val;
int valIsVisited;
int childCount;
int childIndex;
int * children;
};
static void
nodeInitDict( struct SaveNode * node, const tr_benc * val )
{
int i, j;
int nKeys;
struct KeyIndex * indices;
assert( tr_bencIsDict( val ) );
nKeys = val->val.l.count / 2;
node->val = val;
node->children = tr_new0( int, nKeys * 2 );
/* ugh, a dictionary's children have to be sorted by key... */
indices = tr_new( struct KeyIndex, nKeys );
for( i = j = 0; i < ( nKeys * 2 ); i += 2, ++j )
{
indices[j].key = getStr(&val->val.l.vals[i]);
indices[j].index = i;
}
qsort( indices, j, sizeof( struct KeyIndex ), compareKeyIndex );
for( i = 0; i < j; ++i )
{
const int index = indices[i].index;
node->children[node->childCount++] = index;
node->children[node->childCount++] = index + 1;
}
assert( node->childCount == nKeys * 2 );
tr_free( indices );
}
static void
nodeInitList( struct SaveNode * node, const tr_benc * val )
{
int i, n;
assert( tr_bencIsList( val ) );
n = val->val.l.count;
node->val = val;
node->childCount = n;
node->children = tr_new0( int, n );
for( i = 0; i < n; ++i ) /* a list's children don't need to be reordered */
node->children[i] = i;
}
static void
nodeInitLeaf( struct SaveNode * node, const tr_benc * val )
{
assert( !isContainer( val ) );
node->val = val;
}
static void
nodeInit( struct SaveNode * node, const tr_benc * val )
{
static const struct SaveNode INIT_NODE = { NULL, 0, 0, 0, NULL };
*node = INIT_NODE;
if( tr_bencIsList( val ) ) nodeInitList( node, val );
else if( tr_bencIsDict( val ) ) nodeInitDict( node, val );
else nodeInitLeaf( node, val );
}
typedef void ( *BencWalkFunc )( const tr_benc * val, void * user_data );
struct WalkFuncs
{
BencWalkFunc intFunc;
BencWalkFunc boolFunc;
BencWalkFunc realFunc;
BencWalkFunc stringFunc;
BencWalkFunc dictBeginFunc;
BencWalkFunc listBeginFunc;
BencWalkFunc containerEndFunc;
};
/**
* This function's previous recursive implementation was
* easier to read, but was vulnerable to a smash-stacking
* attack via maliciously-crafted bencoded data. (#667)
*/
static void
bencWalk( const tr_benc * top,
const struct WalkFuncs * walkFuncs,
void * user_data )
{
int stackSize = 0;
int stackAlloc = 64;
struct SaveNode * stack = tr_new( struct SaveNode, stackAlloc );
nodeInit( &stack[stackSize++], top );
while( stackSize > 0 )
{
struct SaveNode * node = &stack[stackSize-1];
const tr_benc * val;
if( !node->valIsVisited )
{
val = node->val;
node->valIsVisited = TRUE;
}
else if( node->childIndex < node->childCount )
{
const int index = node->children[node->childIndex++];
val = node->val->val.l.vals + index;
}
else /* done with this node */
{
if( isContainer( node->val ) )
walkFuncs->containerEndFunc( node->val, user_data );
--stackSize;
tr_free( node->children );
continue;
}
if( val ) switch( val->type )
{
case TR_TYPE_INT:
walkFuncs->intFunc( val, user_data );
break;
case TR_TYPE_BOOL:
walkFuncs->boolFunc( val, user_data );
break;
case TR_TYPE_REAL:
walkFuncs->realFunc( val, user_data );
break;
case TR_TYPE_STR:
walkFuncs->stringFunc( val, user_data );
break;
case TR_TYPE_LIST:
if( val == node->val )
walkFuncs->listBeginFunc( val, user_data );
else {
if( stackAlloc == stackSize ) {
stackAlloc *= 2;
stack = tr_renew( struct SaveNode, stack, stackAlloc );
}
nodeInit( &stack[stackSize++], val );
}
break;
case TR_TYPE_DICT:
if( val == node->val )
walkFuncs->dictBeginFunc( val, user_data );
else {
if( stackAlloc == stackSize ) {
stackAlloc *= 2;
stack = tr_renew( struct SaveNode, stack, stackAlloc );
}
nodeInit( &stack[stackSize++], val );
}
break;
default:
/* did caller give us an uninitialized val? */
tr_err( "%s", _( "Invalid metadata" ) );
break;
}
}
tr_free( stack );
}
/****
*****
****/
static void
saveIntFunc( const tr_benc * val, void * evbuf )
{
evbuffer_add_printf( evbuf, "i%" PRId64 "e", val->val.i );
}
static void
saveBoolFunc( const tr_benc * val, void * evbuf )
{
if( val->val.b )
evbuffer_add( evbuf, "i1e", 3 );
else
evbuffer_add( evbuf, "i0e", 3 );
}
static void
saveRealFunc( const tr_benc * val, void * evbuf )
{
char buf[128];
char locale[128];
size_t len;
/* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
tr_strlcpy( locale, setlocale( LC_NUMERIC, NULL ), sizeof( locale ) );
setlocale( LC_NUMERIC, "POSIX" );
tr_snprintf( buf, sizeof( buf ), "%f", val->val.d );
setlocale( LC_NUMERIC, locale );
len = strlen( buf );
evbuffer_add_printf( evbuf, "%lu:", (unsigned long)len );
evbuffer_add( evbuf, buf, len );
}
static void
saveStringFunc( const tr_benc * val, void * evbuf )
{
evbuffer_add_printf( evbuf, "%lu:", (unsigned long)val->val.s.len );
evbuffer_add( evbuf, getStr(val), val->val.s.len );
}
static void
saveDictBeginFunc( const tr_benc * val UNUSED, void * evbuf )
{
evbuffer_add( evbuf, "d", 1 );
}
static void
saveListBeginFunc( const tr_benc * val UNUSED, void * evbuf )
{
evbuffer_add( evbuf, "l", 1 );
}
static void
saveContainerEndFunc( const tr_benc * val UNUSED, void * evbuf )
{
evbuffer_add( evbuf, "e", 1 );
}
static const struct WalkFuncs saveFuncs = { saveIntFunc,
saveBoolFunc,
saveRealFunc,
saveStringFunc,
saveDictBeginFunc,
saveListBeginFunc,
saveContainerEndFunc };
/***
****
***/
static void
freeDummyFunc( const tr_benc * val UNUSED, void * buf UNUSED )
{}
static void
freeStringFunc( const tr_benc * val, void * unused UNUSED )
{
if( stringIsAlloced( val ) )
tr_free( val->val.s.str.ptr );
}
static void
freeContainerEndFunc( const tr_benc * val, void * unused UNUSED )
{
tr_free( val->val.l.vals );
}
static const struct WalkFuncs freeWalkFuncs = { freeDummyFunc,
freeDummyFunc,
freeDummyFunc,
freeStringFunc,
freeDummyFunc,
freeDummyFunc,
freeContainerEndFunc };
void
tr_bencFree( tr_benc * val )
{
if( isSomething( val ) )
bencWalk( val, &freeWalkFuncs, NULL );
}
/***
****
***/
/** @brief Implementation helper class for tr_bencToBuffer(TR_FMT_JSON) */
struct ParentState
{
int bencType;
int childIndex;
int childCount;
};
/** @brief Implementation helper class for tr_bencToBuffer(TR_FMT_JSON) */
struct jsonWalk
{
tr_bool doIndent;
tr_list * parents;
struct evbuffer * out;
};
static void
jsonIndent( struct jsonWalk * data )
{
if( data->doIndent )
{
char buf[1024];
const int width = tr_list_size( data->parents ) * 4;
buf[0] = '\n';
memset( buf+1, ' ', width );
evbuffer_add( data->out, buf, 1+width );
}
}
static void
jsonChildFunc( struct jsonWalk * data )
{
if( data->parents )
{
struct ParentState * parentState = data->parents->data;
switch( parentState->bencType )
{
case TR_TYPE_DICT:
{
const int i = parentState->childIndex++;
if( !( i % 2 ) )
evbuffer_add( data->out, ": ", data->doIndent ? 2 : 1 );
else {
const tr_bool isLast = parentState->childIndex == parentState->childCount;
if( !isLast ) {
evbuffer_add( data->out, ", ", data->doIndent ? 2 : 1 );
jsonIndent( data );
}
}
break;
}
case TR_TYPE_LIST:
{
const tr_bool isLast = ++parentState->childIndex == parentState->childCount;
if( !isLast ) {
evbuffer_add( data->out, ", ", data->doIndent ? 2 : 1 );
jsonIndent( data );
}
break;
}
default:
break;
}
}
}
static void
jsonPushParent( struct jsonWalk * data,
const tr_benc * benc )
{
struct ParentState * parentState = tr_new( struct ParentState, 1 );
parentState->bencType = benc->type;
parentState->childIndex = 0;
parentState->childCount = benc->val.l.count;
tr_list_prepend( &data->parents, parentState );
}
static void
jsonPopParent( struct jsonWalk * data )
{
tr_free( tr_list_pop_front( &data->parents ) );
}
static void
jsonIntFunc( const tr_benc * val,
void * vdata )
{
struct jsonWalk * data = vdata;
evbuffer_add_printf( data->out, "%" PRId64, val->val.i );
jsonChildFunc( data );
}
static void
jsonBoolFunc( const tr_benc * val, void * vdata )
{
struct jsonWalk * data = vdata;
if( val->val.b )
evbuffer_add( data->out, "true", 4 );
else
evbuffer_add( data->out, "false", 5 );
jsonChildFunc( data );
}
static void
jsonRealFunc( const tr_benc * val, void * vdata )
{
struct jsonWalk * data = vdata;
char locale[128];
if( fabs( val->val.d - (int)val->val.d ) < 0.00001 )
evbuffer_add_printf( data->out, "%d", (int)val->val.d );
else {
/* json requires a '.' decimal point regardless of locale */
tr_strlcpy( locale, setlocale( LC_NUMERIC, NULL ), sizeof( locale ) );
setlocale( LC_NUMERIC, "POSIX" );
evbuffer_add_printf( data->out, "%.4f", tr_truncd( val->val.d, 4 ) );
setlocale( LC_NUMERIC, locale );
}
jsonChildFunc( data );
}
static void
jsonStringFunc( const tr_benc * val, void * vdata )
{
char * out;
char * outwalk;
char * outend;
struct evbuffer_iovec vec[1];
struct jsonWalk * data = vdata;
const unsigned char * it = (const unsigned char *) getStr(val);
const unsigned char * end = it + val->val.s.len;
const int safeguard = 512; /* arbitrary margin for escapes and unicode */
evbuffer_reserve_space( data->out, val->val.s.len+safeguard, vec, 1 );
out = vec[0].iov_base;
outend = out + vec[0].iov_len;
outwalk = out;
*outwalk++ = '"';
for( ; it!=end; ++it )
{
switch( *it )
{
case '\b': *outwalk++ = '\\'; *outwalk++ = 'b'; break;
case '\f': *outwalk++ = '\\'; *outwalk++ = 'f'; break;
case '\n': *outwalk++ = '\\'; *outwalk++ = 'n'; break;
case '\r': *outwalk++ = '\\'; *outwalk++ = 'r'; break;
case '\t': *outwalk++ = '\\'; *outwalk++ = 't'; break;
case '"' : *outwalk++ = '\\'; *outwalk++ = '"'; break;
case '\\': *outwalk++ = '\\'; *outwalk++ = '\\'; break;
default:
if( isascii( *it ) )
*outwalk++ = *it;
else {
const UTF8 * tmp = it;
UTF32 buf = 0;
UTF32 * u32 = &buf;
ConversionResult result = ConvertUTF8toUTF32( &tmp, end, &u32, &buf + 1, 0 );
if((( result==conversionOK ) || (result==targetExhausted)) && (tmp!=it)) {
outwalk += tr_snprintf( outwalk, outend-outwalk, "\\u%04x", (unsigned int)buf );
it = tmp - 1;
}
}
}
}
*outwalk++ = '"';
vec[0].iov_len = outwalk - out;
evbuffer_commit_space( data->out, vec, 1 );
jsonChildFunc( data );
}
static void
jsonDictBeginFunc( const tr_benc * val,
void * vdata )
{
struct jsonWalk * data = vdata;
jsonPushParent( data, val );
evbuffer_add( data->out, "{", 1 );
if( val->val.l.count )
jsonIndent( data );
}
static void
jsonListBeginFunc( const tr_benc * val,
void * vdata )
{
const size_t nChildren = tr_bencListSize( val );
struct jsonWalk * data = vdata;
jsonPushParent( data, val );
evbuffer_add( data->out, "[", 1 );
if( nChildren )
jsonIndent( data );
}
static void
jsonContainerEndFunc( const tr_benc * val,
void * vdata )
{
struct jsonWalk * data = vdata;
int emptyContainer = FALSE;
jsonPopParent( data );
if( !emptyContainer )
jsonIndent( data );
if( tr_bencIsDict( val ) )
evbuffer_add( data->out, "}", 1 );
else /* list */
evbuffer_add( data->out, "]", 1 );
jsonChildFunc( data );
}
static const struct WalkFuncs jsonWalkFuncs = { jsonIntFunc,
jsonBoolFunc,
jsonRealFunc,
jsonStringFunc,
jsonDictBeginFunc,
jsonListBeginFunc,
jsonContainerEndFunc };
/***
****
***/
static void
tr_bencListCopy( tr_benc * target, const tr_benc * src )
{
int i = 0;
const tr_benc * val;
while(( val = tr_bencListChild( (tr_benc*)src, i++ )))
{
if( tr_bencIsBool( val ) )
{
tr_bool boolVal = 0;
tr_bencGetBool( val, &boolVal );
tr_bencListAddBool( target, boolVal );
}
else if( tr_bencIsReal( val ) )
{
double realVal = 0;
tr_bencGetReal( val, &realVal );
tr_bencListAddReal( target, realVal );
}
else if( tr_bencIsInt( val ) )
{
int64_t intVal = 0;
tr_bencGetInt( val, &intVal );
tr_bencListAddInt( target, intVal );
}
else if( tr_bencIsString( val ) )
{
tr_bencListAddRaw( target, (const uint8_t*)getStr( val ), val->val.s.len );
}
else if( tr_bencIsDict( val ) )
{
tr_bencMergeDicts( tr_bencListAddDict( target, 0 ), val );
}
else if ( tr_bencIsList( val ) )
{
tr_bencListCopy( tr_bencListAddList( target, 0 ), val );
}
else
{
tr_err( "tr_bencListCopy skipping item" );
}
}
}
static size_t
tr_bencDictSize( const tr_benc * dict )
{
size_t count = 0;
if( tr_bencIsDict( dict ) )
count = dict->val.l.count / 2;
return count;
}
tr_bool
tr_bencDictChild( tr_benc * dict, size_t n, const char ** key, tr_benc ** val )
{
tr_bool success = 0;
assert( tr_bencIsDict( dict ) );
if( tr_bencIsDict( dict ) && (n*2)+1 <= dict->val.l.count )
{
tr_benc * k = dict->val.l.vals + (n*2);
tr_benc * v = dict->val.l.vals + (n*2) + 1;
if(( success = tr_bencGetStr( k, key ) && isSomething( v )))
*val = v;
}
return success;
}
void
tr_bencMergeDicts( tr_benc * target, const tr_benc * source )
{
size_t i;
const size_t sourceCount = tr_bencDictSize( source );
assert( tr_bencIsDict( target ) );
assert( tr_bencIsDict( source ) );
for( i=0; i<sourceCount; ++i )
{
const char * key;
tr_benc * val;
tr_benc * t;
if( tr_bencDictChild( (tr_benc*)source, i, &key, &val ) )
{
if( tr_bencIsBool( val ) )
{
tr_bool boolVal;
tr_bencGetBool( val, &boolVal );
tr_bencDictAddBool( target, key, boolVal );
}
else if( tr_bencIsReal( val ) )
{
double realVal = 0;
tr_bencGetReal( val, &realVal );
tr_bencDictAddReal( target, key, realVal );
}
else if( tr_bencIsInt( val ) )
{
int64_t intVal = 0;
tr_bencGetInt( val, &intVal );
tr_bencDictAddInt( target, key, intVal );
}
else if( tr_bencIsString( val ) )
{
tr_bencDictAddRaw( target, key, getStr( val ), val->val.s.len );
}
else if( tr_bencIsDict( val ) && tr_bencDictFindDict( target, key, &t ) )
{
tr_bencMergeDicts( t, val );
}
else if( tr_bencIsList( val ) )
{
if( tr_bencDictFind( target, key ) == NULL )
{
tr_bencListCopy( tr_bencDictAddList( target, key, tr_bencListSize( val ) ), val );
}
}
else
{
tr_dbg( "tr_bencMergeDicts skipping \"%s\"", key );
}
}
}
}
/***
****
***/
void
tr_bencToBuf( const tr_benc * top, tr_fmt_mode mode, struct evbuffer * buf )
{
evbuffer_drain( buf, evbuffer_get_length( buf ) );
evbuffer_expand( buf, 4096 ); /* alloc a little memory to start off with */
switch( mode )
{
case TR_FMT_BENC:
bencWalk( top, &saveFuncs, buf );
break;
case TR_FMT_JSON:
case TR_FMT_JSON_LEAN: {
struct jsonWalk data;
data.doIndent = mode==TR_FMT_JSON;
data.out = buf;
data.parents = NULL;
bencWalk( top, &jsonWalkFuncs, &data );
if( evbuffer_get_length( buf ) )
evbuffer_add_printf( buf, "\n" );
break;
}
}
}
char*
tr_bencToStr( const tr_benc * top, tr_fmt_mode mode, int * len )
{
char * ret;
struct evbuffer * buf = evbuffer_new( );
size_t n;
tr_bencToBuf( top, mode, buf );
n = evbuffer_get_length( buf );
ret = evbuffer_free_to_str( buf );
if( len != NULL )
*len = (int) n;
return ret;
}
/* portability wrapper for mkstemp(). */
static int
tr_mkstemp( char * template )
{
#ifdef WIN32
const int flags = O_RDWR | O_BINARY | O_CREAT | O_EXCL | _O_SHORT_LIVED;
const mode_t mode = _S_IREAD | _S_IWRITE;
mktemp( template );
return open( template, flags, mode );
#else
return mkstemp( template );
#endif
}
/* portability wrapper for fsync(). */
static void
tr_fsync( int fd )
{
#ifdef WIN32
_commit( fd );
#else
fsync( fd );
#endif
}
int
tr_bencToFile( const tr_benc * top, tr_fmt_mode mode, const char * filename )
{
char * tmp;
int fd;
int err = 0;
char buf[TR_PATH_MAX];
/* follow symlinks to find the "real" file, to make sure the temporary
* we build with tr_mkstemp() is created on the right partition */
if( tr_realpath( filename, buf ) != NULL )
filename = buf;
/* if the file already exists, try to move it out of the way & keep it as a backup */
tmp = tr_strdup_printf( "%s.tmp.XXXXXX", filename );
fd = tr_mkstemp( tmp );
tr_set_file_for_single_pass( fd );
if( fd >= 0 )
{
int nleft;
/* save the benc to a temporary file */
{
struct evbuffer * buffer = evbuffer_new( );
tr_bencToBuf( top, mode, buffer );
nleft = evbuffer_get_length( buffer );
while( nleft > 0 ) {
const int n = evbuffer_write( buffer, fd );
if( n >= 0 )
nleft -= n;
else if( errno != EAGAIN ) {
err = errno;
break;
}
}
evbuffer_free( buffer );
}
if( nleft > 0 )
{
tr_err( _( "Couldn't save temporary file \"%1$s\": %2$s" ), tmp, tr_strerror( err ) );
tr_close_file( fd );
unlink( tmp );
}
else
{
struct stat sb;
const tr_bool already_exists = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
tr_fsync( fd );
tr_close_file( fd );
if( !already_exists || !unlink( filename ) )
{
if( !rename( tmp, filename ) )
{
tr_inf( _( "Saved \"%s\"" ), filename );
}
else
{
err = errno;
tr_err( _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
unlink( tmp );
}
}
else
{
err = errno;
tr_err( _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
unlink( tmp );
}
}
}
else
{
err = errno;
tr_err( _( "Couldn't save temporary file \"%1$s\": %2$s" ), tmp, tr_strerror( err ) );
}
tr_free( tmp );
return err;
}
/***
****
***/
int
tr_bencLoadFile( tr_benc * setme, tr_fmt_mode mode, const char * filename )
{
int err;
size_t contentLen;
uint8_t * content;
content = tr_loadFile( filename, &contentLen );
if( !content && errno )
err = errno;
else if( !content )
err = ENODATA;
else {
if( mode == TR_FMT_BENC )
err = tr_bencLoad( content, contentLen, setme, NULL );
else
err = tr_jsonParse( filename, content, contentLen, setme, NULL );
}
tr_free( content );
return err;
}