strongswan/src/libcharon/plugins/vici/vici_builder.c

284 lines
5.4 KiB
C

/*
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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 <bio/bio_writer.h>
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);
value.len = len;
}
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;
}