Commit 94942ca5 authored by Robert Edmonds's avatar Robert Edmonds

Add initial dnstap implementation

parent 46d26d5a
......@@ -57,6 +57,12 @@
/src/knot/conf/libknotd_la-cf-parse.h
/src/zscanner/scanner.c
# dnstap
/src/dnstap/Makefile
/src/dnstap/Makefile.in
/src/dnstap/dnstap.pb-c.c
/src/dnstap/dnstap.pb-c.h
# zscanner
/src/zscanner/tests/tmp/
/src/zscanner/tests/unittests
......
......@@ -384,6 +384,7 @@ AC_CONFIG_FILES([Makefile
libtap/Makefile
src/Makefile
tests/Makefile
src/dnstap/Makefile
src/zscanner/Makefile
man/khost.1
man/knotc.8
......
ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
SUBDIRS = zscanner .
SUBDIRS = zscanner dnstap .
sbin_PROGRAMS = knotc knotd
bin_PROGRAMS = kdig khost knsupdate knsec3hash
......
ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
AM_CPPFLAGS = \
-include $(top_builddir)/src/config.h \
-I$(top_srcdir)/src
EXTRA_DIST = \
dnstap.proto
if HAVE_DNSTAP
SUFFIXES = .proto .pb-c.c .pb-c.h
.proto.pb-c.c:
$(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
.proto.pb-c.h:
$(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
noinst_LTLIBRARIES = libdnstap.la
libdnstap_la_CFLAGS = \
$(CODE_COVERAGE_CFLAGS) \
$(DNSTAP_CFLAGS)
libdnstap_la_LDFLAGS = \
$(CODE_COVERAGE_LDFLAGS) \
$(DNSTAP_LIBS)
libdnstap_la_SOURCES = \
dnstap.c \
message.c \
writer.c
nodist_libdnstap_la_SOURCES = \
dnstap.pb-c.c \
dnstap.pb-c.h
BUILT_SOURCES = $(nodist_libdnstap_la_SOURCES)
CLEANFILES = $(nodist_libdnstap_la_SOURCES)
endif
/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include "dnstap/dnstap.pb-c.h"
#define DNSTAP_INITIAL_BUF_SIZE 256
uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz)
{
ProtobufCBufferSimple sbuf;
sbuf.base.append = protobuf_c_buffer_simple_append;
sbuf.len = 0;
sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
sbuf.data = malloc(sbuf.alloced);
if (sbuf.data == NULL)
return NULL;
sbuf.must_free_data = 1;
*sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf);
*buf = sbuf.data;
return *buf;
}
/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file dnstap.h
*
* \author Robert Edmonds <edmonds@fsi.io>
*
* \brief Public interface for dnstap.
* @{
*/
#ifndef _DNSTAP__DNSTAP_H_
#define _DNSTAP__DNSTAP_H_
#include <stddef.h>
#include <stdint.h>
#include "dnstap/dnstap.pb-c.h"
/*! \brief Frame Streams "Content Type" value for dnstap. */
#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
/*!
* \brief Serializes a filled out dnstap protobuf struct. Dynamically allocates
* storage for the serialized frame.
*
* \note This function returns a copy of its parameter return value 'buf' to
* make error checking slightly easier.
*
* \param d dnstap protobuf struct.
* \param[out] buf Serialized frame.
* \param[out] sz Size in bytes of the serialized frame.
*
* \return Serialized frame.
* \retval NULL if error.
*/
uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz);
#endif // _DNSTAP__DNSTAP_H_
/*! @} */
// dnstap: flexible, structured event replication format for DNS software
//
// This file contains the protobuf schemas for the "dnstap" structured event
// replication format for DNS software.
// Written in 2013-2014 by Farsight Security, Inc.
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this file to the public
// domain worldwide. This file is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along
// with this file. If not, see:
//
// <http://creativecommons.org/publicdomain/zero/1.0/>.
package dnstap;
// "Dnstap": this is the top-level dnstap type, which is a "union" type that
// contains other kinds of dnstap payloads, although currently only one type
// of dnstap payload is defined.
// See: https://developers.google.com/protocol-buffers/docs/techniques#union
message Dnstap {
// DNS server identity.
// If enabled, this is the identity string of the DNS server which generated
// this message. Typically this would be the same string as returned by an
// "NSID" (RFC 5001) query.
optional bytes identity = 1;
// DNS server version.
// If enabled, this is the version string of the DNS server which generated
// this message. Typically this would be the same string as returned by a
// "version.bind" query.
optional bytes version = 2;
// Extra data for this payload.
// This field can be used for adding an arbitrary byte-string annotation to
// the payload. No encoding or interpretation is applied or enforced.
optional bytes extra = 3;
// Identifies which field below is filled in.
enum Type {
MESSAGE = 1;
}
required Type type = 15;
// One of the following will be filled in.
optional Message message = 14;
}
// SocketFamily: the network protocol family of a socket. This specifies how
// to interpret "network address" fields.
enum SocketFamily {
INET = 1; // IPv4 (RFC 791)
INET6 = 2; // IPv6 (RFC 2460)
}
// SocketProtocol: the transport protocol of a socket. This specifies how to
// interpret "transport port" fields.
enum SocketProtocol {
UDP = 1; // User Datagram Protocol (RFC 768)
TCP = 2; // Transmission Control Protocol (RFC 793)
}
// Message: a wire-format (RFC 1035 section 4) DNS message and associated
// metadata. Applications generating "Message" payloads should follow
// certain requirements based on the MessageType, see below.
message Message {
// There are eight types of "Message" defined that correspond to the
// four arrows in the following diagram, slightly modified from RFC 1035
// section 2:
// +---------+ +----------+ +--------+
// | | query | | query | |
// | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
// | Resolver| | Server | | Name |
// | |<-SR--------CR-| |<-RR----AR-| Server |
// +---------+ response | | response | |
// +----------+ +--------+
// Each arrow has two Type values each, one for each "end" of each arrow,
// because these are considered to be distinct events. Each end of each
// arrow on the diagram above has been marked with a two-letter Type
// mnemonic. Clockwise from upper left, these mnemonic values are:
//
// SQ: STUB_QUERY
// CQ: CLIENT_QUERY
// RQ: RESOLVER_QUERY
// AQ: AUTH_QUERY
// AR: AUTH_RESPONSE
// RR: RESOLVER_RESPONSE
// CR: CLIENT_RESPONSE
// SR: STUB_RESPONSE
// Two additional types of "Message" have been defined for the
// "forwarding" case where an upstream DNS server is responsible for
// further recursion. These are not shown on the diagram above, but have
// the following mnemonic values:
// FQ: FORWARDER_QUERY
// FR: FORWARDER_RESPONSE
// The "Message" Type values are defined below.
enum Type {
// AUTH_QUERY is a DNS query message received from a resolver by an
// authoritative name server, from the perspective of the authorative
// name server.
AUTH_QUERY = 1;
// AUTH_RESPONSE is a DNS response message sent from an authoritative
// name server to a resolver, from the perspective of the authoritative
// name server.
AUTH_RESPONSE = 2;
// RESOLVER_QUERY is a DNS query message sent from a resolver to an
// authoritative name server, from the perspective of the resolver.
// Resolvers typically clear the RD (recursion desired) bit when
// sending queries.
RESOLVER_QUERY = 3;
// RESOLVER_RESPONSE is a DNS response message received from an
// authoritative name server by a resolver, from the perspective of
// the resolver.
RESOLVER_RESPONSE = 4;
// CLIENT_QUERY is a DNS query message sent from a client to a DNS
// server which is expected to perform further recursion, from the
// perspective of the DNS server. The client may be a stub resolver or
// forwarder or some other type of software which typically sets the RD
// (recursion desired) bit when querying the DNS server. The DNS server
// may be a simple forwarding proxy or it may be a full recursive
// resolver.
CLIENT_QUERY = 5;
// CLIENT_RESPONSE is a DNS response message sent from a DNS server to
// a client, from the perspective of the DNS server. The DNS server
// typically sets the RA (recursion available) bit when responding.
CLIENT_RESPONSE = 6;
// FORWARDER_QUERY is a DNS query message sent from a downstream DNS
// server to an upstream DNS server which is expected to perform
// further recursion, from the perspective of the downstream DNS
// server.
FORWARDER_QUERY = 7;
// FORWARDER_RESPONSE is a DNS response message sent from an upstream
// DNS server performing recursion to a downstream DNS server, from the
// perspective of the downstream DNS server.
FORWARDER_RESPONSE = 8;
// STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
// server, from the perspective of the stub resolver.
STUB_QUERY = 9;
// STUB_RESPONSE is a DNS response message sent from a DNS server to a
// stub resolver, from the perspective of the stub resolver.
STUB_RESPONSE = 10;
// TOOL_QUERY is a DNS query message sent from a DNS software tool to a
// DNS server, from the perspective of the tool.
TOOL_QUERY = 11;
// TOOL_RESPONSE is a DNS response message received by a DNS software
// tool from a DNS server, from the perspective of the tool.
TOOL_RESPONSE = 12;
}
// One of the Type values described above.
required Type type = 1;
// One of the SocketFamily values described above.
optional SocketFamily socket_family = 2;
// One of the SocketProtocol values described above.
optional SocketProtocol socket_protocol = 3;
// The network address of the message initiator.
// For SocketFamily INET, this field is 4 octets (IPv4 address).
// For SocketFamily INET6, this field is 16 octets (IPv6 address).
optional bytes query_address = 4;
// The network address of the message responder.
// For SocketFamily INET, this field is 4 octets (IPv4 address).
// For SocketFamily INET6, this field is 16 octets (IPv6 address).
optional bytes response_address = 5;
// The transport port of the message initiator.
// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
optional uint32 query_port = 6;
// The transport port of the message responder.
// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
optional uint32 response_port = 7;
// The time at which the DNS query message was sent or received, depending
// on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
// This is the number of seconds since the UNIX epoch.
optional uint64 query_time_sec = 8;
// The time at which the DNS query message was sent or received.
// This is the seconds fraction, expressed as a count of nanoseconds.
optional fixed32 query_time_nsec = 9;
// The initiator's original wire-format DNS query message, verbatim.
optional bytes query_message = 10;
// The "zone" or "bailiwick" pertaining to the DNS query message.
// This is a wire-format DNS domain name.
optional bytes query_zone = 11;
// The time at which the DNS response message was sent or received,
// depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
// CLIENT_RESPONSE.
// This is the number of seconds since the UNIX epoch.
optional uint64 response_time_sec = 12;
// The time at which the DNS response message was sent or received.
// This is the seconds fraction, expressed as a count of nanoseconds.
optional fixed32 response_time_nsec = 13;
// The responder's original wire-format DNS response message, verbatim.
optional bytes response_message = 14;
}
// All fields except for 'type' in the Message schema are optional.
// It is recommended that at least the following fields be filled in for
// particular types of Messages.
// AUTH_QUERY:
// socket_family, socket_protocol
// query_address, query_port
// query_message
// query_time_sec, query_time_nsec
// AUTH_RESPONSE:
// socket_family, socket_protocol
// query_address, query_port
// query_time_sec, query_time_nsec
// response_message
// response_time_sec, response_time_nsec
// RESOLVER_QUERY:
// socket_family, socket_protocol
// query_message
// query_time_sec, query_time_nsec
// query_zone
// response_address, response_port
// RESOLVER_RESPONSE:
// socket_family, socket_protocol
// query_time_sec, query_time_nsec
// query_zone
// response_address, response_port
// response_message
// response_time_sec, response_time_nsec
// CLIENT_QUERY:
// socket_family, socket_protocol
// query_message
// query_time_sec, query_time_nsec
// CLIENT_RESPONSE:
// socket_family, socket_protocol
// query_time_sec, query_time_nsec
// response_message
// response_time_sec, response_time_nsec
/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <netinet/in.h> // sockaddr_in
#include <stdint.h>
#include <stdlib.h>
#include <string.h> // memset
#include "common/errcode.h"
#include "dnstap/message.h"
int dt_message_fill(Dnstap__Message *m,
const Dnstap__Message__Type type,
const struct sockaddr *response_sa,
const int protocol,
const void *wire,
const size_t len_wire,
const struct timeval *qtime,
const struct timeval *rtime)
{
memset(m, 0, sizeof(*m));
m->base.descriptor = &dnstap__message__descriptor;
if (type != DNSTAP__MESSAGE__TYPE__TOOL_QUERY &&
type != DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE)
{
return KNOT_EINVAL;
}
// Message.type
m->type = type;
if (response_sa->sa_family == AF_INET) {
const struct sockaddr_in *sai =
(const struct sockaddr_in *)response_sa;
// Message.socket_family
m->has_socket_family = 1;
m->socket_family = DNSTAP__SOCKET_FAMILY__INET;
// Message.response_address
m->response_address.len = 4;
m->response_address.data = (uint8_t *)
&sai->sin_addr.s_addr;
m->has_response_address = 1;
// Message.response_port
m->has_response_port = 1;
m->response_port = ntohs(sai->sin_port);
} else if (response_sa->sa_family == AF_INET6) {
const struct sockaddr_in6 *sai6 =
(const struct sockaddr_in6 *)response_sa;
// Message.socket_family
m->socket_family = DNSTAP__SOCKET_FAMILY__INET6;
m->has_socket_family = 1;
// Message.response_address
m->response_address.len = 16;
m->response_address.data = (uint8_t *)
&sai6->sin6_addr.s6_addr;
m->has_response_address = 1;
// Message.response_port
m->has_response_port = 1;
m->response_port = ntohs(sai6->sin6_port);
} else {
return KNOT_EINVAL;
}
// Message.socket_protocol
if (protocol == IPPROTO_UDP) {
m->socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
} else if (protocol == IPPROTO_TCP) {
m->socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
} else {
return KNOT_EINVAL;
}
m->has_socket_protocol = 1;
if (type == DNSTAP__MESSAGE__TYPE__TOOL_QUERY) {
// Message.query_message
m->query_message.len = len_wire;
m->query_message.data = (uint8_t *)wire;
m->has_query_message = 1;
} else if (type == DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE) {
// Message.response_message
m->response_message.len = len_wire;
m->response_message.data = (uint8_t *)wire;
m->has_response_message = 1;
}
// Message.query_time_sec, Message.query_time_nsec
if (qtime != NULL) {
m->query_time_sec = qtime->tv_sec;
m->query_time_nsec = qtime->tv_usec * 1000;
m->has_query_time_sec = 1;
m->has_query_time_nsec = 1;
}
// Message.response_time_sec, Message.response_time_nsec
if (rtime != NULL) {
m->response_time_sec = rtime->tv_sec;
m->response_time_nsec = rtime->tv_usec * 1000;
m->has_response_time_sec = 1;
m->has_response_time_nsec = 1;
}
return KNOT_EOK;
}
/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file message.h
*
* \author Robert Edmonds <edmonds@fsi.io>
*
* \brief dnstap message interface.
*
* \addtogroup dnstap
* @{
*/
#ifndef _DNSTAP__MESSAGE_H_
#define _DNSTAP__MESSAGE_H_
#include <sys/socket.h> // struct sockaddr
#include <sys/time.h> // struct timeval
#include <stddef.h> // size_t
#include "dnstap/dnstap.pb-c.h"
/*!
* \brief Fill a Dnstap__Message structure with the given parameters.
*
* Supported message types:
* \c DNSTAP__MESSAGE__TYPE__TOOL_QUERY
* \c DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE
*
* \param[out] m
* Dnstap__Message structure to fill. Will be zeroed first.
* \param type
* One of the supported message type values.
* \param response_sa
* sockaddr_in or sockaddr_in6 to use when filling the 'socket_family',
* 'response_address', 'response_port' fields.
* \param protocol
* \c IPPROTO_UDP or \c IPPROTO_TCP.
* \param wire
* Wire-format query message or response message (depending on 'type').
* \param len_wire
* Length in bytes of 'wire'.
* \param qtime
* Query time. May be NULL.
* \param rtime
* Response time. May be NULL.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
int dt_message_fill(Dnstap__Message *m,
const Dnstap__Message__Type type,
const struct sockaddr *response_sa,
const int protocol,
const void *wire,
const size_t len_wire,
const struct timeval *qtime,
const struct timeval *rtime);
#endif // _DNSTAP__MESSAGE_H_
/*! @} */
/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h> // htonl
#include <errno.h>
#include <stdint.h> // uint8_t, uint32_t
#include <stdio.h> // fopen, fwrite
#include <stdlib.h> // calloc, free
#include <string.h> // strdup