]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
dnstap test progam can log on one line type,ip,qname,qtype,qclass
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Jan 2020 16:23:01 +0000 (17:23 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Jan 2020 16:23:01 +0000 (17:23 +0100)
dnstap/unbound-dnstap-socket.c

index 30412fb3bc43bae4276caa63de360b332bd67c5e..4b8ef1dadb805f97c34e1eb58454ff9e89f57a7c 100644 (file)
 #include "util/ub_event.h"
 #include "util/net_help.h"
 #include "services/listen_dnsport.h"
+#include "sldns/sbuffer.h"
+#include "sldns/wire2str.h"
+#include <protobuf-c/protobuf-c.h>
+#include "dnstap/dnstap.pb-c.h"
+
+#define DNSTAP_CONTENT_TYPE             "protobuf:dnstap.Dnstap"
 
 /** usage information for streamtcp */
 static void usage(char* argv[])
 {
        printf("usage: %s [options]\n", argv[0]);
        printf("        Listen to dnstap messages\n");
+       printf("stdout has dnstap log, stderr has verbose server log\n");
        printf("-u <socketpath> use unix socket with this file name\n");
        printf("-l              long format for DNS printout\n");
        printf("-v              more verbose log output\n");
@@ -69,6 +76,9 @@ static void usage(char* argv[])
        exit(1);
 }
 
+/** long format option, for multiline printout per message */
+static int longformat = 0;
+
 /** tap callback variables */
 struct tap_data {
        /** the fd */
@@ -79,16 +89,180 @@ struct tap_data {
        int len_done;
        /** have we read the data, and how many bytes of it */
        size_t data_done;
-       /** are we bi-directional (if false, uni-directional) */
-       int is_bidirectional;
        /** are we reading a control frame */
        int control_frame;
+       /** are we bi-directional (if false, uni-directional) */
+       int is_bidirectional;
        /** data of the frame */
        uint8_t* frame;
        /** length of this frame */
        size_t len;
 };
 
+/** log control frame contents */
+static void log_control_frame(uint8_t* pkt, size_t len)
+{
+       uint32_t frametype = 0;
+       char buf[256];
+       size_t at = 0, remain;
+       uint8_t* pos;
+       if(verbosity == 0) return;
+       if(len < 4) {
+               log_err("malformed control frame, too short, len=%d", (int)len);
+               return;
+       }
+       buf[0]=0;
+       frametype = sldns_read_uint32(pkt);
+       if(frametype == FSTRM_CONTROL_FRAME_ACCEPT) {
+               at+=snprintf(buf+at, sizeof(buf)-at, "accept");
+       } else if(frametype == FSTRM_CONTROL_FRAME_START) {
+               at+=snprintf(buf+at, sizeof(buf)-at, "start");
+       } else if(frametype == FSTRM_CONTROL_FRAME_STOP) {
+               at+=snprintf(buf+at, sizeof(buf)-at, "stop");
+       } else if(frametype == FSTRM_CONTROL_FRAME_READY) {
+               at+=snprintf(buf+at, sizeof(buf)-at, "ready");
+       } else if(frametype == FSTRM_CONTROL_FRAME_FINISH) {
+               at+=snprintf(buf+at, sizeof(buf)-at, "finish");
+       } else {
+               at+=snprintf(buf+at, sizeof(buf)-at, "type%d",
+                       (int)frametype);
+       }
+
+       /* show the content type options */
+       pos = pkt + 4;
+       remain = len - 4;
+       while(remain >= 8) {
+               uint32_t field_type = sldns_read_uint32(pos);
+               uint32_t field_len = sldns_read_uint32(pos+4);
+               if(remain < field_len) {
+                       at+=snprintf(buf+at, sizeof(buf)-at, "malformed_field");
+                       break;
+               }
+               if(field_type == FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE) {
+                       at+=snprintf(buf+at, sizeof(buf)-at, " content-type(");
+                       if(at+field_len < sizeof(buf)) {
+                               memmove(buf+at, pos+8, field_len);
+                               at += field_len;
+                       }
+                       at+=snprintf(buf+at, sizeof(buf)-at, ")");
+               }
+               pos += 8 + field_len;
+               remain -= (8 + field_len);
+       }
+       log_info("control frame %s", buf);
+}
+
+/** convert mtype to string */
+static const char* mtype_to_str(enum _Dnstap__Message__Type mtype)
+{
+       switch(mtype) {
+               case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
+                       return "AUTH_QUERY";
+               case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
+                       return "AUTH_RESPONSE";
+               case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
+                       return "RESOLVER_QUERY";
+               case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
+                       return "RESOLVER_RESPONSE";
+               case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
+                       return "CLIENT_QUERY";
+               case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
+                       return "CLIENT_RESPONSE";
+               case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
+                       return "FORWARDER_QUERY";
+               case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
+                       return "FORWARDER_RESPONSE";
+               case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
+                       return "STUB_QUERY";
+               case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
+                       return "STUB_RESPONSE";
+               default: break;
+       }
+       return "unknown_message_type";
+}
+
+/** convert type address to a string ip4 or ip6, malloced or NULL on fail */
+static char* str_of_addr(ProtobufCBinaryData address)
+{
+       char buf[64];
+       socklen_t len = sizeof(buf);
+       if(address.len == 4) {
+               if(inet_ntop(AF_INET, address.data, buf, len)!=0)
+                       return strdup(buf);
+       } else if(address.len == 16) {
+               if(inet_ntop(AF_INET6, address.data, buf, len)!=0)
+                       return strdup(buf);
+       }
+       return NULL;
+}
+
+/** convert message buffer (of dns bytes) to the first qname, type, class,
+ * malloced or NULL on fail */
+static char* q_of_msg(ProtobufCBinaryData message)
+{
+       char buf[300];
+       /* header, name, type, class minimum to get the query tuple */
+       if(message.len < 12 + 1 + 4 + 4) return NULL;
+       if(sldns_wire2str_rrquestion_buf(message.data+12, message.len-12,
+               buf, sizeof(buf)) != 0) {
+               /* remove trailing newline, tabs to spaces */
+               if(buf[0] != 0) buf[strlen(buf)-1]=0;
+               if(strrchr(buf, '\t')) *strrchr(buf, '\t')=' ';
+               if(strrchr(buf, '\t')) *strrchr(buf, '\t')=' ';
+               return strdup(buf);
+       }
+       return NULL;
+}
+
+/** log data frame contents */
+static void log_data_frame(uint8_t* pkt, size_t len)
+{
+       Dnstap__Dnstap* d = dnstap__dnstap__unpack(NULL, len, pkt);
+       const char* mtype = NULL;
+       char* maddr=NULL, *qinf=NULL;
+       if(!d) {
+               log_err("could not unpack");
+               return;
+       }
+       if(d->base.descriptor != &dnstap__dnstap__descriptor) {
+               log_err("wrong base descriptor");
+               dnstap__dnstap__free_unpacked(d, NULL);
+               return;
+       }
+       if(d->type != DNSTAP__DNSTAP__TYPE__MESSAGE) {
+               log_err("dnstap type not type_message");
+               dnstap__dnstap__free_unpacked(d, NULL);
+               return;
+       }
+       if(d->message) {
+               mtype = mtype_to_str(d->message->type);
+               if(d->message->has_query_address)
+                       maddr = str_of_addr(d->message->query_address);
+               else if(d->message->has_response_address)
+                       maddr = str_of_addr(d->message->response_address);
+               if(d->message->has_query_message)
+                       qinf = q_of_msg(d->message->query_message);
+               else if(d->message->has_response_message)
+                       qinf = q_of_msg(d->message->response_message);
+
+       } else {
+               mtype = "nomessage";
+       }
+       
+       printf("%s%s%s%s%s\n", mtype, (maddr?" ":""), (maddr?maddr:""),
+               (qinf?" ":""), (qinf?qinf:""));
+       free(maddr);
+       free(qinf);
+
+       if(longformat) {
+               if(d->has_identity) {
+               }
+               if(d->has_version) {
+               }
+       }
+       dnstap__dnstap__free_unpacked(d, NULL);
+}
+
 /** receive bytes from fd, prints errors if bad,
  * returns 0: closed/error, -1: continue, >0 number of bytes */
 static ssize_t receive_bytes(int fd, void* buf, size_t len)
@@ -131,16 +305,95 @@ void tap_data_free(struct tap_data* data)
        free(data);
 }
 
+/** reply with ACCEPT control frame to bidirectional client,
+ * returns 0 on error */
+static int reply_with_accept(int fd)
+{
+       /* control frame on reply:
+        * 4 bytes 0 escape
+        * 4 bytes bigendian length of frame
+        * 4 bytes bigendian type ACCEPT
+        * 4 bytes bigendian frame option content type
+        * 4 bytes bigendian length of string
+        * string of content type.
+        */
+       uint32_t* acceptframe;
+       /* len includes the escape and framelength */
+       size_t len = 4+4+4+4+4+strlen(DNSTAP_CONTENT_TYPE);
+       acceptframe = calloc(1, len);
+       if(!acceptframe) {
+               log_err("out of memory");
+               return 0;
+       }
+       acceptframe[0] = 0;
+       acceptframe[1] = htonl(4+4+4+strlen(DNSTAP_CONTENT_TYPE));
+       acceptframe[2] = htonl(FSTRM_CONTROL_FRAME_ACCEPT);
+       acceptframe[3] = htonl(FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE);
+       acceptframe[4] = htonl(strlen(DNSTAP_CONTENT_TYPE));
+       memmove(&acceptframe[5], DNSTAP_CONTENT_TYPE,
+               strlen(DNSTAP_CONTENT_TYPE));
+
+       fd_set_block(fd);
+       if(send(fd, acceptframe, len, 0) == -1) {
+#ifndef USE_WINSOCK
+               log_err("send failed: %s", strerror(errno));
+#else
+               log_err("send failed: %s", wsa_strerror(WSAGetLastError()));
+#endif
+               fd_set_nonblock(fd);
+               free(acceptframe);
+               return 0;
+       }
+       if(verbosity) log_info("sent control frame(accept) content-type:(%s)",
+                       DNSTAP_CONTENT_TYPE);
+
+       fd_set_nonblock(fd);
+       free(acceptframe);
+       return 1;
+}
+
+/** reply with FINISH control frame to bidirectional client,
+ * returns 0 on error */
+static int reply_with_finish(int fd)
+{
+       /* control frame on reply:
+        * 4 bytes 0 escape
+        * 4 bytes bigendian length of frame
+        * 4 bytes bigendian type FINISH
+        */
+       uint32_t finishframe[3];
+       /* len includes the escape and framelength */
+       size_t len = 4+4+4;
+       finishframe[0] = 0;
+       finishframe[1] = htonl(4);
+       finishframe[2] = htonl(FSTRM_CONTROL_FRAME_FINISH);
+
+       fd_set_block(fd);
+       if(send(fd, finishframe, len, 0) == -1) {
+#ifndef USE_WINSOCK
+               log_err("send failed: %s", strerror(errno));
+#else
+               log_err("send failed: %s", wsa_strerror(WSAGetLastError()));
+#endif
+               fd_set_nonblock(fd);
+               return 0;
+       }
+       if(verbosity) log_info("sent control frame(finish)");
+
+       fd_set_nonblock(fd);
+       return 1;
+}
+
 /** callback for dnstap listener */
 static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
 {
        struct tap_data* data = (struct tap_data*)arg;
-       if(verbosity) log_info("tap callback");
+       if(verbosity>=3) log_info("tap callback");
        while(data->len_done < 4) {
                uint32_t l = (uint32_t)data->len;
                ssize_t ret = receive_bytes(fd,
                        ((uint8_t*)&l)+data->len_done, 4-data->len_done);
-               log_info("s recv %d got %d %d", (int)ret, (int)l, (int)ntohl(l));
+               if(verbosity>=4) log_info("s recv %d", (int)ret);
                if(ret == 0) {
                        /* closed or error */
                        tap_data_free(data);
@@ -154,7 +407,7 @@ static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
                if(data->len_done < 4)
                        return; /* continue later */
                data->len = (size_t)(ntohl(l));
-               if(verbosity) log_info("length is %d", (int)data->len);
+               if(verbosity>=3) log_info("length is %d", (int)data->len);
                if(data->len == 0) {
                        /* it is a control frame */
                        data->control_frame = 1;
@@ -170,10 +423,12 @@ static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
                        }
                }
        }
+
        /* we want to read the full length now */
        if(data->data_done < data->len) {
                ssize_t r = receive_bytes(fd, data->frame + data->data_done,
                        data->len - data->data_done);
+               if(verbosity>=4) log_info("f recv %d", (int)r);
                if(r == 0) {
                        /* closed or error */
                        tap_data_free(data);
@@ -186,13 +441,27 @@ static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
                if(data->data_done < data->len)
                        return; /* continue later */
        }
+
        /* we are done with a frame */
-       if(verbosity) log_info("received %sframe len %d",
+       if(verbosity>=3) log_info("received %sframe len %d",
                (data->control_frame?"control ":""), (int)data->len);
-       if(data->len >= 4 && ntohl(*(uint32_t*)data->frame) ==
+       if(data->control_frame)
+               log_control_frame(data->frame, data->len);
+       else    log_data_frame(data->frame, data->len);
+
+       if(data->len >= 4 && sldns_read_uint32(data->frame) ==
                FSTRM_CONTROL_FRAME_READY) {
                data->is_bidirectional = 1;
                if(verbosity) log_info("bidirectional stream");
+               if(!reply_with_accept(fd)) {
+                       tap_data_free(data);
+               }
+       } else if(data->len >= 4 && sldns_read_uint32(data->frame) ==
+               FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) {
+               if(!reply_with_finish(fd)) {
+                       tap_data_free(data);
+                       return;
+               }
        }
 
        /* prepare for next frame */
@@ -202,6 +471,7 @@ static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
        data->len = 0;
        data->len_done = 0;
        data->data_done = 0;
+
 }
 
 /** callback for main listening file descriptor */
@@ -279,7 +549,7 @@ setup_fd(char* socketpath)
 
 /** setup and run the server to listen to DNSTAP messages */
 static void
-setup_and_run(char* socketpath, int longformat)
+setup_and_run(char* socketpath)
 {
        int fd;
        time_t secs = 0;
@@ -316,7 +586,7 @@ extern char* optarg;
 int main(int argc, char** argv) 
 {
        int c;
-       int usessl = 0, longformat = 0;
+       int usessl = 0;
        char* socketpath = NULL;
 #ifdef USE_WINSOCK
        WSADATA wsa_data;
@@ -378,7 +648,7 @@ int main(int argc, char** argv)
                (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
 #endif
        }
-       setup_and_run(socketpath, longformat);
+       setup_and_run(socketpath);
        checklock_stop();
 #ifdef USE_WINSOCK
        WSACleanup();