summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--examples/Makefile.am48
-rw-r--r--examples/README.md114
-rw-r--r--examples/client_receiver_uv.c134
-rw-r--r--examples/create_dnstap.c99
-rw-r--r--examples/daemon_sender_uv.c254
-rw-r--r--examples/print_dnstap.c122
-rw-r--r--examples/reader.c65
-rw-r--r--examples/reader_sender.c225
-rw-r--r--examples/receiver.c169
-rw-r--r--examples/sender.c169
-rw-r--r--examples/writer.c84
11 files changed, 1483 insertions, 0 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..06dd2a7
--- /dev/null
+++ b/examples/Makefile.am
@@ -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
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..33d5309
--- /dev/null
+++ b/examples/README.md
@@ -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
+```
diff --git a/examples/client_receiver_uv.c b/examples/client_receiver_uv.c
new file mode 100644
index 0000000..0454ba2
--- /dev/null
+++ b/examples/client_receiver_uv.c
@@ -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);
+}
diff --git a/examples/create_dnstap.c b/examples/create_dnstap.c
new file mode 100644
index 0000000..9a03d1b
--- /dev/null
+++ b/examples/create_dnstap.c
@@ -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;
+}
diff --git a/examples/daemon_sender_uv.c b/examples/daemon_sender_uv.c
new file mode 100644
index 0000000..cfa2be8
--- /dev/null
+++ b/examples/daemon_sender_uv.c
@@ -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);
+}
diff --git a/examples/print_dnstap.c b/examples/print_dnstap.c
new file mode 100644
index 0000000..3a0adb0
--- /dev/null
+++ b/examples/print_dnstap.c
@@ -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");
+}
diff --git a/examples/reader.c b/examples/reader.c
new file mode 100644
index 0000000..593e9bc
--- /dev/null
+++ b/examples/reader.c
@@ -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;
+}
diff --git a/examples/reader_sender.c b/examples/reader_sender.c
new file mode 100644
index 0000000..3498f77
--- /dev/null
+++ b/examples/reader_sender.c
@@ -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;
+}
diff --git a/examples/receiver.c b/examples/receiver.c
new file mode 100644
index 0000000..316e1ec
--- /dev/null
+++ b/examples/receiver.c
@@ -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;
+}
diff --git a/examples/sender.c b/examples/sender.c
new file mode 100644
index 0000000..c150415
--- /dev/null
+++ b/examples/sender.c
@@ -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;
+}
diff --git a/examples/writer.c b/examples/writer.c
new file mode 100644
index 0000000..d9088b8
--- /dev/null
+++ b/examples/writer.c
@@ -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;
+}