Adding upstream version 0.2.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-09 06:31:09 +01:00
parent 23dbed389d
commit a84695b834
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
79 changed files with 7127 additions and 0 deletions

48
examples/Makefile.am Normal file
View file

@ -0,0 +1,48 @@
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
CLEANFILES =
AM_CFLAGS = -I$(top_srcdir)/src \
$(tinyframe_CFLAGS) \
$(protobuf_c_CFLAGS)
AM_LDFLAGS = $(protobuf_c_LIBS) \
$(tinyframe_LIBS) -static
EXTRA_DIST = print_dnstap.c create_dnstap.c
if BUILD_EXAMPLES
noinst_PROGRAMS = reader writer sender receiver reader_sender
reader_SOURCES = reader.c
reader_LDADD = ../src/libdnswire.la
writer_SOURCES = writer.c
writer_LDADD = ../src/libdnswire.la
sender_SOURCES = sender.c
sender_LDADD = ../src/libdnswire.la
receiver_SOURCES = receiver.c
receiver_LDADD = ../src/libdnswire.la
reader_sender_SOURCES = reader_sender.c
reader_sender_LDADD = ../src/libdnswire.la
if HAVE_LIBUV
AM_CFLAGS += -I$(uv_CFLAGS)
AM_LDFLAGS += $(uv_LIBS)
noinst_PROGRAMS += daemon_sender_uv client_receiver_uv
daemon_sender_uv_SOURCES = daemon_sender_uv.c
daemon_sender_uv_LDADD = ../src/libdnswire.la
client_receiver_uv_SOURCES = client_receiver_uv.c
client_receiver_uv_LDADD = ../src/libdnswire.la
endif
endif

114
examples/README.md Normal file
View file

@ -0,0 +1,114 @@
# Examples
- `reader`: Example of reading DNSTAP from a file and printing it's content, using `dnswire_reader` (unidirectional mode)
- `writer`: Example of constructing a DNSTAP message and writing it to a file, using `dnswire_writer` (unidirectional mode)
- `receiver`: Example of receiving a DNSTAP message over a TCP connection and printing it's content, using `dnswire_reader` (bidirectional mode)
- `sender`: Example of constructing a DNSTAP message and sending it over a TCP connection, using `dnswire_writer` (bidirectional mode)
- `daemon_sender_uv`: Example of a daemon that will continuously send DNSTAP messages to connected clients (unidirectional mode), using the event engine `libuv` and `dnstap_encode_protobuf` along with `tinyframe` to encode once and send to many
- `client_receiver_uv`: Example of a client that will receive DNSTAP message from the daemon (unidirectional mode), using the event engine `libuv` and `dnswire_reader` with the buffer push interface
- `reader_sender`: Example of a reader that read DNSTAP from a file (unidirectional mode) and then sends the DNSTAP messages over a TCP connection (bidirectional mode)
## receiver and sender
These examples uses the way of connecting as implemented in the `fstrm`
library, the receiver listens for connections by the sender and the sender
connects to the receiver.
```
$ ./receiver 127.0.0.1 5353
socket
bind
listen
accept
receiving...
---- dnstap
identity: sender
version: 0.1.0
message:
type: MESSAGE
query_time: 1574765731.199945162
response_time: 1574765731.199945362
socket_family: INET
socket_protocol: UDP
query_address: 127.0.0.1
query_port: 12345
response_address: 127.0.0.1
response_port: 53
query_message_length: 27
query_message: dns_wire_format_placeholder
response_message_length: 27
response_message: dns_wire_format_placeholder
----
stopped
```
```
$ ./sender 127.0.0.1 5353
socket
connect
sending...
sent, stopping...
stopped
```
## daemon_sender_uv and client_receiver_uv
These examples works in the reverse way compared to `receiver` and `sender`,
and maybe a more traditional way, the daemon listens and accepts connections
from new clients, and will continuously send messages to established clients
that are ready to receive them.
```
$ ./daemon_sender_uv 127.0.0.1 5353
client 1 connected
client 1: sending control start and content type
client 1: sending DNSTAP
client 1: sending DNSTAP
client 1 disconnected
```
```
$ ./client_receiver_uv 127.0.0.1 5353
received 42 bytes
got control start
got content type DNSTAP
received 133 bytes
---- dnstap
identity: daemon_sender_uv
version: 0.1.0
message:
type: MESSAGE
query_time: 1574257180.95619354
response_time: 1574257180.95619490
socket_family: INET
socket_protocol: UDP
query_address: 127.0.0.1
query_port: 12345
response_address: 127.0.0.1
response_port: 53
query_message_length: 27
query_message: dns_wire_format_placeholder
response_message_length: 27
response_message: dns_wire_format_placeholder
----
received 133 bytes
---- dnstap
identity: daemon_sender_uv
version: 0.1.0
message:
type: MESSAGE
query_time: 1574257181.96381443
response_time: 1574257181.96381557
socket_family: INET
socket_protocol: UDP
query_address: 127.0.0.1
query_port: 12345
response_address: 127.0.0.1
response_port: 53
query_message_length: 27
query_message: dns_wire_format_placeholder
response_message_length: 27
response_message: dns_wire_format_placeholder
----
^C
```

View file

@ -0,0 +1,134 @@
#include <dnswire/reader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
#include <stdbool.h>
#include "print_dnstap.c"
#define BUF_SIZE 4096
uv_loop_t* loop;
uv_tcp_t sock;
uv_connect_t conn;
char rbuf[BUF_SIZE];
struct dnswire_reader reader;
void client_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
buf->base = rbuf;
buf->len = sizeof(rbuf);
}
void client_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{
if (nread > 0) {
/*
* We now push all the data we got to the reader.
*/
size_t pushed = 0;
while (pushed < nread) {
enum dnswire_result res = dnswire_reader_push(&reader, (uint8_t*)&buf->base[pushed], nread - pushed, 0, 0);
pushed += dnswire_reader_pushed(reader);
switch (res) {
case dnswire_have_dnstap:
/*
* We got a DNSTAP message, lets print it!
*/
print_dnstap(dnswire_reader_dnstap(reader));
break;
case dnswire_again:
case dnswire_need_more:
break;
case dnswire_endofdata:
/*
* The remote end sent a control stop or finish.
*/
uv_close((uv_handle_t*)handle, 0);
return;
default:
fprintf(stderr, "dnswire_reader_fread() error\n");
uv_close((uv_handle_t*)handle, 0);
return;
}
}
}
if (nread < 0) {
if (nread != UV_EOF) {
fprintf(stderr, "client_read() error: %s\n", uv_err_name(nread));
} else {
printf("disconnected\n");
}
uv_close((uv_handle_t*)handle, 0);
}
}
void on_connect(uv_connect_t* req, int status)
{
/*
* We have connected to the sender, check that there was no errors
* and start receiving incoming frames.
*/
if (status < 0) {
fprintf(stderr, "on_connect() error: %s\n", uv_strerror(status));
return;
}
uv_read_start((uv_stream_t*)&sock, client_alloc_buffer, client_read);
}
int main(int argc, const char* argv[])
{
if (argc < 3) {
fprintf(stderr, "usage: client_receiver_uv <IP> <port>\n");
return 1;
}
/*
* We first initialize the reader and check that it can allocate the
* buffers it needs.
*/
if (dnswire_reader_init(&reader) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire reader\n");
return 1;
}
/*
* We set this reader to reject bidirectional communication.
*/
if (dnswire_reader_allow_bidirectional(&reader, false) != dnswire_ok) {
fprintf(stderr, "Unable to deny bidirectional communication\n");
return 1;
}
/*
* We setup a TCP client using libuv and connect to the given server.
*/
struct sockaddr_storage addr;
int port = atoi(argv[2]);
if (strchr(argv[1], ':')) {
uv_ip6_addr(argv[1], port, (struct sockaddr_in6*)&addr);
} else {
uv_ip4_addr(argv[1], port, (struct sockaddr_in*)&addr);
}
loop = uv_default_loop();
uv_tcp_init(loop, &sock);
uv_tcp_connect(&conn, &sock, (const struct sockaddr*)&addr, on_connect);
return uv_run(loop, UV_RUN_DEFAULT);
}

99
examples/create_dnstap.c Normal file
View file

@ -0,0 +1,99 @@
#include <dnswire/version.h>
#include <dnswire/dnstap.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
static char dns_wire_format_placeholder[] = "dns_wire_format_placeholder";
static inline struct dnstap create_dnstap(const char* identity)
{
/*
* Now we initialize a DNSTAP message.
*/
struct dnstap d = DNSTAP_INITIALIZER;
/*
* DNSTAP has a header with information about who constructed the
* message.
*/
dnstap_set_identity_string(d, identity);
dnstap_set_version_string(d, DNSWIRE_VERSION_STRING);
/*
* Now we specify that this is a DNSTAP message, this is the one that
* holds the DNS.
*/
dnstap_set_type(d, DNSTAP_TYPE_MESSAGE);
/*
* The message can have different types, we specify this is a query
* made by a tool.
*/
dnstap_message_set_type(d, DNSTAP_MESSAGE_TYPE_TOOL_QUERY);
/*
* As most DNS comes over the network we can specify over what protocol,
* where from, to whom it came to and when it happened.
*
* Even if all fields are optional and there is no restriction on how
* many or how few you set, there is a recommended way of filling in
* the messages based on the message type.
*
* Please see the description of this at the bottom of
* `src/dnstap.pb/dnstap.proto` or
* <https://github.com/dnstap/dnstap.pb/blob/master/dnstap.proto>.
*/
dnstap_message_set_socket_family(d, DNSTAP_SOCKET_FAMILY_INET);
dnstap_message_set_socket_protocol(d, DNSTAP_SOCKET_PROTOCOL_UDP);
unsigned char query_address[sizeof(struct in_addr)];
if (inet_pton(AF_INET, "127.0.0.1", query_address) != 1) {
fprintf(stderr, "inet_pton(127.0.0.1) failed: %s\n", strerror(errno));
} else {
dnstap_message_set_query_address(d, query_address, sizeof(query_address));
}
dnstap_message_set_query_port(d, 12345);
struct timespec query_time = { 0, 0 };
clock_gettime(CLOCK_REALTIME, &query_time);
dnstap_message_set_query_time_sec(d, query_time.tv_sec);
dnstap_message_set_query_time_nsec(d, query_time.tv_nsec);
unsigned char response_address[sizeof(struct in_addr)];
if (inet_pton(AF_INET, "127.0.0.1", response_address) != 1) {
fprintf(stderr, "inet_pton(127.0.0.1) failed: %s\n", strerror(errno));
} else {
dnstap_message_set_response_address(d, response_address, sizeof(response_address));
}
dnstap_message_set_response_port(d, 53);
struct timespec response_time = { 0, 0 };
clock_gettime(CLOCK_REALTIME, &response_time);
dnstap_message_set_response_time_sec(d, response_time.tv_sec);
dnstap_message_set_response_time_nsec(d, response_time.tv_nsec);
/*
* If we also had the DNS wire format we could use it, now we fill it
* with a placeholder text.
*
* NOTE: This will be invalid DNS if the output file is used with a
* tool that uses the DNS messages.
*/
dnstap_message_set_query_message(d, dns_wire_format_placeholder, sizeof(dns_wire_format_placeholder) - 1);
dnstap_message_set_response_message(d, dns_wire_format_placeholder, sizeof(dns_wire_format_placeholder) - 1);
return d;
}

254
examples/daemon_sender_uv.c Normal file
View file

@ -0,0 +1,254 @@
#include <tinyframe/tinyframe.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
#include <errno.h>
#include <stdbool.h>
#include "create_dnstap.c"
#define BUF_SIZE 4096
enum client_state {
no_state,
writing_start,
started,
writing_frame,
};
struct client;
struct client {
struct client* next;
size_t id;
enum client_state state;
uv_tcp_t conn;
char rbuf[BUF_SIZE];
uv_write_t wreq;
uv_buf_t wbuf;
uint8_t buf[BUF_SIZE];
};
struct client* clients = 0;
size_t client_id = 1;
static char content_type[] = "protobuf:dnstap.Dnstap";
uv_loop_t* loop;
struct client* client_new()
{
struct client* c = malloc(sizeof(struct client));
if (c) {
c->conn.data = c;
c->next = clients;
c->id = client_id++;
c->state = no_state;
c->wbuf.base = (void*)c->buf;
clients = c;
}
return c;
}
void client_close(uv_handle_t* handle)
{
struct client* c = handle->data;
if (clients == c) {
clients = c->next;
} else {
struct client* prev = clients;
while (prev) {
if (prev->next == c) {
prev->next = c->next;
break;
}
prev = prev->next;
}
}
free(c);
}
void client_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
buf->base = ((struct client*)handle->data)->rbuf;
buf->len = BUF_SIZE;
}
void client_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf)
{
/*
* We discard any input from the client and only check for errors or
* if the connection was closed.
*/
if (nread < 0) {
if (nread != UV_EOF) {
fprintf(stderr, "client_read() error: %s\n", uv_err_name(nread));
} else {
printf("client %zu disconnected\n", ((struct client*)client->data)->id);
}
uv_close((uv_handle_t*)client, client_close);
}
}
void client_write(uv_write_t* req, int status)
{
/*
* After a write we check that there was no errors and then set the
* client in a state that allows `tick()` to send DNSTAP messages to it.
*/
if (status) {
fprintf(stderr, "client_write() error: %s\n", uv_strerror(status));
uv_close((uv_handle_t*)req->handle, client_close);
return;
}
((struct client*)req->handle->data)->state = started;
}
void on_new_connection(uv_stream_t* server, int status)
{
if (status < 0) {
fprintf(stderr, "on_new_connection() error: %s\n", uv_strerror(status));
return;
}
/*
* We have a new client connecting, create a client struct to hold the
* connection, accept it and send the control start frame.
*/
struct client* client = client_new();
if (!client) {
fprintf(stderr, "on_new_connection() out of memory\n");
return;
}
uv_tcp_init(loop, &client->conn);
if (uv_accept(server, (uv_stream_t*)&client->conn) == 0) {
printf("client %zu connected\n", client->id);
uv_read_start((uv_stream_t*)&client->conn, client_alloc_buffer, client_read);
struct tinyframe_writer writer = TINYFRAME_WRITER_INITIALIZER;
/*
* First we write, to the buffer, a control start with a content type
* control field for the DNSTAP protobuf content type.
*
* Then we send it.
*/
if (tinyframe_write_control_start(&writer, client->buf, BUF_SIZE, content_type, sizeof(content_type) - 1) != tinyframe_ok) {
fprintf(stderr, "tinyframe_write_control_start() failed\n");
uv_close((uv_handle_t*)&client->conn, client_close);
return;
}
printf("client %zu: sending control start and content type\n", client->id);
client->wbuf.len = writer.bytes_wrote;
uv_write((uv_write_t*)&client->wreq, (uv_stream_t*)&client->conn, &client->wbuf, 1, client_write);
client->state = writing_start;
} else {
uv_close((uv_handle_t*)&client->conn, client_close);
}
}
/*
* This function is called every second and will create a DNSTAP message
* and send it to all available clients.
*/
void tick(uv_timer_t* handle)
{
/*
* Now we create a DNSTAP message.
*/
struct dnstap d = create_dnstap("daemon_sender_uv");
/*
* Now that the message is prepared we can begin encapsulating it in
* protobuf and Frame Streams.
*
* First we ask what the encoded size of the protobuf message would be
* and then we allocate a buffer with of that size plus the size of
* a Frame Streams frame header.
*
* Then we encode the DNSTAP message and put it after the frame header
* and call `tinyframe_set_header()` to set the header.
*/
size_t frame_len = dnstap_encode_protobuf_size(&d);
uint8_t frame[TINYFRAME_HEADER_SIZE + frame_len];
dnstap_encode_protobuf(&d, &frame[TINYFRAME_HEADER_SIZE]);
tinyframe_set_header(frame, frame_len);
if (sizeof(frame) > BUF_SIZE) {
fprintf(stderr, "frame larger the client's buffers\n");
exit(1);
}
/*
* We now loop over all the connected clients and send the message
* to those that are currently not busy.
*/
struct client* c = clients;
while (c) {
if (c->state == started) {
c->wbuf.len = sizeof(frame);
memcpy(c->buf, frame, sizeof(frame));
uv_write((uv_write_t*)&c->wreq, (uv_stream_t*)&c->conn, &c->wbuf, 1, client_write);
c->state = writing_frame;
printf("client %zu: sending DNSTAP\n", c->id);
}
c = c->next;
}
}
int main(int argc, const char* argv[])
{
if (argc < 3) {
fprintf(stderr, "usage: daemon_sender_uv <IP> <port>\n");
return 1;
}
/*
* We setup a TCP server using libuv and listen for connections,
* along with a timer that calls the function to send DNSTAP messages
* to all clients.
*/
struct sockaddr_storage addr;
int port = atoi(argv[2]);
if (strchr(argv[1], ':')) {
uv_ip6_addr(argv[1], port, (struct sockaddr_in6*)&addr);
} else {
uv_ip4_addr(argv[1], port, (struct sockaddr_in*)&addr);
}
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
int r = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
if (r) {
fprintf(stderr, "uv_listen() failed: %s\n", uv_strerror(r));
return 1;
}
uv_timer_t ticker;
uv_timer_init(loop, &ticker);
uv_timer_start(&ticker, tick, 1000, 1000);
return uv_run(loop, UV_RUN_DEFAULT);
}

122
examples/print_dnstap.c Normal file
View file

@ -0,0 +1,122 @@
#include <dnswire/dnstap.h>
#include <stdio.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
static const char* printable_string(const uint8_t* data, size_t len)
{
static char buf[512], hex;
size_t r = 0, w = 0;
while (r < len && w < sizeof(buf) - 1) {
if (isprint(data[r])) {
buf[w++] = data[r++];
} else {
if (w + 4 >= sizeof(buf) - 1) {
break;
}
buf[w++] = '\\';
buf[w++] = 'x';
hex = (data[r] & 0xf0) >> 4;
if (hex > 9) {
buf[w++] = 'a' + (hex - 10);
} else {
buf[w++] = '0' + hex;
}
hex = data[r++] & 0xf;
if (hex > 9) {
buf[w++] = 'a' + (hex - 10);
} else {
buf[w++] = '0' + hex;
}
}
}
if (w >= sizeof(buf)) {
buf[sizeof(buf) - 1] = 0;
} else {
buf[w] = 0;
}
return buf;
}
static const char* printable_ip_address(const uint8_t* data, size_t len)
{
static char buf[INET6_ADDRSTRLEN];
buf[0] = 0;
if (len == 4) {
inet_ntop(AF_INET, data, buf, sizeof(buf));
} else if (len == 16) {
inet_ntop(AF_INET6, data, buf, sizeof(buf));
}
return buf;
}
static void print_dnstap(const struct dnstap* d)
{
printf("---- dnstap\n");
/*
* Now we can print the available information in the message.
*/
if (dnstap_has_identity(*d)) {
printf("identity: %s\n", printable_string(dnstap_identity(*d), dnstap_identity_length(*d)));
}
if (dnstap_has_version(*d)) {
printf("version: %s\n", printable_string(dnstap_version(*d), dnstap_version_length(*d)));
}
if (dnstap_has_extra(*d)) {
printf("extra: %s\n", printable_string(dnstap_extra(*d), dnstap_extra_length(*d)));
}
if (dnstap_type(*d) == DNSTAP_TYPE_MESSAGE && dnstap_has_message(*d)) {
printf("message:\n type: %s\n", DNSTAP_MESSAGE_TYPE_STRING[dnstap_message_type(*d)]);
if (dnstap_message_has_query_time_sec(*d) && dnstap_message_has_query_time_nsec(*d)) {
printf(" query_time: %" PRIu64 ".%" PRIu32 "\n", dnstap_message_query_time_sec(*d), dnstap_message_query_time_nsec(*d));
}
if (dnstap_message_has_response_time_sec(*d) && dnstap_message_has_response_time_nsec(*d)) {
printf(" response_time: %" PRIu64 ".%" PRIu32 "\n", dnstap_message_response_time_sec(*d), dnstap_message_response_time_nsec(*d));
}
if (dnstap_message_has_socket_family(*d)) {
printf(" socket_family: %s\n", DNSTAP_SOCKET_FAMILY_STRING[dnstap_message_socket_family(*d)]);
}
if (dnstap_message_has_socket_protocol(*d)) {
printf(" socket_protocol: %s\n", DNSTAP_SOCKET_PROTOCOL_STRING[dnstap_message_socket_protocol(*d)]);
}
if (dnstap_message_has_query_address(*d)) {
printf(" query_address: %s\n", printable_ip_address(dnstap_message_query_address(*d), dnstap_message_query_address_length(*d)));
}
if (dnstap_message_has_query_port(*d)) {
printf(" query_port: %u\n", dnstap_message_query_port(*d));
}
if (dnstap_message_has_response_address(*d)) {
printf(" response_address: %s\n", printable_ip_address(dnstap_message_response_address(*d), dnstap_message_response_address_length(*d)));
}
if (dnstap_message_has_response_port(*d)) {
printf(" response_port: %u\n", dnstap_message_response_port(*d));
}
if (dnstap_message_has_query_zone(*d)) {
printf(" query_zone: %s\n", printable_string(dnstap_message_query_zone(*d), dnstap_message_query_zone_length(*d)));
}
if (dnstap_message_has_query_message(*d)) {
printf(" query_message_length: %lu\n", dnstap_message_query_message_length(*d));
printf(" query_message: %s\n", printable_string(dnstap_message_query_message(*d), dnstap_message_query_message_length(*d)));
}
if (dnstap_message_has_response_message(*d)) {
printf(" response_message_length: %lu\n", dnstap_message_response_message_length(*d));
printf(" response_message: %s\n", printable_string(dnstap_message_response_message(*d), dnstap_message_response_message_length(*d)));
}
}
printf("----\n");
}

65
examples/reader.c Normal file
View file

@ -0,0 +1,65 @@
#include <dnswire/reader.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "print_dnstap.c"
int main(int argc, const char* argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: reader <file>\n");
return 1;
}
/*
* First we open the given file and read all of the content.
*/
FILE* fp = fopen(argv[1], "r");
if (!fp) {
fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno));
return 1;
}
/*
* We now initialize the reader and check that it can allocate the
* buffers it needs.
*/
struct dnswire_reader reader;
int done = 0;
if (dnswire_reader_init(&reader) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire reader\n");
return 1;
}
/*
* We now loop until we have a DNSTAP message, the stream was stopped
* or we got an error.
*/
while (!done) {
switch (dnswire_reader_fread(&reader, fp)) {
case dnswire_have_dnstap:
print_dnstap(dnswire_reader_dnstap(reader));
break;
case dnswire_again:
case dnswire_need_more:
break;
case dnswire_endofdata:
done = 1;
break;
default:
fprintf(stderr, "dnswire_reader_fread() error\n");
done = 1;
}
}
dnswire_reader_destroy(reader);
fclose(fp);
return 0;
}

225
examples/reader_sender.c Normal file
View file

@ -0,0 +1,225 @@
#include <dnswire/reader.h>
#include <dnswire/writer.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
/*
* This is a combination of reader and simpler_sender, comments may
* be a bit off since they haven't been updated for this.
*/
int main(int argc, const char* argv[])
{
if (argc < 3) {
fprintf(stderr, "usage: reader_sender <file> [ <IP> <port> | <unix socket path> ]\n");
return 1;
}
/*
* First we open the given file and read all of the content.
*/
FILE* fp = fopen(argv[1], "r");
if (!fp) {
fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno));
return 1;
}
/*
* We now initialize the reader and check that it can allocate the
* buffers it needs.
*/
struct dnswire_reader reader;
int done = 0;
if (dnswire_reader_init(&reader) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire reader\n");
return 1;
}
/*
* We first initialize the writer and check that it can allocate the
* buffers it needs.
*/
struct dnswire_writer writer;
if (dnswire_writer_init(&writer) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire writer\n");
return 1;
}
/*
* Use bidirectional communication over the TCP or UNIX socket.
*/
if (dnswire_writer_set_bidirectional(&writer, true) != dnswire_ok) {
fprintf(stderr, "Unable to set dnswire writer to bidirectional mode\n");
return 1;
}
int sockfd;
if (argc == 3) {
/*
* We setup and connect to a unix socket at the given path on command line.
*/
struct sockaddr_un path;
memset(&path, 0, sizeof(struct sockaddr_un));
path.sun_family = AF_UNIX;
strncpy(path.sun_path, argv[2], sizeof(path.sun_path) - 1);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (connect(sockfd, (struct sockaddr*)&path, sizeof(struct sockaddr_un))) {
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("connect\n");
} else {
/*
* We setup and connect to the IP and port given on command line.
*/
struct sockaddr_storage addr_store;
struct sockaddr_in* addr = (struct sockaddr_in*)&addr_store;
socklen_t addrlen;
if (strchr(argv[2], ':')) {
addr->sin_family = AF_INET6;
addrlen = sizeof(struct sockaddr_in6);
} else {
addr->sin_family = AF_INET;
addrlen = sizeof(struct sockaddr_in);
}
if (inet_pton(addr->sin_family, argv[2], &addr->sin_addr) != 1) {
fprintf(stderr, "inet_pton(%s) failed: %s\n", argv[2], strerror(errno));
return 1;
}
addr->sin_port = ntohs(atoi(argv[3]));
sockfd = socket(addr->sin_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (connect(sockfd, (struct sockaddr*)addr, addrlen)) {
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("connect\n");
}
/*
* We now loop until we have a DNSTAP message, the stream was stopped
* or we got an error.
*/
int done2 = 0;
while (!done) {
switch (dnswire_reader_fread(&reader, fp)) {
case dnswire_have_dnstap:
dnswire_writer_set_dnstap(writer, dnswire_reader_dnstap(reader));
printf("sending...\n");
done2 = 0;
while (!done2) {
switch (dnswire_writer_write(&writer, sockfd)) {
case dnswire_ok:
/*
* The DNSTAP message was written successfully, we can now set
* a new DNSTAP message for the writer or stop the stream.
*/
printf("sent\n");
done2 = 1;
break;
case dnswire_again:
break;
case dnswire_endofdata:
/*
* The stream is stopped...
*/
printf("prematurely stopped\n");
done = 1;
done2 = 2;
break;
default:
fprintf(stderr, "dnswire_writer_write() error\n");
done = 1;
done2 = 2;
}
}
break;
case dnswire_again:
case dnswire_need_more:
break;
case dnswire_endofdata:
done = 1;
break;
default:
fprintf(stderr, "dnswire_reader_fread() error\n");
done = 1;
}
}
if (done != 2) {
/*
* This stops the stream, loop again until it's stopped.
*/
printf("stopping...\n");
dnswire_writer_stop(&writer);
done2 = 0;
while (!done2) {
switch (dnswire_writer_write(&writer, sockfd)) {
case dnswire_again:
break;
case dnswire_endofdata:
/*
* The stream is stopped, we're done!
*/
printf("stopped\n");
done2 = 1;
break;
default:
fprintf(stderr, "dnswire_writer_write() error\n");
done2 = 1;
}
}
}
dnswire_writer_destroy(writer);
/*
* Time to exit, let's shutdown and close the sockets.
*/
shutdown(sockfd, SHUT_RDWR);
close(sockfd);
dnswire_reader_destroy(reader);
fclose(fp);
return 0;
}

169
examples/receiver.c Normal file
View file

@ -0,0 +1,169 @@
#include <dnswire/reader.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include "print_dnstap.c"
int main(int argc, const char* argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: receiver [ <IP> <port> | <unix socket path> ]\n");
return 1;
}
/*
* We first initialize the reader and check that it can allocate the
* buffers it needs.
*/
struct dnswire_reader reader;
if (dnswire_reader_init(&reader) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire reader\n");
return 1;
}
/*
* Allow bidirectional communication over the TCP or UNIX socket.
*/
if (dnswire_reader_allow_bidirectional(&reader, true) != dnswire_ok) {
fprintf(stderr, "Unable to set dnswire reader to bidirectional mode\n");
return 1;
}
int sockfd;
if (argc == 2) {
/*
* We setup a unix socket at the given path on command line.
*/
struct sockaddr_un path;
memset(&path, 0, sizeof(struct sockaddr_un));
path.sun_family = AF_UNIX;
strncpy(path.sun_path, argv[1], sizeof(path.sun_path) - 1);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (bind(sockfd, (struct sockaddr*)&path, sizeof(struct sockaddr_un))) {
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("bind\n");
} else {
/*
* We setup and listen on the IP and port given on command line.
*/
struct sockaddr_storage addr_store;
struct sockaddr_in* addr = (struct sockaddr_in*)&addr_store;
socklen_t addrlen;
if (strchr(argv[1], ':')) {
addr->sin_family = AF_INET6;
addrlen = sizeof(struct sockaddr_in6);
} else {
addr->sin_family = AF_INET;
addrlen = sizeof(struct sockaddr_in);
}
if (inet_pton(addr->sin_family, argv[1], &addr->sin_addr) != 1) {
fprintf(stderr, "inet_pton(%s) failed: %s\n", argv[1], strerror(errno));
return 1;
}
addr->sin_port = ntohs(atoi(argv[2]));
sockfd = socket(addr->sin_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (bind(sockfd, (struct sockaddr*)addr, addrlen)) {
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("bind\n");
}
if (listen(sockfd, 1)) {
fprintf(stderr, "listen() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("listen\n");
int clifd = accept(sockfd, 0, 0);
if (clifd < 0) {
fprintf(stderr, "accept() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("accept\n");
/*
* We have accepted connection from the sender!
*
* We now loop until we have a DNSTAP message, the stream was stopped
* or we got an error.
*/
int done = 0;
printf("receiving...\n");
while (!done) {
switch (dnswire_reader_read(&reader, clifd)) {
case dnswire_have_dnstap:
/*
* We received a DNSTAP message, let's print it.
*/
print_dnstap(dnswire_reader_dnstap(reader));
break;
case dnswire_again:
case dnswire_need_more:
/*
* This indicates that we need to call the reader again as it
* will only do one pass in a non-blocking fasion.
*/
break;
case dnswire_endofdata:
/*
* The stream was stopped from the sender side, we're done!
*/
printf("stopped\n");
done = 1;
break;
default:
fprintf(stderr, "dnswire_reader_read() error\n");
done = 1;
}
}
dnswire_reader_destroy(reader);
/*
* Time to exit, let's shutdown and close the sockets.
*/
shutdown(clifd, SHUT_RDWR);
close(clifd);
close(sockfd);
return 0;
}

169
examples/sender.c Normal file
View file

@ -0,0 +1,169 @@
#include <dnswire/writer.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "create_dnstap.c"
int main(int argc, const char* argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: sender [ <IP> <port> | <unix socket path> ]\n");
return 1;
}
/*
* We first initialize the writer and check that it can allocate the
* buffers it needs.
*/
struct dnswire_writer writer;
if (dnswire_writer_init(&writer) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire writer\n");
return 1;
}
/*
* Use bidirectional communication over the TCP or UNIX socket.
*/
if (dnswire_writer_set_bidirectional(&writer, true) != dnswire_ok) {
fprintf(stderr, "Unable to set dnswire writer to bidirectional mode\n");
return 1;
}
int sockfd;
if (argc == 2) {
/*
* We setup and connect to a unix socket at the given path on command line.
*/
struct sockaddr_un path;
memset(&path, 0, sizeof(struct sockaddr_un));
path.sun_family = AF_UNIX;
strncpy(path.sun_path, argv[1], sizeof(path.sun_path) - 1);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (connect(sockfd, (struct sockaddr*)&path, sizeof(struct sockaddr_un))) {
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("connect\n");
} else {
/*
* We setup and connect to the IP and port given on command line.
*/
struct sockaddr_storage addr_store;
struct sockaddr_in* addr = (struct sockaddr_in*)&addr_store;
socklen_t addrlen;
if (strchr(argv[1], ':')) {
addr->sin_family = AF_INET6;
addrlen = sizeof(struct sockaddr_in6);
} else {
addr->sin_family = AF_INET;
addrlen = sizeof(struct sockaddr_in);
}
if (inet_pton(addr->sin_family, argv[1], &addr->sin_addr) != 1) {
fprintf(stderr, "inet_pton(%s) failed: %s\n", argv[1], strerror(errno));
return 1;
}
addr->sin_port = ntohs(atoi(argv[2]));
sockfd = socket(addr->sin_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
printf("socket\n");
if (connect(sockfd, (struct sockaddr*)addr, addrlen)) {
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(sockfd);
return 1;
}
printf("connect\n");
}
/*
* We are now connected!
*
* Now we create a DNSTAP message.
*/
struct dnstap d = create_dnstap("sender");
/*
* We set the DNSTAP message the writer should write.
*/
dnswire_writer_set_dnstap(writer, &d);
/*
* We now loop and wait for the DNSTAP message to be written.
*/
int done = 0;
printf("sending...\n");
while (!done) {
switch (dnswire_writer_write(&writer, sockfd)) {
case dnswire_ok:
/*
* The DNSTAP message was written successfully, we can now set
* a new DNSTAP message for the writer or stop the stream.
*
* This stops the stream, loop again until it's stopped.
*/
if (dnswire_writer_stop(&writer) != dnswire_ok) {
printf("stopping failed!\n");
done = 1;
}
break;
case dnswire_again:
/*
* This indicates that we need to call the writer again as it
* will only do one pass in a non-blocking fasion.
*/
break;
case dnswire_endofdata:
/*
* The stream is stopped, we're done!
*/
printf("stopped\n");
done = 1;
break;
default:
fprintf(stderr, "dnswire_writer_write() error\n");
done = 1;
}
}
dnswire_writer_destroy(writer);
/*
* Time to exit, let's shutdown and close the sockets.
*/
shutdown(sockfd, SHUT_RDWR);
close(sockfd);
return 0;
}

84
examples/writer.c Normal file
View file

@ -0,0 +1,84 @@
#include <dnswire/writer.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "create_dnstap.c"
int main(int argc, const char* argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: writer <file>\n");
return 1;
}
/*
* We start by opening the output file for writing.
*/
FILE* fp = fopen(argv[1], "w");
if (!fp) {
fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno));
return 1;
}
/*
* We first initialize the writer and check that it can allocate the
* buffers it needs.
*/
struct dnswire_writer writer;
if (dnswire_writer_init(&writer) != dnswire_ok) {
fprintf(stderr, "Unable to initialize dnswire writer\n");
return 1;
}
/*
* Now we create a DNSTAP message.
*/
struct dnstap d = create_dnstap("writer");
/*
* We set the DNSTAP message the writer should write.
*/
dnswire_writer_set_dnstap(writer, &d);
/*
* We now loop and wait for the DNSTAP message to be written.
*/
int done = 0;
while (!done) {
switch (dnswire_writer_fwrite(&writer, fp)) {
case dnswire_ok:
/*
* The DNSTAP message was written successfully, we can now set
* a new DNSTAP message for the writer or stop the stream.
*
* This stops the stream, loop again until it's stopped.
*/
dnswire_writer_stop(&writer);
break;
case dnswire_again:
break;
case dnswire_endofdata:
/*
* The stream is stopped, we're done!
*/
done = 1;
break;
default:
fprintf(stderr, "dnswire_writer_fwrite() error\n");
done = 1;
}
}
dnswire_writer_destroy(writer);
fclose(fp);
return 0;
}