/* * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * * 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. See . * * 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. */ #include "vici_builder.h" #include typedef struct private_vici_builder_t private_vici_builder_t; /** * Private data of an vici_builder_t object. */ struct private_vici_builder_t { /** * Public vici_builder_t interface. */ vici_builder_t public; /** * Writer for elements */ bio_writer_t *writer; /** * Errors encountered */ u_int error; /** * Section nesting level */ u_int section; /** * In list element? */ bool list; }; METHOD(vici_builder_t, add, void, private_vici_builder_t *this, vici_type_t type, ...) { va_list args; char *name = NULL; chunk_t value = chunk_empty; va_start(args, type); switch (type) { case VICI_SECTION_END: case VICI_LIST_END: case VICI_END: break; case VICI_LIST_START: case VICI_SECTION_START: name = va_arg(args, char*); break; case VICI_KEY_VALUE: name = va_arg(args, char*); value = va_arg(args, chunk_t); break; case VICI_LIST_ITEM: value = va_arg(args, chunk_t); break; default: va_end(args); this->error++; return; } va_end(args); if (value.len > 0xffff) { DBG1(DBG_ENC, "vici value exceeds size limit (%zu > %u)", value.len, 0xffff); this->error++; return; } if (!vici_verify_type(type, this->section, this->list)) { this->error++; return; } if (type != VICI_END) { this->writer->write_uint8(this->writer, type); } switch (type) { case VICI_SECTION_START: this->writer->write_data8(this->writer, chunk_from_str(name)); this->section++; break; case VICI_SECTION_END: this->section--; break; case VICI_KEY_VALUE: this->writer->write_data8(this->writer, chunk_from_str(name)); this->writer->write_data16(this->writer, value); break; case VICI_LIST_START: this->writer->write_data8(this->writer, chunk_from_str(name)); this->list = TRUE; break; case VICI_LIST_ITEM: this->writer->write_data16(this->writer, value); break; case VICI_LIST_END: this->list = FALSE; break; default: this->error++; break; } } /** * Add a list item or a key/value, if key given */ static void vadd_kv_or_li(private_vici_builder_t *this, char *key, char *fmt, va_list args) { u_char buf[512]; chunk_t value; ssize_t len; va_list copy; va_copy(copy, args); len = vsnprintf(buf, sizeof(buf), fmt, copy); va_end(copy); if (len >= sizeof(buf)) { value = chunk_alloc(len + 1); len = vsnprintf(value.ptr, value.len, fmt, args); } else { value = chunk_create(buf, len); } if (len < 0) { DBG1(DBG_ENC, "vici builder format print failed"); this->error++; } else { if (key) { add(this, VICI_KEY_VALUE, key, value); } else { add(this, VICI_LIST_ITEM, value); } } if (value.ptr != buf) { free(value.ptr); } } METHOD(vici_builder_t, vadd_kv, void, private_vici_builder_t *this, char *key, char *fmt, va_list args) { vadd_kv_or_li(this, key, fmt, args); } METHOD(vici_builder_t, add_kv, void, private_vici_builder_t *this, char *key, char *fmt, ...) { va_list args; va_start(args, fmt); vadd_kv(this, key, fmt, args); va_end(args); } METHOD(vici_builder_t, vadd_li, void, private_vici_builder_t *this, char *fmt, va_list args) { vadd_kv_or_li(this, NULL, fmt, args); } METHOD(vici_builder_t, add_li, void, private_vici_builder_t *this, char *fmt, ...) { va_list args; va_start(args, fmt); vadd_li(this, fmt, args); va_end(args); } METHOD(vici_builder_t, begin_section, void, private_vici_builder_t *this, char *name) { add(this, VICI_SECTION_START, name); } METHOD(vici_builder_t, end_section, void, private_vici_builder_t *this) { add(this, VICI_SECTION_END); } METHOD(vici_builder_t, begin_list, void, private_vici_builder_t *this, char *name) { add(this, VICI_LIST_START, name); } METHOD(vici_builder_t, end_list, void, private_vici_builder_t *this) { add(this, VICI_LIST_END); } METHOD(vici_builder_t, destroy, void, private_vici_builder_t *this) { this->writer->destroy(this->writer); free(this); } METHOD(vici_builder_t, finalize, vici_message_t*, private_vici_builder_t *this) { vici_message_t *product; if (this->error || this->section || this->list) { DBG1(DBG_ENC, "vici builder error: %u errors (section: %u, list %u)", this->error, this->section, this->list); destroy(this); return NULL; } product = vici_message_create_from_data( this->writer->extract_buf(this->writer), TRUE); destroy(this); return product; } /** * See header */ vici_builder_t *vici_builder_create() { private_vici_builder_t *this; INIT(this, .public = { .add = _add, .add_kv = _add_kv, .vadd_kv = _vadd_kv, .add_li = _add_li, .vadd_li = _vadd_li, .begin_section = _begin_section, .end_section = _end_section, .begin_list = _begin_list, .end_list = _end_list, .finalize = _finalize, .destroy = _destroy, }, .writer = bio_writer_create(0), ); return &this->public; }