serval-dna/overlay_buffer.c
Andrew Bettison 8db5f9c14a Merge branch 'anyservice' into 'development'
Allows any valid "service" manifest field in the "rhizome add file"
command

Many improvements in Rhizome manifest parsing; stricter manifest syntax
rules (no comment or blank lines, field names must be alphanumeric
identifiers), faster preliminary manifest inspection when receiving
manifest advertisements or syncing manifests

The 'development' branch introduces "struct socket_address" which
coincidentally fixed the recently encountered Linux kernel 3.12
recvmsg(2) EINVAL problem, so that 'rhizomeprotocol' tests which fail on
the 'anyservice' branch will pass after this merge
2013-12-01 05:44:01 +10:30

607 lines
17 KiB
C

/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <assert.h>
#include "serval.h"
#include "conf.h"
#include "mem.h"
#include "overlay_buffer.h"
/*
When writing to a buffer, sizeLimit may place an upper bound on the amount of space to use
When reading from a buffer, sizeLimit should first be set to the length of any existing data.
In either case, functions that don't take an offset use and advance the position.
*/
struct overlay_buffer *_ob_new(struct __sourceloc __whence)
{
struct overlay_buffer *ret = emalloc_zero(sizeof(struct overlay_buffer));
if (config.debug.overlaybuffer)
DEBUGF("ob_new() return %p", ret);
if (ret == NULL)
return NULL;
ob_unlimitsize(ret);
return ret;
}
// index an existing static buffer.
// and allow other callers to use the ob_ convenience methods for reading and writing up to size bytes.
struct overlay_buffer *_ob_static(struct __sourceloc __whence, unsigned char *bytes, int size)
{
struct overlay_buffer *ret = emalloc_zero(sizeof(struct overlay_buffer));
if (config.debug.overlaybuffer)
DEBUGF("ob_static(bytes=%p, size=%d) return %p", bytes, size, ret);
if (ret == NULL)
return NULL;
ret->bytes = bytes;
ret->allocSize = size;
ret->allocated = NULL;
ob_unlimitsize(ret);
return ret;
}
// create a new overlay buffer from an existing piece of another buffer.
// Both buffers will point to the same memory region.
// It is up to the caller to ensure this buffer is not used after the parent buffer is freed.
struct overlay_buffer *_ob_slice(struct __sourceloc __whence, struct overlay_buffer *b, int offset, int length)
{
if (offset+length > b->allocSize) {
WHY("Buffer isn't long enough to slice");
return NULL;
}
struct overlay_buffer *ret = emalloc_zero(sizeof(struct overlay_buffer));
if (config.debug.overlaybuffer)
DEBUGF("ob_slice(b=%p, offset=%d, length=%d) return %p", b, offset, length, ret);
if (ret == NULL)
return NULL;
ret->bytes = b->bytes+offset;
ret->allocSize = length;
ret->allocated = NULL;
ob_unlimitsize(ret);
return ret;
}
struct overlay_buffer *_ob_dup(struct __sourceloc __whence, struct overlay_buffer *b)
{
struct overlay_buffer *ret = emalloc_zero(sizeof(struct overlay_buffer));
if (config.debug.overlaybuffer)
DEBUGF("ob_dup(b=%p) return %p", b, ret);
if (ret == NULL)
return NULL;
ret->sizeLimit = b->sizeLimit;
ret->position = b->position;
ret->checkpointLength = b->checkpointLength;
if (b->bytes && b->allocSize){
// duplicate any bytes that might be relevant
int byteCount = b->sizeLimit;
if (byteCount < b->position)
byteCount = b->position;
if (byteCount > b->allocSize)
byteCount = b->allocSize;
if (byteCount)
ob_append_bytes(ret, b->bytes, byteCount);
}
return ret;
}
void _ob_free(struct __sourceloc __whence, struct overlay_buffer *b)
{
assert(b != NULL);
if (config.debug.overlaybuffer)
DEBUGF("ob_free(b=%p)", b);
if (b->allocated)
free(b->allocated);
free(b);
}
int _ob_checkpoint(struct __sourceloc __whence, struct overlay_buffer *b)
{
assert(b != NULL);
b->checkpointLength = b->position;
if (config.debug.overlaybuffer)
DEBUGF("ob_checkpoint(b=%p) checkpointLength=%d", b, b->checkpointLength);
return 0;
}
int _ob_rewind(struct __sourceloc __whence, struct overlay_buffer *b)
{
assert(b != NULL);
b->position = b->checkpointLength;
if (config.debug.overlaybuffer)
DEBUGF("ob_rewind(b=%p) position=%d", b, b->position);
return 0;
}
void _ob_limitsize(struct __sourceloc __whence, struct overlay_buffer *b, int bytes)
{
assert(b != NULL);
assert(bytes >= 0);
assert(b->position >= 0);
assert(b->position <= bytes);
assert(b->checkpointLength <= bytes);
if (b->bytes && b->allocated == NULL)
assert(bytes <= b->allocSize);
b->sizeLimit = bytes;
if (config.debug.overlaybuffer)
DEBUGF("ob_limitsize(b=%p, bytes=%d) sizeLimit=%d", b, bytes, b->sizeLimit);
}
void _ob_unlimitsize(struct __sourceloc __whence, struct overlay_buffer *b)
{
assert(b != NULL);
b->sizeLimit = -1;
if (config.debug.overlaybuffer)
DEBUGF("ob_unlimitsize(b=%p) sizeLimit=%d", b, b->sizeLimit);
}
void _ob_flip(struct __sourceloc __whence, struct overlay_buffer *b)
{
if (config.debug.overlaybuffer)
DEBUGF("ob_flip(b=%p) checkpointLength=0 position=0", b);
b->checkpointLength = 0;
ob_limitsize(b, b->position);
b->position = 0;
}
/* Return 1 if space is available, 0 if not.
*/
ssize_t _ob_makespace(struct __sourceloc __whence, struct overlay_buffer *b, size_t bytes)
{
assert(b != NULL);
if (config.debug.overlaybuffer)
DEBUGF("ob_makespace(b=%p, bytes=%zd) b->bytes=%p b->position=%d b->allocSize=%d",
b, bytes, b->bytes, b->position, b->allocSize);
assert(b->position >= 0);
if (b->sizeLimit != -1)
assert(b->sizeLimit >= 0);
assert(b->allocSize >= 0);
if (b->position)
assert(b->bytes != NULL);
if (b->sizeLimit != -1 && b->position + bytes > b->sizeLimit) {
if (config.debug.packetformats)
DEBUGF("ob_makespace(): asked for space to %zu, beyond size limit of %u", b->position + bytes, b->sizeLimit);
return 0;
}
if (b->position + bytes <= b->allocSize)
return 1;
// Don't realloc a static buffer.
if (b->bytes && b->allocated == NULL) {
if (config.debug.packetformats)
DEBUGF("ob_makespace(): asked for space to %zu, beyond static buffer size of %u", b->position + bytes, b->allocSize);
return 0;
}
int newSize=b->position+bytes;
if (newSize<64) newSize=64;
if (newSize&63) newSize+=64-(newSize&63);
if (newSize>1024 && (newSize&1023))
newSize+=1024-(newSize&1023);
if (newSize>65536 && (newSize&65535))
newSize+=65536-(newSize&65535);
if (config.debug.overlaybuffer)
DEBUGF("realloc(b->bytes=%p,newSize=%d)", b->bytes,newSize);
/* XXX OSX realloc() seems to be able to corrupt things if the heap is not happy when calling realloc(), making debugging memory corruption much harder.
So will do a three-stage malloc,bcopy,free to see if we can tease bugs out that way. */
/*
unsigned char *r=realloc(b->bytes,newSize);
if (!r) return WHY("realloc() failed");
b->bytes=r;
*/
#ifdef MALLOC_PARANOIA
#warning adding lots of padding to try to catch overruns
if (b->bytes) {
int i;
int corrupt=0;
for(i=0;i<4096;i++) if (b->bytes[b->allocSize+i]!=0xbd) corrupt++;
if (corrupt) {
WHYF("!!!!!! %d corrupted bytes in overrun catch tray", corrupt);
dump("overrun catch tray",&b->bytes[b->allocSize],4096);
sleep_ms(36000000);
}
}
unsigned char *new = emalloc(newSize+4096);
{
int i;
for(i=0;i<4096;i++) new[newSize+i]=0xbd;
}
#else
unsigned char *new = emalloc(newSize);
#endif
if (!new)
return 0;
bcopy(b->bytes,new,b->position);
if (b->allocated) {
assert(b->allocated == b->bytes);
free(b->allocated);
}
b->bytes=new;
b->allocated=new;
b->allocSize=newSize;
return 1;
}
/*
Functions that append data and increase the size of the buffer if possible / required
*/
void _ob_append_byte(struct __sourceloc __whence, struct overlay_buffer *b, unsigned char byte)
{
const int bytes = 1;
if (ob_makespace(b, bytes)) {
b->bytes[b->position] = byte;
if (config.debug.overlaybuffer)
DEBUGF("ob_append_byte(b=%p, byte=0x%02x) %p[%d]=%02x position=%d", b, byte, b->bytes, b->position, byte, b->position + bytes);
} else {
if (config.debug.overlaybuffer)
DEBUGF("ob_append_byte(b=%p, byte=0x%02x) OVERRUN position=%d", b, byte, b->position + bytes);
}
b->position += bytes;
}
unsigned char *_ob_append_space(struct __sourceloc __whence, struct overlay_buffer *b, int count)
{
assert(count > 0);
unsigned char *r = ob_makespace(b, count) ? &b->bytes[b->position] : NULL;
b->position += count;
if (config.debug.overlaybuffer)
DEBUGF("ob_append_space(b=%p, count=%d) position=%d return %p", b, count, b->position, r);
return r;
}
void _ob_append_bytes(struct __sourceloc __whence, struct overlay_buffer *b, const unsigned char *bytes, int count)
{
assert(count > 0);
unsigned char *r = ob_makespace(b, count) ? &b->bytes[b->position] : NULL;
if (r) {
bcopy(bytes, r, count);
if (config.debug.overlaybuffer)
DEBUGF("ob_append_bytes(b=%p, bytes=%p, count=%d) position=%d return %p", b, bytes, count, b->position + count, r);
} else {
if (config.debug.overlaybuffer)
DEBUGF("ob_append_bytes(b=%p, bytes=%p, count=%d) OVERRUN position=%d return NULL", b, bytes, count, b->position + count);
}
if (config.debug.overlaybuffer)
dump("ob_append_bytes", bytes, count);
b->position += count;
}
void _ob_append_buffer(struct __sourceloc __whence, struct overlay_buffer *b, struct overlay_buffer *s)
{
ob_append_bytes(b, s->bytes, s->position);
}
void _ob_append_ui16(struct __sourceloc __whence, struct overlay_buffer *b, uint16_t v)
{
const int bytes = 2;
if (ob_makespace(b, bytes)) {
b->bytes[b->position] = (v >> 8) & 0xFF;
b->bytes[b->position+1] = v & 0xFF;
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui16(b=%p, v=%u) %p[%d]=%s position=%d", b, v, b->bytes, b->position, alloca_tohex(&b->bytes[b->position], bytes), b->position + bytes);
} else {
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui16(b=%p, v=%u) OVERRUN position=%d", b, v, b->position + bytes);
}
b->position += bytes;
}
void _ob_append_ui32(struct __sourceloc __whence, struct overlay_buffer *b, uint32_t v)
{
const int bytes = 4;
if (ob_makespace(b, bytes)) {
b->bytes[b->position] = (v >> 24) & 0xFF;
b->bytes[b->position+1] = (v >> 16) & 0xFF;
b->bytes[b->position+2] = (v >> 8) & 0xFF;
b->bytes[b->position+3] = v & 0xFF;
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui32(b=%p, v=%"PRIu32") %p[%d]=%s position=%d",
b, v, b->bytes, b->position, alloca_tohex(&b->bytes[b->position], bytes), b->position + bytes);
} else {
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui32(b=%p, v=%"PRIu32") OVERRUN position=%d", b, v, b->position + bytes);
}
b->position += bytes;
}
void _ob_append_ui64(struct __sourceloc __whence, struct overlay_buffer *b, uint64_t v)
{
const int bytes = 8;
if (ob_makespace(b, bytes)) {
b->bytes[b->position] = (v >> 56) & 0xFF;
b->bytes[b->position+1] = (v >> 48) & 0xFF;
b->bytes[b->position+2] = (v >> 40) & 0xFF;
b->bytes[b->position+3] = (v >> 32) & 0xFF;
b->bytes[b->position+4] = (v >> 24) & 0xFF;
b->bytes[b->position+5] = (v >> 16) & 0xFF;
b->bytes[b->position+6] = (v >> 8) & 0xFF;
b->bytes[b->position+7] = v & 0xFF;
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui64(b=%p, v=%"PRIu64") %p[%d]=%s position=%d",
b, v, b->bytes, b->position, alloca_tohex(&b->bytes[b->position], bytes), b->position + bytes);
} else {
if (config.debug.overlaybuffer)
DEBUGF("ob_append_ui64(b=%p, v=%"PRIu64") OVERRUN position=%d", b, v, b->position + bytes);
}
b->position += bytes;
}
int measure_packed_uint(uint64_t v){
int ret=0;
do{
v>>=7;
ret++;
}while(v);
return ret;
}
int pack_uint(unsigned char *buffer, uint64_t v){
int ret=0;
do{
*buffer++=(v&0x7f) | (v>0x7f?0x80:0);
v>>=7;
ret++;
}while(v);
return ret;
}
int unpack_uint(unsigned char *buffer, int buff_size, uint64_t *v){
int i=0;
*v=0;
while(1){
if (i>=buff_size)
return -1;
char byte = buffer[i];
*v |= (byte&0x7f)<<(i*7);
i++;
if (!(byte&0x80))
break;
}
return i;
}
void _ob_append_packed_ui32(struct __sourceloc __whence, struct overlay_buffer *b, uint32_t v)
{
do {
ob_append_byte(b, (v&0x7f) | (v>0x7f?0x80:0));
v = v >> 7;
} while (v != 0);
}
void _ob_append_packed_ui64(struct __sourceloc __whence, struct overlay_buffer *b, uint64_t v)
{
do {
ob_append_byte(b, (v&0x7f) | (v>0x7f?0x80:0));
v = v >> 7;
} while (v != 0);
}
void _ob_append_rfs(struct __sourceloc __whence, struct overlay_buffer *b, int l)
{
assert(l >= 0);
assert(l <= 0xffff);
b->var_length_offset = b->position;
ob_append_ui16(b, l);
}
/*
Functions that read / write data within the existing length limit
*/
// make sure a range of bytes is valid for reading
int test_offset(struct overlay_buffer *b,int start,int length)
{
if (!b) return -1;
if (start<0) return -1;
if (b->sizeLimit>=0 && start+length>b->sizeLimit) return -1;
if (start+length>b->allocSize) return -1;
return 0;
}
// next byte without advancing
int ob_peek(struct overlay_buffer *b)
{
if (test_offset(b, b->position, 1))
return -1;
return b->bytes[b->position];
}
void ob_skip(struct overlay_buffer *b, unsigned n)
{
b->position += n;
}
int ob_get_bytes(struct overlay_buffer *b, unsigned char *buff, int len){
if (test_offset(b, b->position, len))
return -1;
bcopy(b->bytes + b->position, buff, len);
b->position+=len;
return 0;
}
unsigned char * ob_get_bytes_ptr(struct overlay_buffer *b, int len){
if (test_offset(b, b->position, len))
return NULL;
unsigned char *ret = b->bytes + b->position;
b->position+=len;
return ret;
}
uint32_t ob_get_ui32(struct overlay_buffer *b)
{
if (test_offset(b, b->position, 4))
return 0xFFFFFFFF; // ... unsigned
uint32_t ret = b->bytes[b->position] << 24
| b->bytes[b->position +1] << 16
| b->bytes[b->position +2] << 8
| b->bytes[b->position +3];
b->position+=4;
return ret;
}
uint64_t ob_get_ui64(struct overlay_buffer *b)
{
if (test_offset(b, b->position, 8))
return 0xFFFFFFFF; // ... unsigned
uint64_t ret = (uint64_t)b->bytes[b->position] << 56
| (uint64_t)b->bytes[b->position +1] << 48
| (uint64_t)b->bytes[b->position +2] << 40
| (uint64_t)b->bytes[b->position +3] << 36
| b->bytes[b->position +4] << 24
| b->bytes[b->position +5] << 16
| b->bytes[b->position +6] << 8
| b->bytes[b->position +7];
b->position+=8;
return ret;
}
uint16_t ob_get_ui16(struct overlay_buffer *b)
{
if (test_offset(b, b->position, 2))
return 0xFFFF; // ... unsigned
uint16_t ret = b->bytes[b->position] << 8
| b->bytes[b->position +1];
b->position+=2;
return ret;
}
uint32_t ob_get_packed_ui32(struct overlay_buffer *b)
{
uint32_t ret=0;
int shift=0;
int byte;
do{
byte = ob_get(b);
if (byte<0)
return WHY("Failed to unpack integer");
ret |= (byte&0x7f)<<shift;
shift+=7;
}while(byte & 0x80);
return ret;
}
uint64_t ob_get_packed_ui64(struct overlay_buffer *b)
{
uint64_t ret=0;
int shift=0;
int byte;
do{
byte = ob_get(b);
if (byte<0)
return WHY("Failed to unpack integer");
ret |= (byte&0x7f)<<shift;
shift+=7;
}while(byte & 0x80);
return ret;
}
int ob_get(struct overlay_buffer *b)
{
if (test_offset(b, b->position, 1))
return -1;
return b->bytes[b->position++];
}
void _ob_set_ui16(struct __sourceloc __whence, struct overlay_buffer *b, int offset, uint16_t v)
{
const int bytes = 2;
assert(b != NULL);
assert(offset >= 0);
if (b->sizeLimit != -1)
assert(offset + bytes <= b->sizeLimit);
assert(offset + bytes <= b->allocSize);
b->bytes[offset] = (v >> 8) & 0xFF;
b->bytes[offset+1] = v & 0xFF;
if (config.debug.overlaybuffer)
DEBUGF("ob_set_ui16(b=%p, offset=%d, v=%u) %p[%d]=%s", b, offset, v, b->bytes, offset, alloca_tohex(&b->bytes[offset], bytes));
}
void _ob_set(struct __sourceloc __whence, struct overlay_buffer *b, int offset, unsigned char byte)
{
const int bytes = 1;
assert(b != NULL);
assert(offset >= 0);
if (b->sizeLimit != -1)
assert(offset + bytes <= b->sizeLimit);
assert(offset + bytes <= b->allocSize);
b->bytes[offset] = byte;
if (config.debug.overlaybuffer)
DEBUGF("ob_set(b=%p, offset=%d, byte=0x%02x) %p[%d]=%s", b, offset, byte, b->bytes, offset, alloca_tohex(&b->bytes[offset], bytes));
}
void _ob_patch_rfs(struct __sourceloc __whence, struct overlay_buffer *b)
{
ob_set_ui16(b,b->var_length_offset,b->position - (b->var_length_offset + 2));
}
int ob_position(struct overlay_buffer *b)
{
return b->position;
}
int ob_limit(struct overlay_buffer *b)
{
return b->sizeLimit;
}
int ob_remaining(struct overlay_buffer *b)
{
assert(b->sizeLimit != -1);
return b->sizeLimit - b->position;
}
int _ob_overrun(struct __sourceloc __whence, struct overlay_buffer *b)
{
assert(b->allocSize >= 0);
if (b->sizeLimit != -1)
assert(b->sizeLimit >= 0);
int ret = b->position > (b->sizeLimit != -1 && b->sizeLimit < b->allocSize ? b->sizeLimit : b->allocSize);
if (config.debug.overlaybuffer)
DEBUGF("ob_overrun(b=%p) return %d", b, ret);
return ret;
}
unsigned char *ob_ptr(struct overlay_buffer *b)
{
return b->bytes;
}
int asprintable(int c)
{
if (c<' ') return '.';
if (c>0x7e) return '.';
return c;
}
int ob_dump(struct overlay_buffer *b, char *desc)
{
DEBUGF("overlay_buffer '%s' at %p (%p) : position=%d, size=%d", desc, b, b->bytes, b->position, b->sizeLimit);
if (b->bytes && (b->position || b->sizeLimit))
dump(desc, b->bytes, b->position?b->position:b->sizeLimit);
return 0;
}