diff options
Diffstat (limited to 'src/reader.c')
-rw-r--r-- | src/reader.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/src/reader.c b/src/reader.c new file mode 100644 index 0000000..ba04284 --- /dev/null +++ b/src/reader.c @@ -0,0 +1,700 @@ +/* + * Author Jerry Lundström <jerry@dns-oarc.net> + * Copyright (c) 2019, OARC, Inc. + * All rights reserved. + * + * This file is part of the dnswire library. + * + * dnswire library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dnswire library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with dnswire library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "dnswire/reader.h" +#include "dnswire/trace.h" + +#include <assert.h> +#include <stdlib.h> + +const char* const dnswire_reader_state_string[] = { + "reading_control", + "decoding_control", + "encoding_accept", + "writing_accept", + "reading", + "decoding", + "encoding_finish", + "writing_finish", + "done", +}; + +#define __state(h, s) \ + __trace("state %s => %s", dnswire_reader_state_string[(h)->state], dnswire_reader_state_string[s]); \ + (h)->state = s; + +static struct dnswire_reader _defaults = { + .state = dnswire_reader_reading_control, + + .decoder = DNSWIRE_DECODER_INITIALIZER, + .buf = 0, + .size = DNSWIRE_DEFAULT_BUF_SIZE, + .inc = DNSWIRE_DEFAULT_BUF_SIZE, + .max = DNSWIRE_MAXIMUM_BUF_SIZE, + .at = 0, + .left = 0, + .pushed = 0, + + .encoder = DNSWIRE_ENCODER_INITIALIZER, + .write_buf = 0, + .write_size = DNSWIRE_DEFAULT_BUF_SIZE, + .write_inc = DNSWIRE_DEFAULT_BUF_SIZE, + .write_max = DNSWIRE_MAXIMUM_BUF_SIZE, + .write_at = 0, + .write_left = 0, + + .allow_bidirectional = false, + .is_bidirectional = false, +}; + +enum dnswire_result dnswire_reader_init(struct dnswire_reader* handle) +{ + assert(handle); + + *handle = _defaults; + + if (!(handle->buf = malloc(handle->size))) { + return dnswire_error; + } + + return dnswire_ok; +} + +enum dnswire_result dnswire_reader_allow_bidirectional(struct dnswire_reader* handle, bool allow_bidirectional) +{ + assert(handle); + + if (allow_bidirectional) { + if (!handle->write_buf) { + if (!(handle->write_buf = malloc(handle->write_size))) { + return dnswire_error; + } + } + + handle->encoder.state = dnswire_encoder_control_accept; + } else { + handle->encoder.state = dnswire_encoder_control_start; + } + + handle->allow_bidirectional = allow_bidirectional; + + return dnswire_ok; +} + +enum dnswire_result dnswire_reader_set_bufsize(struct dnswire_reader* handle, size_t size) +{ + assert(handle); + assert(size); + assert(handle->buf); + + if (handle->left > size) { + // we got data and it doesn't fit in the new size + return dnswire_error; + } + if (size > handle->max) { + // don't expand over max + return dnswire_error; + } + + if (handle->at + handle->left > size) { + // move what's left to the start + if (handle->left) { + memmove(handle->buf, &handle->buf[handle->at], handle->left); + } + handle->at = 0; + } + + uint8_t* buf = realloc(handle->buf, size); + if (!buf) { + return dnswire_error; + } + + handle->buf = buf; + handle->size = size; + + return dnswire_ok; +} + +enum dnswire_result dnswire_reader_set_bufinc(struct dnswire_reader* handle, size_t inc) +{ + assert(handle); + assert(inc); + + handle->inc = inc; + + return dnswire_ok; +} + +enum dnswire_result dnswire_reader_set_bufmax(struct dnswire_reader* handle, size_t max) +{ + assert(handle); + assert(max); + + if (max < handle->size) { + return dnswire_error; + } + + handle->max = max; + + return dnswire_ok; +} + +static enum dnswire_result _encoding(struct dnswire_reader* handle) +{ + enum dnswire_result res; + + while (1) { + res = dnswire_encoder_encode(&handle->encoder, &handle->write_buf[handle->write_at], handle->write_size - handle->write_at); + __trace("encode %s", dnswire_result_string[res]); + + switch (res) { + case dnswire_ok: + case dnswire_again: + case dnswire_endofdata: + handle->write_at += dnswire_encoder_encoded(handle->encoder); + handle->write_left += dnswire_encoder_encoded(handle->encoder); + break; + + case dnswire_need_more: { + if (handle->write_size >= handle->write_max) { + // already at max size and it's not enough + return dnswire_error; + } + + // no space left, expand + size_t size = handle->write_size + handle->write_inc > handle->write_max ? handle->write_max : handle->write_size + handle->write_inc; + uint8_t* buf = realloc(handle->write_buf, size); + if (!buf) { + return dnswire_error; + } + handle->write_buf = buf; + handle->write_size = size; + continue; + } + default: + break; + } + break; + } + return res; +} + +enum dnswire_result dnswire_reader_push(struct dnswire_reader* handle, const uint8_t* data, size_t len, uint8_t* out_data, size_t* out_len) +{ + assert(handle); + assert(data); + assert(handle->buf); + assert(!handle->allow_bidirectional || out_data); + assert(!handle->allow_bidirectional || out_len); + + handle->pushed = 0; + size_t out_len_orig = 0; + if (out_len) { + out_len_orig = *out_len; + *out_len = 0; + } + + switch (handle->state) { + case dnswire_reader_reading_control: + if (!len) { + return dnswire_need_more; + } + // if the space we have left is less then len we only move that amount + handle->pushed = handle->size - handle->at - handle->left < len ? handle->size - handle->at - handle->left : len; + // if we can't add more we fallthrough and let it decode some or expand the buffer + if (handle->pushed) { + memcpy(&handle->buf[handle->at + handle->left], data, handle->pushed); + __trace("%s", __printable_string(&handle->buf[handle->at + handle->left], handle->pushed)); + handle->left += handle->pushed; + } + __state(handle, dnswire_reader_decoding_control); + // fallthrough + + case dnswire_reader_decoding_control: + switch (dnswire_decoder_decode(&handle->decoder, &handle->buf[handle->at], handle->left)) { + case dnswire_bidirectional: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading_control); + } + + if (!handle->allow_bidirectional) { + return dnswire_error; + } + + handle->is_bidirectional = true; + + if (!handle->decoder.ready_support_dnstap_protobuf) { + return dnswire_error; + } + + __state(handle, dnswire_reader_encoding_accept); + return dnswire_again; + + case dnswire_again: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading_control); + } + return dnswire_again; + + case dnswire_need_more: + if (handle->left < handle->size) { + // still space left to fill + if (handle->at) { + // move what's left to the start + if (handle->left) { + memmove(handle->buf, &handle->buf[handle->at], handle->left); + } + handle->at = 0; + } + } else if (handle->size < handle->max) { + // no space left, expand + size_t size = handle->size + handle->inc > handle->max ? handle->max : handle->size + handle->inc; + uint8_t* buf = realloc(handle->buf, size); + if (!buf) { + return dnswire_error; + } + handle->buf = buf; + handle->size = size; + } else { + // already at max size, and full + return dnswire_error; + } + __state(handle, dnswire_reader_reading_control); + if (len && handle->pushed < len) { + // we had more to push but won't loop, call again please + return dnswire_again; + } + return dnswire_need_more; + + case dnswire_have_dnstap: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (handle->left) { + __state(handle, dnswire_reader_decoding); + } else { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_have_dnstap; + + case dnswire_endofdata: + if (handle->is_bidirectional) { + __state(handle, dnswire_reader_encoding_finish); + return dnswire_again; + } + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + + default: + break; + } + return dnswire_error; + + case dnswire_reader_encoding_accept: + switch (_encoding(handle)) { + case dnswire_again: + __state(handle, dnswire_reader_writing_accept); + break; + // fallthrough + + default: + return dnswire_error; + } + + case dnswire_reader_writing_accept: + // if what we have left to write is less then the space available, we write it all + assert(out_len); + *out_len = handle->write_left < out_len_orig ? handle->write_left : out_len_orig; + memcpy(out_data, &handle->write_buf[handle->write_at - handle->write_left], *out_len); + __trace("pushed %zu", *out_len); + handle->write_left -= *out_len; + __trace("left %zu", handle->write_left); + if (!handle->write_left) { + handle->write_at = 0; + __state(handle, dnswire_reader_reading_control); + } + return dnswire_again; + + case dnswire_reader_reading: + if (!len) { + return dnswire_need_more; + } + // if the space we have left is less then len we only move that amount + handle->pushed = handle->size - handle->at - handle->left < len ? handle->size - handle->at - handle->left : len; + // if we can't add more we fallthrough and let it decode some or expand the buffer + if (handle->pushed) { + memcpy(&handle->buf[handle->at + handle->left], data, handle->pushed); + __trace("%s", __printable_string(&handle->buf[handle->at + handle->left], handle->pushed)); + handle->left += handle->pushed; + } + __state(handle, dnswire_reader_decoding); + // fallthrough + + case dnswire_reader_decoding: + switch (dnswire_decoder_decode(&handle->decoder, &handle->buf[handle->at], handle->left)) { + case dnswire_again: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_again; + + case dnswire_need_more: + if (handle->left < handle->size) { + // still space left to fill + if (handle->at) { + // move what's left to the start + if (handle->left) { + memmove(handle->buf, &handle->buf[handle->at], handle->left); + } + handle->at = 0; + } + } else if (handle->size < handle->max) { + // no space left, expand + size_t size = handle->size + handle->inc > handle->max ? handle->max : handle->size + handle->inc; + uint8_t* buf = realloc(handle->buf, size); + if (!buf) { + return dnswire_error; + } + handle->buf = buf; + handle->size = size; + } else { + // already at max size, and full + return dnswire_error; + } + __state(handle, dnswire_reader_reading); + if (len && handle->pushed < len) { + // we had more to push but won't loop, call again please + return dnswire_again; + } + return dnswire_need_more; + + case dnswire_have_dnstap: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_have_dnstap; + + case dnswire_endofdata: + if (handle->is_bidirectional) { + __state(handle, dnswire_reader_encoding_finish); + return dnswire_again; + } + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + + default: + break; + } + return dnswire_error; + + case dnswire_reader_encoding_finish: + switch (_encoding(handle)) { + case dnswire_endofdata: + __state(handle, dnswire_reader_writing_finish); + break; + // fallthrough + + default: + return dnswire_error; + } + + case dnswire_reader_writing_finish: + // if what we have left to write is less then the space available, we write it all + assert(out_len); + *out_len = handle->write_left < out_len_orig ? handle->write_left : out_len_orig; + memcpy(out_data, &handle->write_buf[handle->write_at - handle->write_left], *out_len); + __trace("pushed %zu", *out_len); + handle->write_left -= *out_len; + __trace("left %zu", handle->write_left); + if (!handle->write_left) { + handle->write_at = 0; + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + } + return dnswire_again; + + case dnswire_reader_done: + break; + } + + return dnswire_error; +} + +enum dnswire_result dnswire_reader_read(struct dnswire_reader* handle, int fd) +{ + assert(handle); + assert(handle->buf); + + switch (handle->state) { + case dnswire_reader_reading_control: { + ssize_t nread = read(fd, &handle->buf[handle->at + handle->left], handle->size - handle->at - handle->left); + if (nread < 0) { + // TODO + return dnswire_error; + } else if (!nread) { + // TODO + return dnswire_error; + } + __trace("%s", __printable_string(&handle->buf[handle->at + handle->left], nread)); + handle->left += nread; + __state(handle, dnswire_reader_decoding_control); + // fallthrough + } + + case dnswire_reader_decoding_control: + switch (dnswire_decoder_decode(&handle->decoder, &handle->buf[handle->at], handle->left)) { + case dnswire_bidirectional: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading_control); + } + + if (!handle->allow_bidirectional) { + return dnswire_error; + } + + handle->is_bidirectional = true; + + if (!handle->decoder.ready_support_dnstap_protobuf) { + return dnswire_error; + } + + __state(handle, dnswire_reader_encoding_accept); + return dnswire_again; + + case dnswire_again: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading_control); + } + return dnswire_again; + + case dnswire_need_more: + if (handle->left < handle->size) { + // still space left to fill + if (handle->at) { + // move what's left to the start + if (handle->left) { + memmove(handle->buf, &handle->buf[handle->at], handle->left); + } + handle->at = 0; + } + } else if (handle->size < handle->max) { + // no space left, expand + size_t size = handle->size + handle->inc > handle->max ? handle->max : handle->size + handle->inc; + uint8_t* buf = realloc(handle->buf, size); + if (!buf) { + return dnswire_error; + } + handle->buf = buf; + handle->size = size; + } else { + // already at max size, and full + return dnswire_error; + } + __state(handle, dnswire_reader_reading_control); + return dnswire_need_more; + + case dnswire_have_dnstap: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (handle->left) { + __state(handle, dnswire_reader_decoding); + } else { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_have_dnstap; + + case dnswire_endofdata: + if (handle->is_bidirectional) { + __state(handle, dnswire_reader_encoding_finish); + return dnswire_again; + } + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + + default: + break; + } + return dnswire_error; + + case dnswire_reader_encoding_accept: + switch (_encoding(handle)) { + case dnswire_again: + __state(handle, dnswire_reader_writing_accept); + break; + // fallthrough + + default: + return dnswire_error; + } + + case dnswire_reader_writing_accept: { + ssize_t nwrote = write(fd, &handle->write_buf[handle->write_at - handle->write_left], handle->write_left); + __trace("wrote %zd", nwrote); + if (nwrote < 0) { + // TODO + return dnswire_error; + } else if (!nwrote) { + // TODO + return dnswire_error; + } + + handle->write_left -= nwrote; + __trace("left %zu", handle->write_left); + if (!handle->write_left) { + handle->write_at = 0; + __state(handle, dnswire_reader_reading_control); + } + return dnswire_again; + } + + case dnswire_reader_reading: { + ssize_t nread = read(fd, &handle->buf[handle->at + handle->left], handle->size - handle->at - handle->left); + if (nread < 0) { + // TODO + return dnswire_error; + } else if (!nread) { + // TODO + return dnswire_error; + } + __trace("%s", __printable_string(&handle->buf[handle->at + handle->left], nread)); + handle->left += nread; + __state(handle, dnswire_reader_decoding); + // fallthrough + } + + case dnswire_reader_decoding: + switch (dnswire_decoder_decode(&handle->decoder, &handle->buf[handle->at], handle->left)) { + case dnswire_again: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_again; + + case dnswire_need_more: + if (handle->left < handle->size) { + // still space left to fill + if (handle->at) { + // move what's left to the start + if (handle->left) { + memmove(handle->buf, &handle->buf[handle->at], handle->left); + } + handle->at = 0; + } + } else if (handle->size < handle->max) { + // no space left, expand + size_t size = handle->size + handle->inc > handle->max ? handle->max : handle->size + handle->inc; + uint8_t* buf = realloc(handle->buf, size); + if (!buf) { + return dnswire_error; + } + handle->buf = buf; + handle->size = size; + } else { + // already at max size, and full + return dnswire_error; + } + __state(handle, dnswire_reader_reading); + return dnswire_need_more; + + case dnswire_have_dnstap: + handle->at += dnswire_decoder_decoded(handle->decoder); + handle->left -= dnswire_decoder_decoded(handle->decoder); + if (!handle->left) { + handle->at = 0; + __state(handle, dnswire_reader_reading); + } + return dnswire_have_dnstap; + + case dnswire_endofdata: + if (handle->is_bidirectional) { + __state(handle, dnswire_reader_encoding_finish); + return dnswire_again; + } + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + + default: + break; + } + return dnswire_error; + + case dnswire_reader_encoding_finish: + switch (_encoding(handle)) { + case dnswire_endofdata: + __state(handle, dnswire_reader_writing_finish); + break; + // fallthrough + + default: + return dnswire_error; + } + + case dnswire_reader_writing_finish: { + ssize_t nwrote = write(fd, &handle->write_buf[handle->write_at - handle->write_left], handle->write_left); + __trace("wrote %zd", nwrote); + if (nwrote < 0) { + // TODO + return dnswire_error; + } else if (!nwrote) { + // TODO + return dnswire_error; + } + + handle->write_left -= nwrote; + __trace("left %zu", handle->write_left); + if (!handle->write_left) { + handle->write_at = 0; + __state(handle, dnswire_reader_done); + return dnswire_endofdata; + } + return dnswire_again; + } + + case dnswire_reader_done: + break; + } + + return dnswire_error; +} |