WIP
This commit is contained in:
parent
3b4106743b
commit
1b4e4c5287
2 changed files with 652 additions and 0 deletions
141
include/bencode.h
Normal file
141
include/bencode.h
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// https://github.com/willemt/heapless-bencode
|
||||||
|
//
|
||||||
|
#ifndef BENCODE_H_
|
||||||
|
#define BENCODE_H_
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
const char *start;
|
||||||
|
void *parent;
|
||||||
|
int val;
|
||||||
|
int len;
|
||||||
|
} bencode_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise a bencode object.
|
||||||
|
* @param be The bencode object
|
||||||
|
* @param str Buffer we expect input from
|
||||||
|
* @param len Length of buffer
|
||||||
|
*/
|
||||||
|
void bencode_init(
|
||||||
|
bencode_t * be,
|
||||||
|
const char *str,
|
||||||
|
int len
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the bencode object is an int; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_is_int(
|
||||||
|
const bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the bencode object is a string; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_is_string(
|
||||||
|
const bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the bencode object is a list; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_is_list(
|
||||||
|
const bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the bencode object is a dict; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_is_dict(
|
||||||
|
const bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain value from integer bencode object.
|
||||||
|
* @param val Long int we are writing the result to
|
||||||
|
* @return 1 on success, otherwise 0
|
||||||
|
*/
|
||||||
|
int bencode_int_value(
|
||||||
|
bencode_t * be,
|
||||||
|
long int *val
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if there is another item on this dict; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_dict_has_next(
|
||||||
|
bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next item within this dictionary.
|
||||||
|
* @param be_item Next item.
|
||||||
|
* @param key Const pointer to key string of next item.
|
||||||
|
* @param klen Length of the key of next item.
|
||||||
|
* @return 1 on success; otherwise 0.
|
||||||
|
*/
|
||||||
|
int bencode_dict_get_next(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * be_item,
|
||||||
|
const char **key,
|
||||||
|
int *klen
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the string value from this bencode object.
|
||||||
|
* The buffer returned is stored on the stack.
|
||||||
|
* @param be The bencode object.
|
||||||
|
* @param str Const pointer to the buffer.
|
||||||
|
* @param slen Length of the buffer we are outputting.
|
||||||
|
* @return 1 on success; otherwise 0
|
||||||
|
*/
|
||||||
|
int bencode_string_value(
|
||||||
|
bencode_t * be,
|
||||||
|
const char **str,
|
||||||
|
int *len
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell if there is another item within this list.
|
||||||
|
* @param be The bencode object
|
||||||
|
* @return 1 if another item exists on the list; 0 otherwise; -1 on invalid processing
|
||||||
|
*/
|
||||||
|
int bencode_list_has_next(
|
||||||
|
bencode_t * be
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next item within this list.
|
||||||
|
* @param be The bencode object
|
||||||
|
* @param be_item The next bencode object that we are going to initiate.
|
||||||
|
* @return return 0 on end; 1 on have next; -1 on error
|
||||||
|
*/
|
||||||
|
int bencode_list_get_next(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * be_item
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy bencode object into other bencode object
|
||||||
|
*/
|
||||||
|
void bencode_clone(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * output
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start and end position of this dictionary
|
||||||
|
* @param be Bencode object
|
||||||
|
* @param start Starting string
|
||||||
|
* @param len Length of the dictionary
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int bencode_dict_get_start_and_len(
|
||||||
|
bencode_t * be,
|
||||||
|
const char **start,
|
||||||
|
int *len
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* BENCODE_H_ */
|
511
lib/bencode.c
Normal file
511
lib/bencode.c
Normal file
|
@ -0,0 +1,511 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Willem-Hendrik Thiart
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Read bencoded data
|
||||||
|
* @author Willem Thiart himself@willemthiart.com
|
||||||
|
* @version 0.1
|
||||||
|
* https://github.com/willemt/heapless-bencode
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "bencode.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carry length over to a new bencode object.
|
||||||
|
* This is done so that we don't exhaust the buffer */
|
||||||
|
static int __carry_length(
|
||||||
|
bencode_t * be,
|
||||||
|
const char *pos
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assert(0 < be->len);
|
||||||
|
return be->len - (pos - be->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param end The point that we read out to
|
||||||
|
* @param val Output of number represented by string
|
||||||
|
* @return 0 if error; otherwise 1 */
|
||||||
|
static long int __read_string_int(
|
||||||
|
const char *sp,
|
||||||
|
const char **end,
|
||||||
|
long int *val
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*val = 0;
|
||||||
|
|
||||||
|
int sign = 1;
|
||||||
|
|
||||||
|
/* negative */
|
||||||
|
if ('-' == *sp)
|
||||||
|
{
|
||||||
|
sign = -1;
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isdigit(*sp))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* work out number */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*val *= 10;
|
||||||
|
*val += *sp - '0';
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
while (isdigit(*sp));
|
||||||
|
|
||||||
|
*val *= sign;
|
||||||
|
|
||||||
|
*end = sp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_is_dict(
|
||||||
|
const bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return be->str && *be->str == 'd';
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_is_int(
|
||||||
|
const bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return be->str && *be->str == 'i';
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_is_list(
|
||||||
|
const bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return be->str && *be->str == 'l';
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_is_string(
|
||||||
|
const bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
sp = be->str;
|
||||||
|
|
||||||
|
assert(sp);
|
||||||
|
|
||||||
|
if (!isdigit(*sp))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
do sp++;
|
||||||
|
while (isdigit(*sp));
|
||||||
|
|
||||||
|
return *sp == ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to next item
|
||||||
|
* @param sp The bencode string we are processing
|
||||||
|
* @return Pointer to string on success, otherwise NULL */
|
||||||
|
static const char *__iterate_to_next_string_pos(
|
||||||
|
bencode_t * be,
|
||||||
|
const char *sp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bencode_t iter;
|
||||||
|
|
||||||
|
bencode_init(&iter, sp, __carry_length(be, sp));
|
||||||
|
|
||||||
|
if (bencode_is_dict(&iter))
|
||||||
|
{
|
||||||
|
/* navigate to the end of the dictionary */
|
||||||
|
while (bencode_dict_has_next(&iter))
|
||||||
|
{
|
||||||
|
/* ERROR: input string is invalid */
|
||||||
|
if (0 == bencode_dict_get_next(&iter, NULL, NULL, NULL))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* special case for empty dict */
|
||||||
|
if (*iter.str == 'd' && *(iter.str + 1) == 'e')
|
||||||
|
return iter.str + 2;
|
||||||
|
|
||||||
|
return iter.str + 1;
|
||||||
|
}
|
||||||
|
else if (bencode_is_list(&iter))
|
||||||
|
{
|
||||||
|
/* navigate to the end of the list */
|
||||||
|
while (bencode_list_has_next(&iter))
|
||||||
|
{
|
||||||
|
/* ERROR: input string is invalid */
|
||||||
|
if (-1 == bencode_list_get_next(&iter, NULL))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter.str + 1;
|
||||||
|
}
|
||||||
|
else if (bencode_is_string(&iter))
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
const char *str;
|
||||||
|
|
||||||
|
/* ERROR: input string is invalid */
|
||||||
|
if (0 == bencode_string_value(&iter, &str, &len))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return str + len;
|
||||||
|
}
|
||||||
|
else if (bencode_is_int(&iter))
|
||||||
|
{
|
||||||
|
const char *end;
|
||||||
|
long int val;
|
||||||
|
|
||||||
|
if (0 == __read_string_int(&iter.str[1], &end, &val))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert(end[0] == 'e');
|
||||||
|
|
||||||
|
return end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* input string is invalid */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *__read_string_len(
|
||||||
|
const char *sp,
|
||||||
|
int *slen
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*slen = 0;
|
||||||
|
|
||||||
|
if (!isdigit(*sp))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*slen *= 10;
|
||||||
|
*slen += *sp - '0';
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
while (isdigit(*sp));
|
||||||
|
|
||||||
|
assert(*sp == ':');
|
||||||
|
assert(0 <= *slen);
|
||||||
|
|
||||||
|
return sp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bencode_init(
|
||||||
|
bencode_t * be,
|
||||||
|
const char *str,
|
||||||
|
const int len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
memset(be, 0, sizeof(bencode_t));
|
||||||
|
be->str = be->start = str;
|
||||||
|
be->str = str;
|
||||||
|
be->len = len;
|
||||||
|
/* assert(0 < be->len); */
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_int_value(
|
||||||
|
bencode_t * be,
|
||||||
|
long int *val
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *end;
|
||||||
|
|
||||||
|
if (0 == __read_string_int(&be->str[1], &end, val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert(end[0] == 'e');
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_dict_has_next(
|
||||||
|
bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp = be->str;
|
||||||
|
|
||||||
|
assert(be);
|
||||||
|
|
||||||
|
if (!sp
|
||||||
|
/* at end of dict */
|
||||||
|
|| *sp == 'e'
|
||||||
|
/* at end of string */
|
||||||
|
|| *sp == '\0'
|
||||||
|
|| *sp == '\r'
|
||||||
|
/* empty dict */
|
||||||
|
|| (*sp == 'd' && *(sp + 1) == 'e')
|
||||||
|
/* at the end of the input string */
|
||||||
|
|| be->str >= be->start + be->len - 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_dict_get_next(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * be_item,
|
||||||
|
const char **key,
|
||||||
|
int *klen
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp = be->str;
|
||||||
|
const char *keyin;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
assert(*sp != 'e');
|
||||||
|
|
||||||
|
/* if at start increment to 1st key */
|
||||||
|
if (*sp == 'd')
|
||||||
|
{
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can't get the next item if we are at the end of the dict */
|
||||||
|
if (*sp == 'e')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. find out what the key's length is */
|
||||||
|
keyin = __read_string_len(sp, &len);
|
||||||
|
|
||||||
|
/* 2. if we have a value bencode, lets put the value inside */
|
||||||
|
if (be_item)
|
||||||
|
{
|
||||||
|
*klen = len;
|
||||||
|
bencode_init(be_item, keyin + len, __carry_length(be, keyin + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. iterate to next dict key, or move to next item in parent */
|
||||||
|
if (!(be->str = __iterate_to_next_string_pos(be, keyin + len)))
|
||||||
|
{
|
||||||
|
/* if there isn't anything else or we are at the end of the string */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* if at the end of bencode, check that the 'e' terminator is there */
|
||||||
|
if (be->str == be->start + be->len - 1 && *be->str != 'e')
|
||||||
|
{
|
||||||
|
be->str = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert(be->str);
|
||||||
|
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
*key = keyin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_string_value(
|
||||||
|
bencode_t * be,
|
||||||
|
const char **str,
|
||||||
|
int *slen
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
*slen = 0;
|
||||||
|
|
||||||
|
assert(bencode_is_string(be));
|
||||||
|
|
||||||
|
sp = __read_string_len(be->str, slen);
|
||||||
|
|
||||||
|
assert(sp);
|
||||||
|
assert(0 < be->len);
|
||||||
|
|
||||||
|
/* make sure we still fit within the buffer */
|
||||||
|
if (sp + *slen > be->start + (long int) be->len)
|
||||||
|
{
|
||||||
|
*str = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*str = sp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_list_has_next(
|
||||||
|
bencode_t * be
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
sp = be->str;
|
||||||
|
|
||||||
|
/* empty list */
|
||||||
|
if (*sp == 'l' &&
|
||||||
|
sp == be->start &&
|
||||||
|
*(sp + 1) == 'e')
|
||||||
|
{
|
||||||
|
be->str++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of list */
|
||||||
|
if (*sp == 'e')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_list_get_next(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * be_item
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
sp = be->str;
|
||||||
|
|
||||||
|
#if 0 /* debugging */
|
||||||
|
printf("%.*s\n", (int)(be->len - (be->str - be->start)), be->str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* we're at the end */
|
||||||
|
if (!sp || *sp == 'e')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (*sp == 'l')
|
||||||
|
{
|
||||||
|
/* just move off the start of this list */
|
||||||
|
if (be->start == be->str)
|
||||||
|
{
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can't get the next item if we are at the end of the list */
|
||||||
|
if (*sp == 'e')
|
||||||
|
{
|
||||||
|
be->str = sp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* populate the be_item if it is available */
|
||||||
|
if (be_item)
|
||||||
|
{
|
||||||
|
bencode_init(be_item, sp, __carry_length(be, sp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterate to next value */
|
||||||
|
if (!(be->str = __iterate_to_next_string_pos(be, sp)))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bencode_clone(
|
||||||
|
bencode_t * be,
|
||||||
|
bencode_t * output
|
||||||
|
)
|
||||||
|
{
|
||||||
|
memcpy(output, be, sizeof(bencode_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_dict_get_start_and_len(
|
||||||
|
bencode_t * be,
|
||||||
|
const char **start,
|
||||||
|
int *len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bencode_t ben, ben2;
|
||||||
|
const char *ren;
|
||||||
|
int tmplen;
|
||||||
|
|
||||||
|
bencode_clone(be, &ben);
|
||||||
|
*start = ben.str;
|
||||||
|
while (bencode_dict_has_next(&ben))
|
||||||
|
bencode_dict_get_next(&ben, &ben2, &ren, &tmplen);
|
||||||
|
|
||||||
|
*len = ben.str - *start + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __validate(bencode_t *ben)
|
||||||
|
{
|
||||||
|
if (bencode_is_dict(ben))
|
||||||
|
{
|
||||||
|
while (bencode_dict_has_next(ben))
|
||||||
|
{
|
||||||
|
int klen;
|
||||||
|
const char *key;
|
||||||
|
bencode_t benk;
|
||||||
|
|
||||||
|
if (0 == bencode_dict_get_next(ben, &benk, &key, &klen))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int ret = __validate(&benk);
|
||||||
|
if (0 != ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bencode_is_list(ben))
|
||||||
|
{
|
||||||
|
while (bencode_list_has_next(ben))
|
||||||
|
{
|
||||||
|
bencode_t benl;
|
||||||
|
|
||||||
|
if (-1 == bencode_list_get_next(ben, &benl))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int ret = __validate(&benl);
|
||||||
|
if (0 != ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (bencode_is_string(ben))
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (0 == bencode_string_value(ben, &str, &len))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (bencode_is_int(ben))
|
||||||
|
{
|
||||||
|
long int val;
|
||||||
|
|
||||||
|
if (0 == bencode_int_value(ben, &val))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bencode_validate(char* buf, int len)
|
||||||
|
{
|
||||||
|
bencode_t ben;
|
||||||
|
if (0 == len)
|
||||||
|
return 0;
|
||||||
|
bencode_init(&ben, buf, len);
|
||||||
|
return __validate(&ben);
|
||||||
|
}
|
Loading…
Reference in a new issue