mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
978 lines
21 KiB
978 lines
21 KiB
#include "bencode.h"
|
|
#include <stdio.h>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <json-glib/json-glib.h>
|
|
#include "helpers.h"
|
|
|
|
/* set to 0 for alloc debugging, e.g. through valgrind */
|
|
#define BENCODE_MIN_BUFFER_PIECE_LEN 512
|
|
|
|
#define BENCODE_HASH_BUCKETS 31 /* prime numbers work best */
|
|
|
|
#define BENCODE_ALLOC_ALIGN 8
|
|
|
|
struct __bencode_buffer_piece {
|
|
char *tail;
|
|
unsigned int left;
|
|
struct __bencode_buffer_piece *next;
|
|
char buf[0];
|
|
};
|
|
struct __bencode_free_list {
|
|
void *ptr;
|
|
free_func_t func;
|
|
struct __bencode_free_list *next;
|
|
};
|
|
struct __bencode_hash {
|
|
struct bencode_item *buckets[BENCODE_HASH_BUCKETS];
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static bencode_item_t __bencode_end_marker = {
|
|
.type = BENCODE_END_MARKER,
|
|
.iov = {
|
|
{
|
|
.iov_base = "e",
|
|
.iov_len = 1,
|
|
},
|
|
},
|
|
.iov_cnt = 1,
|
|
.str_len = 1,
|
|
};
|
|
|
|
|
|
|
|
|
|
static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end);
|
|
|
|
|
|
|
|
static void __bencode_item_init(bencode_item_t *item) {
|
|
item->last_child = item->parent = item->child = item->sibling = NULL;
|
|
}
|
|
|
|
static void __bencode_container_init(bencode_item_t *cont) {
|
|
cont->iov[0].iov_len = 1;
|
|
cont->iov[1].iov_base = "e";
|
|
cont->iov[1].iov_len = 1;
|
|
cont->iov_cnt = 2;
|
|
cont->str_len = 2;
|
|
}
|
|
|
|
static void __bencode_dictionary_init(bencode_item_t *dict) {
|
|
dict->type = BENCODE_DICTIONARY;
|
|
dict->iov[0].iov_base = "d";
|
|
dict->value = 0;
|
|
__bencode_container_init(dict);
|
|
}
|
|
|
|
static void __bencode_list_init(bencode_item_t *list) {
|
|
list->type = BENCODE_LIST;
|
|
list->iov[0].iov_base = "l";
|
|
__bencode_container_init(list);
|
|
}
|
|
|
|
static struct __bencode_buffer_piece *__bencode_piece_new(unsigned int size) {
|
|
struct __bencode_buffer_piece *ret;
|
|
|
|
if (size < BENCODE_MIN_BUFFER_PIECE_LEN)
|
|
size = BENCODE_MIN_BUFFER_PIECE_LEN;
|
|
ret = BENCODE_MALLOC(sizeof(*ret) + size + BENCODE_ALLOC_ALIGN);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
ret->tail = ret->buf;
|
|
ret->left = size;
|
|
ret->next = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bencode_buffer_init(bencode_buffer_t *buf) {
|
|
buf->pieces = __bencode_piece_new(0);
|
|
if (!buf->pieces)
|
|
return -1;
|
|
buf->free_list = NULL;
|
|
buf->error = 0;
|
|
return 0;
|
|
}
|
|
|
|
void *bencode_buffer_alloc(bencode_buffer_t *buf, unsigned int size) {
|
|
struct __bencode_buffer_piece *piece;
|
|
void *ret;
|
|
unsigned int align_size = ((size + BENCODE_ALLOC_ALIGN - 1) / BENCODE_ALLOC_ALIGN) * BENCODE_ALLOC_ALIGN;
|
|
|
|
if (!buf)
|
|
return NULL;
|
|
if (buf->error)
|
|
return NULL;
|
|
|
|
piece = buf->pieces;
|
|
|
|
if (size <= piece->left)
|
|
goto alloc;
|
|
|
|
piece = __bencode_piece_new(size);
|
|
if (!piece) {
|
|
buf->error = 1;
|
|
return NULL;
|
|
}
|
|
piece->next = buf->pieces;
|
|
buf->pieces = piece;
|
|
|
|
assert(size <= piece->left);
|
|
|
|
alloc:
|
|
if (piece->left >= align_size)
|
|
piece->left -= align_size;
|
|
else
|
|
piece->left = 0;
|
|
ret = piece->tail;
|
|
piece->tail += align_size;
|
|
return ret;
|
|
}
|
|
|
|
void bencode_buffer_free(bencode_buffer_t *buf) {
|
|
struct __bencode_free_list *fl;
|
|
struct __bencode_buffer_piece *piece, *next;
|
|
|
|
for (fl = buf->free_list; fl; fl = fl->next)
|
|
fl->func(fl->ptr);
|
|
|
|
for (piece = buf->pieces; piece; piece = next) {
|
|
next = piece->next;
|
|
BENCODE_FREE(piece);
|
|
}
|
|
}
|
|
|
|
static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) {
|
|
bencode_item_t *ret;
|
|
|
|
ret = bencode_buffer_alloc(buf, sizeof(struct bencode_item) + payload);
|
|
if (!ret)
|
|
return NULL;
|
|
ret->buffer = buf;
|
|
__bencode_item_init(ret);
|
|
return ret;
|
|
}
|
|
|
|
bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) {
|
|
bencode_item_t *ret;
|
|
|
|
ret = __bencode_item_alloc(buf, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
__bencode_dictionary_init(ret);
|
|
return ret;
|
|
}
|
|
|
|
bencode_item_t *bencode_list(bencode_buffer_t *buf) {
|
|
bencode_item_t *ret;
|
|
|
|
ret = __bencode_item_alloc(buf, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
__bencode_list_init(ret);
|
|
return ret;
|
|
}
|
|
|
|
static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *child) {
|
|
if (!parent)
|
|
return;
|
|
if (!child)
|
|
return;
|
|
|
|
assert(child->parent == NULL);
|
|
assert(child->sibling == NULL);
|
|
|
|
child->parent = parent;
|
|
if (parent->last_child)
|
|
parent->last_child->sibling = child;
|
|
parent->last_child = child;
|
|
if (!parent->child)
|
|
parent->child = child;
|
|
|
|
while (parent) {
|
|
parent->iov_cnt += child->iov_cnt;
|
|
parent->str_len += child->str_len;
|
|
parent = parent->parent;
|
|
}
|
|
}
|
|
|
|
static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void *base,
|
|
size_t str_len, size_t iov_len, unsigned int iov_cnt, bencode_type_t type)
|
|
{
|
|
bencode_item_t *ret;
|
|
int len_len;
|
|
|
|
assert(str_len <= 99999);
|
|
ret = __bencode_item_alloc(buf, 7);
|
|
if (!ret)
|
|
return NULL;
|
|
len_len = sprintf(ret->__buf, "%zu:", str_len);
|
|
|
|
ret->type = type;
|
|
ret->iov[0].iov_base = ret->__buf;
|
|
ret->iov[0].iov_len = len_len;
|
|
ret->iov[1].iov_base = (void *) base;
|
|
ret->iov[1].iov_len = iov_len;
|
|
ret->iov_cnt = iov_cnt + 1;
|
|
ret->str_len = len_len + str_len;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, size_t len) {
|
|
char *sd = bencode_buffer_alloc(buf, len);
|
|
if (!sd)
|
|
return NULL;
|
|
memcpy(sd, s, len);
|
|
return bencode_string_len(buf, sd, len);
|
|
}
|
|
|
|
bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, size_t len) {
|
|
return __bencode_string_alloc(buf, s, len, len, 1, BENCODE_STRING);
|
|
}
|
|
|
|
bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) {
|
|
bencode_item_t *ret;
|
|
int alen, rlen;
|
|
|
|
alen = 8;
|
|
while (1) {
|
|
ret = __bencode_item_alloc(buf, alen + 3);
|
|
if (!ret)
|
|
return NULL;
|
|
rlen = snprintf(ret->__buf, alen, "i%llde", i);
|
|
if (rlen < alen)
|
|
break;
|
|
alen <<= 1;
|
|
}
|
|
|
|
ret->type = BENCODE_INTEGER;
|
|
ret->iov[0].iov_base = ret->__buf;
|
|
ret->iov[0].iov_len = rlen;
|
|
ret->iov[1].iov_base = NULL;
|
|
ret->iov[1].iov_len = 0;
|
|
ret->iov_cnt = 1;
|
|
ret->str_len = rlen;
|
|
ret->value = i;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, size_t keylen, bencode_item_t *val) {
|
|
bencode_item_t *s;
|
|
|
|
if (!dict || !val)
|
|
return NULL;
|
|
assert(dict->type == BENCODE_DICTIONARY);
|
|
|
|
s = bencode_string_len(dict->buffer, key, keylen);
|
|
if (!s)
|
|
return NULL;
|
|
__bencode_container_add(dict, s);
|
|
__bencode_container_add(dict, val);
|
|
return val;
|
|
}
|
|
|
|
bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) {
|
|
if (!list || !item)
|
|
return NULL;
|
|
assert(list->type == BENCODE_LIST);
|
|
__bencode_container_add(list, item);
|
|
return item;
|
|
}
|
|
|
|
static int __bencode_iovec_cpy(struct iovec *out, const struct iovec *in, int num) {
|
|
memcpy(out, in, num * sizeof(*out));
|
|
return num;
|
|
}
|
|
|
|
static int __bencode_str_cpy(char *out, const struct iovec *in, int num) {
|
|
char *orig = out;
|
|
|
|
while (--num >= 0) {
|
|
memcpy(out, in->iov_base, in->iov_len);
|
|
out += in->iov_len;
|
|
in++;
|
|
}
|
|
return out - orig;
|
|
}
|
|
|
|
static int __bencode_iovec_dump(struct iovec *out, bencode_item_t *item) {
|
|
bencode_item_t *child;
|
|
struct iovec *orig = out;
|
|
|
|
assert(item->iov[0].iov_base != NULL);
|
|
out += __bencode_iovec_cpy(out, &item->iov[0], 1);
|
|
|
|
child = item->child;
|
|
while (child) {
|
|
out += __bencode_iovec_dump(out, child);
|
|
child = child->sibling;
|
|
}
|
|
|
|
if (item->iov[1].iov_base)
|
|
out += __bencode_iovec_cpy(out, &item->iov[1], 1);
|
|
|
|
assert((out - orig) == item->iov_cnt);
|
|
(void) orig;
|
|
return item->iov_cnt;
|
|
}
|
|
|
|
static size_t __bencode_str_dump(char *out, bencode_item_t *item) {
|
|
char *orig = out;
|
|
bencode_item_t *child;
|
|
|
|
assert(item->iov[0].iov_base != NULL);
|
|
out += __bencode_str_cpy(out, &item->iov[0], 1);
|
|
|
|
child = item->child;
|
|
while (child) {
|
|
out += __bencode_str_dump(out, child);
|
|
child = child->sibling;
|
|
}
|
|
|
|
if (item->iov[1].iov_base)
|
|
out += __bencode_str_cpy(out, &item->iov[1], 1);
|
|
|
|
assert((out - orig) == item->str_len);
|
|
(void) orig;
|
|
*out = '\0';
|
|
return item->str_len;
|
|
}
|
|
|
|
struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail) {
|
|
struct iovec *ret;
|
|
|
|
if (!root)
|
|
return NULL;
|
|
assert(cnt != NULL);
|
|
assert(root->iov_cnt > 0);
|
|
|
|
ret = bencode_buffer_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail));
|
|
if (!ret)
|
|
return NULL;
|
|
*cnt = __bencode_iovec_dump(ret + head, root);
|
|
return ret;
|
|
}
|
|
|
|
char *bencode_collapse(bencode_item_t *root, size_t *len) {
|
|
char *ret;
|
|
size_t l;
|
|
|
|
if (!root)
|
|
return NULL;
|
|
assert(root->str_len > 0);
|
|
|
|
ret = bencode_buffer_alloc(root->buffer, root->str_len + 1);
|
|
if (!ret)
|
|
return NULL;
|
|
l = __bencode_str_dump(ret, root);
|
|
if (len)
|
|
*len = l;
|
|
return ret;
|
|
}
|
|
|
|
char *bencode_collapse_dup(bencode_item_t *root, size_t *len) {
|
|
char *ret;
|
|
int l;
|
|
|
|
if (!root)
|
|
return NULL;
|
|
assert(root->str_len > 0);
|
|
|
|
ret = BENCODE_MALLOC(root->str_len + 1);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
l = __bencode_str_dump(ret, root);
|
|
if (len)
|
|
*len = l;
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int __bencode_hash_str_len(const unsigned char *s, int len) {
|
|
unsigned long ul;
|
|
unsigned int ui;
|
|
unsigned short us;
|
|
|
|
if (len >= sizeof(ul)) {
|
|
memcpy(&ul, s, sizeof(ul));
|
|
return ul % BENCODE_HASH_BUCKETS;
|
|
}
|
|
if (len >= sizeof(ui)) {
|
|
memcpy(&ui, s, sizeof(ui));
|
|
return ui % BENCODE_HASH_BUCKETS;
|
|
}
|
|
if (len >= sizeof(us)) {
|
|
memcpy(&us, s, sizeof(us));
|
|
return us % BENCODE_HASH_BUCKETS;
|
|
}
|
|
if (len >= sizeof(*s))
|
|
return *s % BENCODE_HASH_BUCKETS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int __bencode_hash_str(bencode_item_t *s) {
|
|
assert(s->type == BENCODE_STRING);
|
|
return __bencode_hash_str_len(s->iov[1].iov_base, s->iov[1].iov_len);
|
|
}
|
|
|
|
static void __bencode_hash_insert(bencode_item_t *key, struct __bencode_hash *hash) {
|
|
unsigned int bucket, i;
|
|
|
|
i = bucket = __bencode_hash_str(key);
|
|
|
|
while (1) {
|
|
if (!hash->buckets[i]) {
|
|
hash->buckets[i] = key;
|
|
break;
|
|
}
|
|
i++;
|
|
if (i >= BENCODE_HASH_BUCKETS)
|
|
i = 0;
|
|
if (i == bucket)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bencode_item_t *__bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) {
|
|
bencode_item_t *ret, *key, *value;
|
|
struct __bencode_hash *hash;
|
|
|
|
if (*s != 'd')
|
|
return NULL;
|
|
s++;
|
|
|
|
ret = __bencode_item_alloc(buf, sizeof(*hash));
|
|
if (!ret)
|
|
return NULL;
|
|
__bencode_dictionary_init(ret);
|
|
ret->value = 1;
|
|
hash = (void *) ret->__buf;
|
|
memset(hash, 0, sizeof(*hash));
|
|
|
|
while (s < end) {
|
|
key = __bencode_decode(buf, s, end);
|
|
if (!key)
|
|
return NULL;
|
|
s += key->str_len;
|
|
if (key->type == BENCODE_END_MARKER)
|
|
break;
|
|
if (key->type != BENCODE_STRING)
|
|
return NULL;
|
|
__bencode_container_add(ret, key);
|
|
|
|
if (s >= end)
|
|
return NULL;
|
|
value = __bencode_decode(buf, s, end);
|
|
if (!value)
|
|
return NULL;
|
|
s += value->str_len;
|
|
if (value->type == BENCODE_END_MARKER)
|
|
return NULL;
|
|
__bencode_container_add(ret, value);
|
|
|
|
__bencode_hash_insert(key, hash);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bencode_item_t *__bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) {
|
|
bencode_item_t *ret, *item;
|
|
|
|
if (*s != 'l')
|
|
return NULL;
|
|
s++;
|
|
|
|
ret = __bencode_item_alloc(buf, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
__bencode_list_init(ret);
|
|
|
|
while (s < end) {
|
|
item = __bencode_decode(buf, s, end);
|
|
if (!item)
|
|
return NULL;
|
|
s += item->str_len;
|
|
if (item->type == BENCODE_END_MARKER)
|
|
break;
|
|
__bencode_container_add(ret, item);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bencode_item_t *__bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) {
|
|
long long int i;
|
|
const char *orig = s;
|
|
char *convend;
|
|
bencode_item_t *ret;
|
|
|
|
if (*s != 'i')
|
|
return NULL;
|
|
s++;
|
|
|
|
if (s >= end)
|
|
return NULL;
|
|
|
|
if (*s == '0') {
|
|
i = 0;
|
|
s++;
|
|
goto done;
|
|
}
|
|
|
|
i = strtoll(s, &convend, 10);
|
|
if (convend == s)
|
|
return NULL;
|
|
s += (convend - s);
|
|
|
|
done:
|
|
if (s >= end)
|
|
return NULL;
|
|
if (*s != 'e')
|
|
return NULL;
|
|
s++;
|
|
|
|
ret = __bencode_item_alloc(buf, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
ret->type = BENCODE_INTEGER;
|
|
ret->iov[0].iov_base = (void *) orig;
|
|
ret->iov[0].iov_len = s - orig;
|
|
ret->iov[1].iov_base = NULL;
|
|
ret->iov[1].iov_len = 0;
|
|
ret->iov_cnt = 1;
|
|
ret->str_len = s - orig;
|
|
ret->value = i;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bencode_item_t *__bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) {
|
|
unsigned long int sl;
|
|
char *convend;
|
|
const char *orig = s;
|
|
bencode_item_t *ret;
|
|
|
|
if (*s == '0') {
|
|
sl = 0;
|
|
s++;
|
|
goto colon;
|
|
}
|
|
|
|
sl = strtoul(s, &convend, 10);
|
|
if (convend == s)
|
|
return NULL;
|
|
s += (convend - s);
|
|
|
|
colon:
|
|
if (s >= end)
|
|
return NULL;
|
|
if (*s != ':')
|
|
return NULL;
|
|
s++;
|
|
|
|
if (s + sl > end)
|
|
return NULL;
|
|
|
|
ret = __bencode_item_alloc(buf, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
ret->type = BENCODE_STRING;
|
|
ret->iov[0].iov_base = (void *) orig;
|
|
ret->iov[0].iov_len = s - orig;
|
|
ret->iov[1].iov_base = (void *) s;
|
|
ret->iov[1].iov_len = sl;
|
|
ret->iov_cnt = 2;
|
|
ret->str_len = s - orig + sl;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end) {
|
|
if (s >= end)
|
|
return NULL;
|
|
|
|
switch (*s) {
|
|
case 'd':
|
|
return __bencode_decode_dictionary(buf, s, end);
|
|
case 'l':
|
|
return __bencode_decode_list(buf, s, end);
|
|
case 'i':
|
|
return __bencode_decode_integer(buf, s, end);
|
|
case 'e':
|
|
return &__bencode_end_marker;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
return __bencode_decode_string(buf, s, end);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, size_t len) {
|
|
assert(s != NULL);
|
|
return __bencode_decode(buf, s, s + len);
|
|
}
|
|
|
|
|
|
static int __bencode_dictionary_key_match(bencode_item_t *key, const char *keystr, size_t keylen) {
|
|
assert(key->type == BENCODE_STRING);
|
|
|
|
if (keylen != key->iov[1].iov_len)
|
|
return 0;
|
|
if (memcmp(keystr, key->iov[1].iov_base, keylen))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, size_t keylen) {
|
|
bencode_item_t *key;
|
|
unsigned int bucket, i;
|
|
struct __bencode_hash *hash;
|
|
|
|
if (!dict)
|
|
return NULL;
|
|
if (dict->type != BENCODE_DICTIONARY)
|
|
return NULL;
|
|
|
|
/* try hash lookup first if possible */
|
|
if (dict->value == 1) {
|
|
hash = (void *) dict->__buf;
|
|
i = bucket = __bencode_hash_str_len((const unsigned char *) keystr, keylen);
|
|
while (1) {
|
|
key = hash->buckets[i];
|
|
if (!key)
|
|
return NULL; /* would be there, but isn't */
|
|
assert(key->sibling != NULL);
|
|
if (__bencode_dictionary_key_match(key, keystr, keylen))
|
|
return key->sibling;
|
|
i++;
|
|
if (i >= BENCODE_HASH_BUCKETS)
|
|
i = 0;
|
|
if (i == bucket)
|
|
break; /* fall back to regular lookup */
|
|
}
|
|
}
|
|
|
|
for (key = dict->child; key; key = key->sibling->sibling) {
|
|
assert(key->sibling != NULL);
|
|
if (__bencode_dictionary_key_match(key, keystr, keylen))
|
|
return key->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p) {
|
|
struct __bencode_free_list *li;
|
|
|
|
if (!p)
|
|
return;
|
|
li = bencode_buffer_alloc(buf, sizeof(*li));
|
|
if (!li)
|
|
return;
|
|
li->ptr = p;
|
|
li->func = func;
|
|
li->next = buf->free_list;
|
|
buf->free_list = li;
|
|
}
|
|
|
|
static ssize_t __bencode_string(const char *s, ssize_t offset, size_t len) {
|
|
size_t pos;
|
|
unsigned long sl;
|
|
char *end;
|
|
|
|
for (pos = offset; s[pos] != ':' && isdigit(s[pos]) && pos < len; ++pos);
|
|
if (pos == len)
|
|
return -1;
|
|
|
|
sl = strtoul(s + offset, &end, 10);
|
|
if (s + offset == end || end != s + pos)
|
|
return -2;
|
|
|
|
if (pos + sl > len)
|
|
return -1;
|
|
|
|
return pos + sl + 1;
|
|
}
|
|
|
|
static ssize_t __bencode_integer(const char *s, ssize_t offset, size_t len) {
|
|
size_t pos;
|
|
|
|
if (s[offset + 1] == '-') {
|
|
if (offset + 3 < len && s[offset + 2] == '0' && s[offset + 3] == 'e') {
|
|
return -2;
|
|
}
|
|
++offset;
|
|
}
|
|
|
|
if (s[offset + 1] == 'e')
|
|
return -2;
|
|
|
|
if (s[offset + 1] == '0') {
|
|
if (offset + 2 < len && s[offset + 2] == 'e')
|
|
return offset + 3;
|
|
return -2;
|
|
}
|
|
|
|
for (pos = offset + 1; s[pos] != 'e' && pos < len; ++pos) {
|
|
if (s[pos] < '0' || s[pos] > '9')
|
|
return -2;
|
|
}
|
|
|
|
if (pos == len)
|
|
return -1;
|
|
|
|
return pos + 1;
|
|
}
|
|
|
|
static ssize_t __bencode_next(const char *s, ssize_t offset, size_t len);
|
|
|
|
static ssize_t __bencode_list(const char *s, ssize_t offset, size_t len) {
|
|
for (++offset; s[offset] != 'e' && offset < len;) {
|
|
offset = __bencode_next(s, offset, len);
|
|
if (offset < 0)
|
|
return offset;
|
|
}
|
|
|
|
if (offset == len)
|
|
return -1;
|
|
|
|
return offset + 1;
|
|
}
|
|
|
|
static ssize_t __bencode_dictionary(const char *s, ssize_t offset, size_t len) {
|
|
for (++offset; s[offset] != 'e' && offset < len;) {
|
|
offset = __bencode_string(s, offset, len);
|
|
if (offset < 0)
|
|
return offset;
|
|
offset = __bencode_next(s, offset, len);
|
|
if (offset < 0)
|
|
return offset;
|
|
}
|
|
|
|
if (offset == len)
|
|
return -1;
|
|
|
|
return offset + 1;
|
|
}
|
|
|
|
static ssize_t __bencode_next(const char *s, ssize_t offset, size_t len) {
|
|
if (offset >= len)
|
|
return -1;
|
|
switch(s[offset]) {
|
|
case 'i':
|
|
return __bencode_integer(s, offset, len);
|
|
case 'l':
|
|
return __bencode_list(s, offset, len);
|
|
case 'd':
|
|
return __bencode_dictionary(s, offset, len);
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
return __bencode_string(s, offset, len);
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
ssize_t bencode_valid(const char *s, size_t len) {
|
|
return __bencode_next(s, 0, len);
|
|
}
|
|
|
|
|
|
|
|
static bencode_item_t *bencode_convert_json_node(bencode_buffer_t *buf, JsonNode *node);
|
|
|
|
static bencode_item_t *bencode_convert_json_dict(bencode_buffer_t *buf, JsonNode *node) {
|
|
JsonObject *obj = json_node_get_object(node);
|
|
if (!obj)
|
|
return NULL;
|
|
bencode_item_t *dict = bencode_dictionary(buf);
|
|
if (!dict)
|
|
return NULL;
|
|
|
|
JsonObjectIter iter;
|
|
json_object_iter_init(&iter, obj);
|
|
const char *key;
|
|
JsonNode *value;
|
|
while (json_object_iter_next(&iter, &key, &value)) {
|
|
if (!key || !value)
|
|
return NULL;
|
|
bencode_item_t *b_val = bencode_convert_json_node(buf, value);
|
|
if (!b_val)
|
|
return NULL;
|
|
bencode_dictionary_add(dict, key, b_val);
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
static bencode_item_t *bencode_convert_json_array(bencode_buffer_t *buf, JsonNode *node) {
|
|
JsonArray *arr = json_node_get_array(node);
|
|
if (!arr)
|
|
return NULL;
|
|
bencode_item_t *list = bencode_list(buf);
|
|
if (!list)
|
|
return NULL;
|
|
guint len = json_array_get_length(arr);
|
|
for (guint i = 0; i < len; i++) {
|
|
JsonNode *el = json_array_get_element(arr, i);
|
|
if (!el)
|
|
return NULL;
|
|
bencode_item_t *it = bencode_convert_json_node(buf, el);
|
|
if (!it)
|
|
return NULL;
|
|
bencode_list_add(list, it);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static bencode_item_t *bencode_convert_json_value(bencode_buffer_t *buf, JsonNode *node) {
|
|
GType type = json_node_get_value_type(node);
|
|
switch (type) {
|
|
case G_TYPE_STRING:;
|
|
const char *s = json_node_get_string(node);
|
|
if (!s)
|
|
return NULL;
|
|
return bencode_string(buf, s);
|
|
case G_TYPE_INT:
|
|
case G_TYPE_UINT:
|
|
case G_TYPE_LONG:
|
|
case G_TYPE_ULONG:
|
|
case G_TYPE_INT64:
|
|
case G_TYPE_UINT64:
|
|
case G_TYPE_BOOLEAN:;
|
|
gint64 i = json_node_get_int(node);
|
|
return bencode_integer(buf, i);
|
|
// everything else is unsupported
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bencode_item_t *bencode_convert_json_node(bencode_buffer_t *buf, JsonNode *node) {
|
|
JsonNodeType type = json_node_get_node_type(node);
|
|
switch (type) {
|
|
case JSON_NODE_OBJECT:
|
|
return bencode_convert_json_dict(buf, node);
|
|
case JSON_NODE_ARRAY:
|
|
return bencode_convert_json_array(buf, node);
|
|
case JSON_NODE_VALUE:
|
|
return bencode_convert_json_value(buf, node);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bencode_item_t *bencode_convert_json(bencode_buffer_t *buf, JsonParser *json) {
|
|
JsonNode *root = json_parser_get_root(json);
|
|
if (!root)
|
|
return NULL;
|
|
return bencode_convert_json_node(buf, root);
|
|
}
|
|
|
|
|
|
|
|
gboolean bencode_collapse_json_item(bencode_item_t *item, JsonBuilder *builder);
|
|
|
|
gboolean bencode_collapse_json_list(bencode_item_t *item, JsonBuilder *builder) {
|
|
json_builder_begin_array(builder);
|
|
for (bencode_item_t *el = item->child; el; el = el->sibling) {
|
|
if (!bencode_collapse_json_item(el, builder))
|
|
return FALSE;
|
|
}
|
|
json_builder_end_array(builder);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean bencode_collapse_json_string(bencode_item_t *item, JsonBuilder *builder) {
|
|
char buf[item->iov[1].iov_len + 1];
|
|
memcpy(buf, item->iov[1].iov_base, item->iov[1].iov_len);
|
|
buf[item->iov[1].iov_len] = '\0';
|
|
json_builder_add_string_value(builder, buf);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean bencode_collapse_json_dict(bencode_item_t *item, JsonBuilder *builder) {
|
|
json_builder_begin_object(builder);
|
|
bencode_item_t *val;
|
|
for (bencode_item_t *key = item->child; key; key = val->sibling) {
|
|
val = key->sibling;
|
|
if (key->type != BENCODE_STRING)
|
|
return FALSE;
|
|
|
|
char buf[key->iov[1].iov_len + 1];
|
|
memcpy(buf, key->iov[1].iov_base, key->iov[1].iov_len);
|
|
buf[key->iov[1].iov_len] = '\0';
|
|
|
|
json_builder_set_member_name(builder, buf);
|
|
|
|
if (!bencode_collapse_json_item(val, builder))
|
|
return FALSE;
|
|
}
|
|
json_builder_end_object(builder);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean bencode_collapse_json_int(bencode_item_t *item, JsonBuilder *builder) {
|
|
json_builder_add_int_value(builder, item->value);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean bencode_collapse_json_item(bencode_item_t *item, JsonBuilder *builder) {
|
|
switch (item->type) {
|
|
case BENCODE_LIST:
|
|
return bencode_collapse_json_list(item, builder);
|
|
case BENCODE_STRING:
|
|
return bencode_collapse_json_string(item, builder);
|
|
case BENCODE_DICTIONARY:
|
|
return bencode_collapse_json_dict(item, builder);
|
|
case BENCODE_INTEGER:
|
|
return bencode_collapse_json_int(item, builder);
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
str *bencode_collapse_str_json(bencode_item_t *root, str *out) {
|
|
JsonBuilder *builder = json_builder_new();
|
|
if (!bencode_collapse_json_item(root, builder))
|
|
goto err;
|
|
char *result = glib_json_print(builder);
|
|
out->s = result;
|
|
out->len = strlen(result);
|
|
bencode_buffer_destroy_add(root->buffer, free, result);
|
|
return out;
|
|
|
|
err:
|
|
g_object_unref(builder);
|
|
return NULL;
|
|
}
|