diff options
Diffstat (limited to '')
-rw-r--r-- | examples/Makefile.am | 48 | ||||
-rw-r--r-- | examples/README.md | 114 | ||||
-rw-r--r-- | examples/client_receiver_uv.c | 134 | ||||
-rw-r--r-- | examples/create_dnstap.c | 99 | ||||
-rw-r--r-- | examples/daemon_sender_uv.c | 254 | ||||
-rw-r--r-- | examples/print_dnstap.c | 122 | ||||
-rw-r--r-- | examples/reader.c | 65 | ||||
-rw-r--r-- | examples/reader_sender.c | 225 | ||||
-rw-r--r-- | examples/receiver.c | 169 | ||||
-rw-r--r-- | examples/sender.c | 169 | ||||
-rw-r--r-- | examples/writer.c | 84 |
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; +} |