mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-01 10:56:15 -05:00
cc5fa5e5dc
* Advertises client as DBxxxx where xxxx is provided by using app. * Added COMPILE switches to enable ANONYMOUS version or remove BD portion. * Fixed version code. * Added slow increase in retry timeout, to reduce DHT traffic over time. now RETRY PERIOD = query age / 2 * initialised bdToken bdVersion etc length. * new bdboot.txt git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@3615 b45a01b8-16f6-495d-af2f-9b41ad6348cc
603 lines
12 KiB
C
603 lines
12 KiB
C
/*
|
|
* C implementation of a bencode decoder.
|
|
* This is the format defined by BitTorrent:
|
|
* http://wiki.theory.org/BitTorrentSpecification#bencoding
|
|
*
|
|
* The only external requirements are a few [standard] function calls and
|
|
* the long long type. Any sane system should provide all of these things.
|
|
*
|
|
* See the bencode.h header file for usage information.
|
|
*
|
|
* This is released into the public domain.
|
|
* Written by Mike Frysinger <vapier@gmail.com>.
|
|
*/
|
|
|
|
/*
|
|
* This implementation isn't optimized at all as I wrote it to support
|
|
* a bogus system. I have no real interest in this format. Feel free
|
|
* to send me patches (so long as you don't copyright them and you release
|
|
* your changes into the public domain as well).
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* malloc() realloc() free() strtoll() */
|
|
#include <string.h> /* memset() */
|
|
|
|
#include "bitdht/bencode.h"
|
|
|
|
/***
|
|
* #define BE_DEBUG_DECODE 1
|
|
* #define BE_DEBUG 1 // controlled from Makefile too.
|
|
***/
|
|
|
|
#ifdef BE_DEBUG_DECODE
|
|
#include <stdio.h> /* debug */
|
|
#endif
|
|
|
|
static be_node *be_alloc(be_type type)
|
|
{
|
|
be_node *ret = (be_node *) malloc(sizeof(*ret));
|
|
if (ret) {
|
|
memset(ret, 0x00, sizeof(*ret));
|
|
ret->type = type;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static long long _be_decode_int(const char **data, long long *data_len)
|
|
{
|
|
char *endp;
|
|
long long ret = strtoll(*data, &endp, 10);
|
|
*data_len -= (endp - *data);
|
|
*data = endp;
|
|
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_int(pnt: %p, rem: %lld) = %lld\n", *data, *data_len, ret);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
long long be_str_len(be_node *node)
|
|
{
|
|
long long ret = 0;
|
|
if (node->val.s)
|
|
memcpy(&ret, node->val.s - sizeof(ret), sizeof(ret));
|
|
return ret;
|
|
}
|
|
|
|
static char *_be_decode_str(const char **data, long long *data_len)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str(pnt: %p, rem: %lld)\n", *data, *data_len);
|
|
#endif
|
|
long long sllen = _be_decode_int(data, data_len);
|
|
long slen = sllen;
|
|
unsigned long len;
|
|
char *ret = NULL;
|
|
|
|
/* slen is signed, so negative values get rejected */
|
|
if (sllen < 0)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str() reject bad length\n");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* reject attempts to allocate large values that overflow the
|
|
* size_t type which is used with malloc()
|
|
*/
|
|
if (sizeof(long long) != sizeof(long))
|
|
if (sllen != slen)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str() reject large_values\n");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* make sure we have enough data left */
|
|
if (sllen > *data_len - 1)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str() reject large_values\n");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* switch from signed to unsigned so we don't overflow below */
|
|
len = slen;
|
|
|
|
if (**data == ':') {
|
|
char *_ret = (char *) malloc(sizeof(sllen) + len + 1);
|
|
memcpy(_ret, &sllen, sizeof(sllen));
|
|
ret = _ret + sizeof(sllen);
|
|
memcpy(ret, *data + 1, len);
|
|
ret[len] = '\0';
|
|
*data += len + 1;
|
|
*data_len -= len + 1;
|
|
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str() read %ld bytes\n", len+1);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode_str() reject missing :\n");
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static be_node *_be_decode(const char **data, long long *data_len)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode(pnt: %p, rem: %lld)\n", *data, *data_len);
|
|
#endif
|
|
be_node *ret = NULL;
|
|
|
|
if (!*data_len)
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() reject invalid datalen\n");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
switch (**data) {
|
|
/* lists */
|
|
case 'l': {
|
|
unsigned int i = 0;
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() found list\n");
|
|
#endif
|
|
|
|
ret = be_alloc(BE_LIST);
|
|
|
|
--(*data_len);
|
|
++(*data);
|
|
while (**data != 'e') {
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() list get item (%d)\n", i);
|
|
#endif
|
|
ret->val.l = (be_node **) realloc(ret->val.l, (i + 2) * sizeof(*ret->val.l));
|
|
ret->val.l[i] = _be_decode(data, data_len);
|
|
if (ret->val.l[i] == NULL)
|
|
{
|
|
/* failed decode - kill decode */
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() failed list decode - kill\n");
|
|
#endif
|
|
be_free(ret);
|
|
return NULL;
|
|
}
|
|
++i;
|
|
}
|
|
--(*data_len);
|
|
++(*data);
|
|
|
|
/* empty list case. */
|
|
if (i == 0)
|
|
{
|
|
ret->val.l = (be_node **) realloc(ret->val.l, 1 * sizeof(*ret->val.l));
|
|
}
|
|
|
|
ret->val.l[i] = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* dictionaries */
|
|
case 'd': {
|
|
unsigned int i = 0;
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() found dictionary\n");
|
|
#endif
|
|
|
|
ret = be_alloc(BE_DICT);
|
|
|
|
--(*data_len);
|
|
++(*data);
|
|
while (**data != 'e') {
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() dictionary get key (%d)\n", i);
|
|
#endif
|
|
ret->val.d = (be_dict *) realloc(ret->val.d, (i + 2) * sizeof(*ret->val.d));
|
|
ret->val.d[i].key = _be_decode_str(data, data_len);
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() dictionary get val\n");
|
|
#endif
|
|
ret->val.d[i].val = _be_decode(data, data_len);
|
|
|
|
if ((ret->val.d[i].key == NULL) || (ret->val.d[i].val == NULL))
|
|
{
|
|
/* failed decode - kill decode */
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() failed dict decode - kill\n");
|
|
#endif
|
|
be_free(ret);
|
|
return NULL;
|
|
}
|
|
++i;
|
|
}
|
|
--(*data_len);
|
|
++(*data);
|
|
|
|
/* empty dictionary case. */
|
|
if (i == 0)
|
|
{
|
|
ret->val.d = (be_dict *) realloc(ret->val.d, 1 * sizeof(*ret->val.d));
|
|
}
|
|
|
|
|
|
ret->val.d[i].val = NULL;
|
|
return ret;
|
|
}
|
|
|
|
/* integers */
|
|
case 'i': {
|
|
ret = be_alloc(BE_INT);
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() found int\n");
|
|
#endif
|
|
|
|
--(*data_len);
|
|
++(*data);
|
|
ret->val.i = _be_decode_int(data, data_len);
|
|
if (**data != 'e')
|
|
{
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() reject data != e - kill\n");
|
|
#endif
|
|
be_free(ret);
|
|
return NULL;
|
|
}
|
|
--(*data_len);
|
|
++(*data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* byte strings */
|
|
case '0'...'9': {
|
|
ret = be_alloc(BE_STR);
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() found string\n");
|
|
#endif
|
|
|
|
ret->val.s = _be_decode_str(data, data_len);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* invalid */
|
|
default:
|
|
#ifdef BE_DEBUG_DECODE
|
|
fprintf(stderr, "bencode::_be_decode() found invalid - kill\n");
|
|
#endif
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
be_node *be_decoden(const char *data, long long len)
|
|
{
|
|
return _be_decode(&data, &len);
|
|
}
|
|
|
|
be_node *be_decode(const char *data)
|
|
{
|
|
return be_decoden(data, strlen(data));
|
|
}
|
|
|
|
static inline void _be_free_str(char *str)
|
|
{
|
|
if (str)
|
|
free(str - sizeof(long long));
|
|
}
|
|
void be_free(be_node *node)
|
|
{
|
|
switch (node->type) {
|
|
case BE_STR:
|
|
_be_free_str(node->val.s);
|
|
break;
|
|
|
|
case BE_INT:
|
|
break;
|
|
|
|
case BE_LIST: {
|
|
unsigned int i;
|
|
for (i = 0; node->val.l[i]; ++i)
|
|
be_free(node->val.l[i]);
|
|
free(node->val.l);
|
|
break;
|
|
}
|
|
|
|
case BE_DICT: {
|
|
unsigned int i;
|
|
for (i = 0; node->val.d[i].val; ++i) {
|
|
_be_free_str(node->val.d[i].key);
|
|
be_free(node->val.d[i].val);
|
|
}
|
|
free(node->val.d);
|
|
break;
|
|
}
|
|
}
|
|
free(node);
|
|
}
|
|
|
|
#ifdef BE_DEBUG
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
static void _be_dump_indent(ssize_t indent)
|
|
{
|
|
while (indent-- > 0)
|
|
printf(" ");
|
|
}
|
|
static void _be_dump(be_node *node, ssize_t indent)
|
|
{
|
|
size_t i;
|
|
|
|
_be_dump_indent(indent);
|
|
indent = abs(indent);
|
|
|
|
switch (node->type) {
|
|
case BE_STR:
|
|
be_dump_str(node);
|
|
//printf("str = %s (len = %lli)\n", node->val.s, be_str_len(node));
|
|
break;
|
|
|
|
case BE_INT:
|
|
printf("int = %lli\n", node->val.i);
|
|
break;
|
|
|
|
case BE_LIST:
|
|
puts("list [");
|
|
|
|
for (i = 0; node->val.l[i]; ++i)
|
|
_be_dump(node->val.l[i], indent + 1);
|
|
|
|
_be_dump_indent(indent);
|
|
puts("]");
|
|
break;
|
|
|
|
case BE_DICT:
|
|
puts("dict {");
|
|
|
|
for (i = 0; node->val.d[i].val; ++i) {
|
|
_be_dump_indent(indent + 1);
|
|
printf("%s => ", node->val.d[i].key);
|
|
_be_dump(node->val.d[i].val, -(indent + 1));
|
|
}
|
|
|
|
_be_dump_indent(indent);
|
|
puts("}");
|
|
break;
|
|
}
|
|
}
|
|
void be_dump(be_node *node)
|
|
{
|
|
_be_dump(node, 0);
|
|
}
|
|
|
|
void be_dump_str(be_node *node)
|
|
{
|
|
if (node->type != BE_STR)
|
|
{
|
|
printf("be_dump_str(): error not a string\n");
|
|
return;
|
|
}
|
|
|
|
int len = be_str_len(node);
|
|
int i = 0;
|
|
printf("str[%d] = ", len);
|
|
for(i = 0; i < len; i++)
|
|
{
|
|
/* sensible chars */
|
|
if ((node->val.s[i] > 31) && (node->val.s[i] < 127))
|
|
{
|
|
printf("%c", node->val.s[i]);
|
|
}
|
|
else
|
|
{
|
|
printf("[%d]", node->val.s[i]);
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
/******************** New Functions added by drBob *************
|
|
* Output bencode
|
|
*
|
|
*/
|
|
|
|
int be_encode(be_node *node, char *str, int len)
|
|
{
|
|
size_t i;
|
|
int loc = 0;
|
|
|
|
switch (node->type) {
|
|
case BE_STR:
|
|
snprintf(str, len, "%lli:", be_str_len(node));
|
|
loc += strlen(&(str[loc]));
|
|
|
|
memcpy(&(str[loc]), node->val.s, be_str_len(node));
|
|
loc += be_str_len(node);
|
|
break;
|
|
|
|
case BE_INT:
|
|
snprintf(str, len, "i%llie", node->val.i);
|
|
loc += strlen(&(str[loc]));
|
|
break;
|
|
|
|
case BE_LIST:
|
|
|
|
snprintf(str, len, "l");
|
|
loc += 1;
|
|
|
|
for (i = 0; node->val.l[i]; ++i)
|
|
{
|
|
loc += be_encode(node->val.l[i], &(str[loc]), len-loc);
|
|
}
|
|
|
|
snprintf(&(str[loc]), len - loc, "e");
|
|
loc += 1;
|
|
|
|
break;
|
|
|
|
case BE_DICT:
|
|
snprintf(str, len, "d");
|
|
loc += 1;
|
|
|
|
for (i = 0; node->val.d[i].val; ++i) {
|
|
|
|
/* assumption that key must be ascii! */
|
|
snprintf(&(str[loc]), len-loc, "%i:%s",
|
|
(int) strlen(node->val.d[i].key),
|
|
node->val.d[i].key);
|
|
loc += strlen(&(str[loc]));
|
|
loc += be_encode(node->val.d[i].val, &(str[loc]), len-loc);
|
|
}
|
|
|
|
snprintf(&(str[loc]), len - loc, "e");
|
|
loc += 1;
|
|
|
|
break;
|
|
}
|
|
return loc;
|
|
}
|
|
|
|
/* hackish way to create nodes! */
|
|
be_node *be_create_dict()
|
|
{
|
|
be_node *n = be_decode("de");
|
|
return n;
|
|
}
|
|
|
|
|
|
be_node *be_create_list()
|
|
{
|
|
be_node *n = be_decode("le");
|
|
return n;
|
|
}
|
|
|
|
be_node *be_create_str(const char *str)
|
|
{
|
|
|
|
/* must */
|
|
be_node *n = be_alloc(BE_STR);
|
|
int len = strlen(str);
|
|
long long int sllen = len;
|
|
char *_ret = (char *) malloc(sizeof(sllen) + len + 1);
|
|
char *ret = NULL;
|
|
|
|
memcpy(_ret, &sllen, sizeof(sllen));
|
|
ret = _ret + sizeof(sllen);
|
|
memcpy(ret, str, len);
|
|
ret[len] = '\0';
|
|
|
|
n->val.s = ret;
|
|
|
|
return n;
|
|
}
|
|
|
|
be_node *be_create_str_wlen(const char *str, int len) /* not including \0 */
|
|
{
|
|
|
|
/* must */
|
|
be_node *n = be_alloc(BE_STR);
|
|
long long int sllen = len;
|
|
char *_ret = (char *) malloc(sizeof(sllen) + len + 1);
|
|
char *ret = NULL;
|
|
|
|
memcpy(_ret, &sllen, sizeof(sllen));
|
|
ret = _ret + sizeof(sllen);
|
|
memcpy(ret, str, len);
|
|
ret[len] = '\0';
|
|
|
|
n->val.s = ret;
|
|
|
|
return n;
|
|
}
|
|
|
|
be_node *be_create_int(long long int num)
|
|
{
|
|
/* must */
|
|
be_node *n = be_alloc(BE_INT);
|
|
n->val.i = num;
|
|
return n;
|
|
}
|
|
|
|
int be_add_keypair(be_node *dict, const char *str, be_node *node)
|
|
{
|
|
int i = 0;
|
|
|
|
/* only if dict type */
|
|
if (dict->type != BE_DICT)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// get to end of dict.
|
|
for(i = 0; dict->val.d[i].val; i++);
|
|
|
|
//fprintf(stderr, "be_add_keypair() i = %d\n",i);
|
|
|
|
/* realloc space */
|
|
dict->val.d = (be_dict *) realloc(dict->val.d, (i + 2) * sizeof(*dict->val.d));
|
|
|
|
/* stupid key storage system */
|
|
int len = strlen(str);
|
|
long long int sllen = len;
|
|
char *_ret = (char *) malloc(sizeof(sllen) + len + 1);
|
|
char *ret = NULL;
|
|
|
|
//fprintf(stderr, "be_add_keypair() key len = %d\n",len);
|
|
|
|
memcpy(_ret, &sllen, sizeof(sllen));
|
|
ret = _ret + sizeof(sllen);
|
|
memcpy(ret, str, len);
|
|
ret[len] = '\0';
|
|
|
|
dict->val.d[i].key = ret;
|
|
dict->val.d[i].val = node;
|
|
i++;
|
|
dict->val.d[i].val = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int be_add_list(be_node *list, be_node *node)
|
|
{
|
|
int i = 0;
|
|
|
|
/* only if dict type */
|
|
if (list->type != BE_LIST)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// get to end of dict.
|
|
for(i = 0; list->val.l[i]; i++);
|
|
|
|
/* realloc space */
|
|
list->val.l = (be_node **) realloc(list->val.l, (i + 2) * sizeof(*list->val.l));
|
|
list->val.l[i] = node;
|
|
++i;
|
|
list->val.l[i] = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|