1
0
Fork 0
dnswire/src/reader.c
Daniel Baumann 59def95384
Merging upstream version 0.4.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-09 06:37:39 +01:00

700 lines
23 KiB
C

/*
* Author Jerry Lundström <jerry@dns-oarc.net>
* Copyright (c) 2019-2023, 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;
}