diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-binary-code.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/sieve-binary-code.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-binary-code.c b/pigeonhole/src/lib-sieve/sieve-binary-code.c new file mode 100644 index 0000000..0d76ee0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-code.c @@ -0,0 +1,405 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "mempool.h" +#include "buffer.h" +#include "hash.h" +#include "array.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-script.h" + +#include "sieve-binary-private.h" + +/* + * Forward declarations + */ + +static inline sieve_size_t +sieve_binary_emit_dynamic_data(struct sieve_binary_block *sblock, + const void *data, size_t size); + +/* + * Emission functions + */ + +/* Low-level emission functions */ + +static inline void +_sieve_binary_emit_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + buffer_append(sblock->data, data, size); +} + +static inline void +_sieve_binary_emit_byte(struct sieve_binary_block *sblock, uint8_t byte) +{ + _sieve_binary_emit_data(sblock, &byte, 1); +} + +static inline void +_sieve_binary_update_data(struct sieve_binary_block *sblock, + sieve_size_t address, const void *data, + sieve_size_t size) +{ + buffer_write(sblock->data, address, data, size); +} + +sieve_size_t sieve_binary_emit_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + + _sieve_binary_emit_data(sblock, data, size); + + return address; +} + +sieve_size_t sieve_binary_emit_byte(struct sieve_binary_block *sblock, + uint8_t byte) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + + _sieve_binary_emit_data(sblock, &byte, 1); + + return address; +} + +void sieve_binary_update_data(struct sieve_binary_block *sblock, + sieve_size_t address, const void *data, + sieve_size_t size) +{ + _sieve_binary_update_data(sblock, address, data, size); +} + +/* Offset emission functions */ + +sieve_size_t sieve_binary_emit_offset(struct sieve_binary_block *sblock, + sieve_offset_t offset) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + uint8_t encoded[sizeof(offset)]; + int i; + + for (i = sizeof(offset)-1; i >= 0; i--) { + encoded[i] = (uint8_t)offset; + offset >>= 8; + } + + _sieve_binary_emit_data(sblock, encoded, sizeof(offset)); + + return address; +} + +void sieve_binary_resolve_offset(struct sieve_binary_block *sblock, + sieve_size_t address) +{ + sieve_size_t cur_address = _sieve_binary_block_get_size(sblock); + sieve_offset_t offset; + uint8_t encoded[sizeof(offset)]; + int i; + + i_assert(cur_address > address); + i_assert((cur_address - address) <= (sieve_offset_t)-1); + offset = cur_address - address; + for (i = sizeof(offset)-1; i >= 0; i--) { + encoded[i] = (uint8_t)offset; + offset >>= 8; + } + + _sieve_binary_update_data(sblock, address, encoded, sizeof(offset)); +} + +/* Literal emission */ + +sieve_size_t sieve_binary_emit_integer(struct sieve_binary_block *sblock, + sieve_number_t integer) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + uint8_t buffer[sizeof(sieve_number_t) + 1]; + int bufpos = sizeof(buffer) - 1; + + /* Encode last byte [0xxxxxxx]; msb == 0 marks the last byte */ + buffer[bufpos] = integer & 0x7F; + bufpos--; + + /* Encode first bytes [1xxxxxxx] */ + integer >>= 7; + while (integer > 0) { + buffer[bufpos] = (integer & 0x7F) | 0x80; + bufpos--; + integer >>= 7; + } + + /* Emit encoded integer */ + bufpos++; + _sieve_binary_emit_data(sblock, buffer + bufpos, sizeof(buffer) - bufpos); + + return address; +} + +static inline sieve_size_t +sieve_binary_emit_dynamic_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + sieve_size_t address = + sieve_binary_emit_integer(sblock, (sieve_number_t)size); + + _sieve_binary_emit_data(sblock, data, size); + + return address; +} + +sieve_size_t sieve_binary_emit_cstring(struct sieve_binary_block *sblock, + const char *str) +{ + sieve_size_t address = + sieve_binary_emit_dynamic_data(sblock, (void *)str, + (sieve_size_t)strlen(str)); + + _sieve_binary_emit_byte(sblock, 0); + return address; +} + +sieve_size_t sieve_binary_emit_string(struct sieve_binary_block *sblock, + const string_t *str) +{ + sieve_size_t address = + sieve_binary_emit_dynamic_data(sblock, (void *)str_data(str), + (sieve_size_t)str_len(str)); + + _sieve_binary_emit_byte(sblock, 0); + return address; +} + +/* + * Extension emission + */ + +sieve_size_t sieve_binary_emit_extension(struct sieve_binary_block *sblock, + const struct sieve_extension *ext, + unsigned int offset) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + struct sieve_binary_extension_reg *ereg = NULL; + + (void)sieve_binary_extension_register(sblock->sbin, ext, &ereg); + + i_assert(ereg != NULL); + + _sieve_binary_emit_byte(sblock, offset + ereg->index); + return address; +} + +void sieve_binary_emit_extension_object( + struct sieve_binary_block *sblock, + const struct sieve_extension_objects *objs, unsigned int code) +{ + if (objs->count > 1) + _sieve_binary_emit_byte(sblock, code); +} + +/* + * Code retrieval + */ + +#define ADDR_CODE_READ(block) \ + size_t _code_size; \ + const int8_t *_code = buffer_get_data((block)->data, &_code_size) + +#define ADDR_CODE_AT(address) \ + ((int8_t)(_code[*address])) +#define ADDR_DATA_AT(address) \ + ((uint8_t)(_code[*address])) +#define ADDR_POINTER(address) \ + ((const int8_t *)(&_code[*address])) + +#define ADDR_BYTES_LEFT(address) \ + ((*address) > _code_size ? 0 : ((_code_size) - (*address))) +#define ADDR_JUMP(address, offset) \ + (*address) += offset + +/* Literals */ + +bool sieve_binary_read_byte(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *byte_r) +{ + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 1) { + if (byte_r != NULL) + *byte_r = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + return TRUE; + } + + if (byte_r != NULL) + *byte_r = 0; + return FALSE; +} + +bool sieve_binary_read_code(struct sieve_binary_block *sblock, + sieve_size_t *address, signed int *code_r) +{ + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 1) { + if (code_r != NULL) + *code_r = ADDR_CODE_AT(address); + ADDR_JUMP(address, 1); + + return TRUE; + } + + if (code_r != NULL) + *code_r = 0; + return FALSE; +} + + +bool sieve_binary_read_offset(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_offset_t *offset_r) +{ + sieve_offset_t offs = 0; + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 4) { + int i; + + for (i = 0; i < 4; i++) { + offs = (offs << 8) + ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + } + + if (offset_r != NULL) + *offset_r = offs; + + return TRUE; + } + return FALSE; +} + +/* FIXME: might need negative numbers in the future */ +bool sieve_binary_read_integer(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_number_t *int_r) +{ + int bits = sizeof(sieve_number_t) * 8; + sieve_number_t integer = 0; + + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) == 0) + return FALSE; + + /* Read first integer bytes [1xxxxxxx] */ + while ((ADDR_DATA_AT(address) & 0x80) > 0) { + if (ADDR_BYTES_LEFT(address) > 0 && bits > 0) { + integer |= ADDR_DATA_AT(address) & 0x7F; + ADDR_JUMP(address, 1); + + /* Each byte encodes 7 bits of the integer */ + integer <<= 7; + bits -= 7; + } else { + /* This is an error */ + return FALSE; + } + } + + /* Read last byte [0xxxxxxx] */ + integer |= ADDR_DATA_AT(address) & 0x7F; + ADDR_JUMP(address, 1); + + if (int_r != NULL) + *int_r = integer; + return TRUE; +} + +bool sieve_binary_read_string(struct sieve_binary_block *sblock, + sieve_size_t *address, string_t **str_r) +{ + unsigned int strlen = 0; + const char *strdata; + + ADDR_CODE_READ(sblock); + + if (!sieve_binary_read_unsigned(sblock, address, &strlen)) + return FALSE; + + if (strlen > ADDR_BYTES_LEFT(address)) + return FALSE; + + strdata = (const char *)ADDR_POINTER(address); + ADDR_JUMP(address, strlen); + + if (ADDR_CODE_AT(address) != 0) + return FALSE; + + if (str_r != NULL) + *str_r = t_str_new_const(strdata, strlen); + + ADDR_JUMP(address, 1); + + return TRUE; +} + +bool sieve_binary_read_extension(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *offset_r, + const struct sieve_extension **ext_r) +{ + unsigned int code; + unsigned int offset = *offset_r; + const struct sieve_extension *ext = NULL; + + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) == 0) + return FALSE; + + *offset_r = code = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + if (code >= offset) { + ext = sieve_binary_extension_get_by_index(sblock->sbin, + (code - offset)); + if (ext == NULL) + return FALSE; + } + + if (ext_r != NULL) + *ext_r = ext; + return TRUE; +} + +const void * +sieve_binary_read_extension_object(struct sieve_binary_block *sblock, + sieve_size_t *address, + const struct sieve_extension_objects *objs) +{ + unsigned int code; + + ADDR_CODE_READ(sblock); + + if (objs->count == 0) + return NULL; + if (objs->count == 1) + return objs->objects; + if (ADDR_BYTES_LEFT(address) == 0) + return NULL; + + code = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + if (code >= objs->count) + return NULL; + return ((const void *const *)objs->objects)[code]; +} |