summaryrefslogtreecommitdiffstats
path: root/src/reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reader.c')
-rw-r--r--src/reader.c700
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;
+}