From 834a3f2151dd8738a1f878489f6207664c4af5aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 14 Jul 2013 17:14:53 -0700 Subject: Imported Upstream version 1.1.1 --- src/bencode.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 src/bencode.c (limited to 'src/bencode.c') diff --git a/src/bencode.c b/src/bencode.c new file mode 100644 index 0000000..89f35ed --- /dev/null +++ b/src/bencode.c @@ -0,0 +1,302 @@ +/* + * 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 gint64 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: + * http://en.wikipedia.org/wiki/Public_Domain + * + * Written by: + * Mike Frysinger + * And improvements from: + * Gilles Chanteperdrix + */ + +/* + * 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 /* malloc() realloc() free() strtoll() */ +#include /* memset() */ +#include + +#include + +#include "bencode.h" + +static be_node *be_alloc(be_type type) +{ + be_node *ret = g_malloc0(sizeof(be_node)); + if (ret) + ret->type = type; + return ret; +} + +static gint64 _be_decode_int(const char **data, gint64 * data_len) +{ + char *endp; + gint64 ret = strtoll(*data, &endp, 10); + *data_len -= (endp - *data); + *data = endp; + return ret; +} + +gint64 be_str_len(be_node * node) +{ + gint64 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, gint64 * data_len) +{ + gint64 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) + return ret; + + /* reject attempts to allocate large values that overflow the + * size_t type which is used with malloc() + */ + if (sizeof(gint64) != sizeof(long)) + if (sllen != slen) + return ret; + + /* make sure we have enough data left */ + if (sllen > *data_len - 1) + return ret; + + /* switch from signed to unsigned so we don't overflow below */ + len = slen; + + if (**data == ':') { + char *_ret = g_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; + } + return ret; +} + +static be_node *_be_decode(const char **data, gint64 * data_len) +{ + be_node *ret = NULL; + char dc; + + if (!*data_len) + return ret; + + dc = **data; + if (dc == 'l') { + unsigned int i = 0; + + ret = be_alloc(BE_LIST); + + --(*data_len); + ++(*data); + while (**data != 'e') { + ret->val.l = + g_realloc(ret->val.l, (i + 2) * sizeof(*ret->val.l)); + ret->val.l[i] = _be_decode(data, data_len); + if (!ret->val.l[i]) + break; + ++i; + } + --(*data_len); + ++(*data); + + if (i > 0) + ret->val.l[i] = NULL; + + return ret; + } else if (dc == 'd') { + unsigned int i = 0; + + ret = be_alloc(BE_DICT); + + --(*data_len); + ++(*data); + while (**data != 'e') { + ret->val.d = + g_realloc(ret->val.d, (i + 2) * sizeof(*ret->val.d)); + ret->val.d[i].key = _be_decode_str(data, data_len); + ret->val.d[i].val = _be_decode(data, data_len); + if (!ret->val.l[i]) + break; + ++i; + } + --(*data_len); + ++(*data); + + if (i > 0) + ret->val.d[i].val = NULL; + + return ret; + } else if (dc == 'i') { + ret = be_alloc(BE_INT); + + --(*data_len); + ++(*data); + ret->val.i = _be_decode_int(data, data_len); + if (**data != 'e') + return NULL; + --(*data_len); + ++(*data); + + return ret; + } else if (isdigit(dc)) { + ret = be_alloc(BE_STR); + + ret->val.s = _be_decode_str(data, data_len); + return ret; + } + + return ret; +} + +be_node *be_decoden(const char *data, gint64 len) +{ + return _be_decode(&data, &len); +} + +be_node *be_decode(const char *data) +{ + return be_decoden(data, strlen(data)); +} + +gboolean be_validate_node(be_node * node, gint type) +{ + if (!node || node->type != type) + return FALSE; + else + return TRUE; +} + +static inline void _be_free_str(char *str) +{ + if (str) + g_free(str - sizeof(gint64)); +} + +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; + if (node->val.l) { + for (i = 0; node->val.l[i]; ++i) + be_free(node->val.l[i]); + g_free(node->val.l); + } + break; + } + + case BE_DICT: + { + unsigned int i; + if (node->val.d) { + for (i = 0; node->val.d[i].val; ++i) { + _be_free_str(node->val.d[i].key); + be_free(node->val.d[i].val); + } + g_free(node->val.d); + } + break; + } + } + g_free(node); +} + +be_node *be_dict_find(be_node * node, char *key, int type) +{ + int i; + for (i = 0; node->val.d[i].val; ++i) { + if (!strcmp(node->val.d[i].key, key)) { + be_node *cn = node->val.d[i].val; + if (type < 0 || cn->type == type) + return node->val.d[i].val; + } + } + return NULL; +} + +#ifdef BE_DEBUG +#include +#include + +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: + 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); +} +#endif -- cgit v1.2.3