]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3936: Dns response ip/name parser
authorSerhii Vlasiuk -X (svlasiuk - SOFTSERVE INC at Cisco) <svlasiuk@cisco.com>
Fri, 11 Aug 2023 17:36:45 +0000 (17:36 +0000)
committerSteve Chew (stechew) <stechew@cisco.com>
Fri, 11 Aug 2023 17:36:45 +0000 (17:36 +0000)
Merge in SNORT/snort3 from ~SVLASIUK/snort3:dns_name_parser to master

Squashed commit of the following:

commit 81500ab8cd6138545a0c60009eda898e88de0e62
Author: Serhii Vlasiuk <svlasiuk@cisco.com>
Date:   Tue Jul 25 18:50:36 2023 +0300

    dns: parse and publish dns response with ip, fqdn/ttl data

    added publish_response new dns inspector option

src/pub_sub/CMakeLists.txt
src/pub_sub/dns_events.cc [new file with mode: 0644]
src/pub_sub/dns_events.h [new file with mode: 0644]
src/service_inspectors/CMakeLists.txt
src/service_inspectors/dns/CMakeLists.txt
src/service_inspectors/dns/dns.cc
src/service_inspectors/dns/dns.h
src/service_inspectors/dns/dns_config.cc [new file with mode: 0644]
src/service_inspectors/dns/dns_config.h [new file with mode: 0644]
src/service_inspectors/dns/dns_module.cc
src/service_inspectors/dns/dns_module.h

index 6ce86c53283bc2ce677c049448e3e8e84246352b..c8af9e779adc7faaaed0e769329fb529b8f83523 100644 (file)
@@ -26,12 +26,14 @@ set (PUB_SUB_INCLUDES
     smb_events.h
     ssh_events.h
     ssl_events.h
+    dns_events.h
 )
 
 add_library( pub_sub OBJECT
     ${PUB_SUB_INCLUDES}
     cip_events.cc
     http_events.cc
+    dns_events.cc
     http_request_body_event.cc
     sip_events.cc
 )
diff --git a/src/pub_sub/dns_events.cc b/src/pub_sub/dns_events.cc
new file mode 100644 (file)
index 0000000..73c7497
--- /dev/null
@@ -0,0 +1,75 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2022-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// dns_events.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dns_events.h"
+
+#include "service_inspectors/dns/dns.h"
+
+using namespace snort;
+
+void IPFqdnCacheItem::add_ip(const SfIp& ip)
+{
+    if (ip.is_set())
+        ips.emplace_back(ip);
+}
+
+void IPFqdnCacheItem::add_fqdn(const FqdnTtl& fqdn_ttl)
+{
+    if (fqdn_ttl.fqdn.empty())
+        return;
+
+    if (std::find_if(fqdns.cbegin(), fqdns.cend(),
+        [&fqdn_ttl](const FqdnTtl& i){ return i.fqdn == fqdn_ttl.fqdn; }) != fqdns.end())
+            return;
+
+    fqdns.emplace_back(fqdn_ttl);
+} 
+
+void DnsResponseDataEvents::add_ip(const DnsResponseIp& ip)
+{
+    dns_ips.emplace_back(ip);
+}
+
+void DnsResponseDataEvents::add_fqdn(DnsResponseFqdn& fqdn, uint32_t ttl)
+{
+    fqdn.update_ttl(ttl);
+    dns_fqdns.emplace_back(fqdn);
+}
+
+void DnsResponseDataEvents::get_dns_data(IPFqdnCacheItem& ip_fqdn_cache_item)
+{
+    for (auto& it: dns_ips)
+        ip_fqdn_cache_item.add_ip(it.get_ip());
+
+    // don't add fqdns without ips
+    if (ip_fqdn_cache_item.ips.empty())
+        return;
+
+    for (auto& it: dns_fqdns)
+        ip_fqdn_cache_item.add_fqdn(it.get_fqdn());
+}
+
+bool DnsResponseDataEvents::empty() const
+{
+    return (dns_ips.empty() or dns_fqdns.empty());
+}
diff --git a/src/pub_sub/dns_events.h b/src/pub_sub/dns_events.h
new file mode 100644 (file)
index 0000000..ca92e14
--- /dev/null
@@ -0,0 +1,78 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2022-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// dns_events.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef DNS_EVENTS_H
+#define DNS_EVENTS_H
+
+// This event allows the dns service inspector to publish dns response
+// for use by data bus subscribers
+
+#include "framework/data_bus.h"
+#include "sfip/sf_ip.h"
+
+struct FqdnTtl
+{
+    FqdnTtl(const std::string& fqdn, uint32_t ttl) :
+        fqdn(fqdn), ttl(ttl)
+    {}
+
+    std::string fqdn;
+    uint32_t ttl;
+};
+
+struct IPFqdnCacheItem
+{
+    void add_ip(const snort::SfIp& ip);
+    void add_fqdn(const FqdnTtl& fqdn_ttl);
+
+    std::vector<snort::SfIp> ips;
+    std::vector<FqdnTtl> fqdns;
+};
+
+struct DnsEventIds
+{
+    enum : unsigned
+    {
+        DNS_RESPONSE_DATA,
+        num_ids
+    };
+};
+
+const snort::PubKey dns_pub_key { "dns", DnsEventIds::num_ids };
+
+class DnsResponseIp;
+class DnsResponseFqdn;
+
+namespace snort
+{
+class SO_PUBLIC DnsResponseDataEvents : public snort::DataEvent
+{
+public:
+    void add_ip(const DnsResponseIp& event);
+    void add_fqdn(DnsResponseFqdn& event, uint32_t ttl);
+    void get_dns_data(IPFqdnCacheItem& ip_fqdn_cache_item);
+    bool empty() const;
+
+private:
+    std::vector<DnsResponseIp> dns_ips;
+    std::vector<DnsResponseFqdn> dns_fqdns;
+};
+}
+
+#endif
index c4d54ac27d77f3e2142e6ffc00b8b66363436cf8..bfac57111aaaa695c90d0a45fc88626e5b108ea7 100644 (file)
@@ -28,7 +28,6 @@ if (STATIC_INSPECTORS)
         $<TARGET_OBJECTS:cip>
         $<TARGET_OBJECTS:dce_rpc>
         $<TARGET_OBJECTS:dnp3>
-        $<TARGET_OBJECTS:dns>
         $<TARGET_OBJECTS:ftp_telnet>
         $<TARGET_OBJECTS:gtp_inspect>
         $<TARGET_OBJECTS:iec104>
@@ -50,6 +49,7 @@ set(STATIC_SERVICE_INSPECTOR_PLUGINS
     $<TARGET_OBJECTS:http2_inspect>
     $<TARGET_OBJECTS:sip>
     $<TARGET_OBJECTS:ssl>
+    $<TARGET_OBJECTS:dns>
     ${STATIC_INSPECTOR_OBJS}
     CACHE INTERNAL "STATIC_SERVICE_INSPECTOR_PLUGINS"
 )
index 151d3ae573c1ebbaaca8a1bacabb763dbbb456ed..9a5ca65ed3c6cf8719cb7d8be55be46f6da3cb04 100644 (file)
@@ -2,16 +2,18 @@
 set( FILE_LIST
     dns.cc
     dns.h
+    dns_config.cc
+    dns_config.h
     dns_module.cc
     dns_module.h
     dns_splitter.cc
     dns_splitter.h
 )
 
-if (STATIC_INSPECTORS)
-    add_library( dns OBJECT ${FILE_LIST})
+if (STATIC_INSPECTORS)
+add_library( dns OBJECT ${FILE_LIST})
 
-else (STATIC_INSPECTORS)
-    add_dynamic_module(dns inspectors ${FILE_LIST})
+else (STATIC_INSPECTORS)
+    add_dynamic_module(dns inspectors ${FILE_LIST})
 
-endif (STATIC_INSPECTORS)
+endif (STATIC_INSPECTORS)
index eb5b6610424178e780e93f90038d45268c823633..b874faa4e538dc0c6edc5a08f0bb9c934b9ee169 100644 (file)
@@ -28,6 +28,7 @@
 #include "dns.h"
 
 #include "detection/detection_engine.h"
+#include "dns_config.h"
 #include "log/messages.h"
 #include "profiler/profiler.h"
 #include "protocols/packet.h"
@@ -35,6 +36,7 @@
 
 #include "dns_module.h"
 #include "dns_splitter.h"
+#include "pub_sub/dns_events.h"
 
 using namespace snort;
 
@@ -58,13 +60,12 @@ const PegInfo dns_peg_names[] =
 /*
  * Function prototype(s)
  */
-static void snort_dns(Packet* p);
+static void snort_dns(Packet* p, const DnsConfig* dns_config);
 
 unsigned DnsFlowData::inspector_id = 0;
 
 DnsFlowData::DnsFlowData() : FlowData(inspector_id)
 {
-    memset(&session, 0, sizeof(session));
     dnsstats.concurrent_sessions++;
     if(dnsstats.max_concurrent_sessions < dnsstats.concurrent_sessions)
         dnsstats.max_concurrent_sessions = dnsstats.concurrent_sessions;
@@ -76,6 +77,16 @@ DnsFlowData::~DnsFlowData()
     dnsstats.concurrent_sessions--;
 }
 
+bool DNSData::publish_response() const
+{
+    return (dns_config->publish_response and state == DNS_RESP_STATE_ANS_RR);
+}
+
+bool DNSData::has_events() const
+{
+    return !dns_events.empty();
+}
+
 static DNSData* SetNewDNSData(Packet* p)
 {
     DnsFlowData* fd;
@@ -109,7 +120,6 @@ static DNSData* get_dns_session_data(Packet* p, bool from_server, DNSData& udpSe
                 return nullptr;
         }
 
-        memset(&udpSessionData, 0, sizeof(udpSessionData));
         return &udpSessionData;
     }
 
@@ -280,7 +290,7 @@ static uint16_t ParseDNSHeader(
 }
 
 static uint16_t ParseDNSName(
-    const unsigned char* data, uint16_t bytes_unused, DNSData* dnsSessionData)
+    const unsigned char* data, uint16_t bytes_unused, DNSData* dnsSessionData, bool parse_dns_name = false)
 {
     uint16_t bytes_required = dnsSessionData->curr_txt.txt_len -
         dnsSessionData->curr_txt.txt_bytes_seen;
@@ -338,7 +348,23 @@ static uint16_t ParseDNSName(
                 {
                     /* If this one is a relative offset, read that extra byte */
                     dnsSessionData->curr_txt.offset |= *data;
+                    if (parse_dns_name)
+                    {
+                        // parse recursively relative name
+                        dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_SIZE;
+                        return ParseDNSName(&dnsSessionData->data[0] + dnsSessionData->curr_txt.offset,
+                            dnsSessionData->bytes_unused, dnsSessionData, parse_dns_name);
+                    }
                 }
+
+                if (parse_dns_name)
+                {
+                    if (!dnsSessionData->curr_txt.dns_name.empty())
+                        dnsSessionData->curr_txt.dns_name += ".";
+
+                    dnsSessionData->curr_txt.dns_name.append((const char*)data, bytes_required);
+                }
+
                 data += bytes_required;
                 dnsSessionData->bytes_seen_curr_rec += bytes_required;
                 dnsSessionData->curr_txt.txt_bytes_seen += bytes_required;
@@ -384,7 +410,7 @@ static uint16_t ParseDNSQuestion(
         if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE)
         {
             dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_TYPE;
-            memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+            dnsSessionData->curr_txt = DNSNameState();
             data = data + bytes_used;
             bytes_unused = new_bytes_unused;
 
@@ -451,13 +477,16 @@ static uint16_t ParseDNSAnswer(
 
     if (dnsSessionData->curr_rec_state < DNS_RESP_STATE_RR_NAME_COMPLETE)
     {
+        if (dnsSessionData->publish_response())
+            dnsSessionData->cur_fqdn_event = DnsResponseFqdn(data, bytes_unused, dnsSessionData);
+
         uint16_t new_bytes_unused = ParseDNSName(data, bytes_unused, dnsSessionData);
         uint16_t bytes_used = bytes_unused - new_bytes_unused;
 
         if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE)
         {
             dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TYPE;
-            memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+            dnsSessionData->curr_txt = DNSNameState();
             data = data + bytes_used;
         }
         bytes_unused = new_bytes_unused;
@@ -717,8 +746,22 @@ static uint16_t ParseDNSRData(
         bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
         break;
     case DNS_RR_TYPE_A:
-    case DNS_RR_TYPE_NS:
+    case DNS_RR_TYPE_AAAA:
+        if (dnsSessionData->publish_response())
+        {
+            dnsSessionData->dns_events.add_fqdn(dnsSessionData->cur_fqdn_event, dnsSessionData->curr_rr.ttl);
+            dnsSessionData->dns_events.add_ip(DnsResponseIp(data, dnsSessionData->curr_rr.type));
+        }
+
+        bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
+        break;
     case DNS_RR_TYPE_CNAME:
+        if (dnsSessionData->publish_response())
+            dnsSessionData->dns_events.add_fqdn(dnsSessionData->cur_fqdn_event, dnsSessionData->curr_rr.ttl);
+
+        bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
+        break;
+    case DNS_RR_TYPE_NS:
     case DNS_RR_TYPE_SOA:
     case DNS_RR_TYPE_WKS:
     case DNS_RR_TYPE_PTR:
@@ -736,11 +779,17 @@ static uint16_t ParseDNSRData(
     return bytes_unused;
 }
 
-static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
+static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData, bool& needNextPacket)
 {
     uint16_t bytes_unused = p->dsize;
     int i;
     const unsigned char* data = p->data;
+    if (dnsSessionData->dns_config->publish_response and dnsSessionData->data.empty())
+    {
+        dnsSessionData->data.resize(bytes_unused);
+        memcpy((void*)&dnsSessionData->data[0], data, bytes_unused);
+        dnsSessionData->bytes_unused = bytes_unused;
+    }
 
     while (bytes_unused)
     {
@@ -767,7 +816,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
             }
             else
             {
-                /* No more data */
+                needNextPacket = true;
                 return;
             }
 
@@ -800,7 +849,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
                 }
                 else
                 {
-                    /* No more data */
+                    needNextPacket = true;
                     return;
                 }
             }
@@ -819,7 +868,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
 
                 if (bytes_unused == 0)
                 {
-                    /* No more data */
+                    needNextPacket = true;
                     return;
                 }
 
@@ -835,7 +884,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
                     bytes_unused = ParseDNSRData(data, bytes_unused, dnsSessionData);
                     if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE)
                     {
-                        /* Out of data, pick up on the next packet */
+                        needNextPacket = true;
                         return;
                     }
                     else
@@ -847,7 +896,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
                         if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
                         {
                             /* Reset the state tracking for this record */
-                            memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+                            dnsSessionData->curr_txt = DNSNameState();
                         }
                         data = p->data + (p->dsize - bytes_unused);
                     }
@@ -892,7 +941,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
                         if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
                         {
                             /* Reset the state tracking for this record */
-                            memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+                            dnsSessionData->curr_txt = DNSNameState();
                         }
                         data = p->data + (p->dsize - bytes_unused);
                     }
@@ -937,7 +986,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
                         if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
                         {
                             /* Reset the state tracking for this record */
-                            memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+                            dnsSessionData->curr_txt = DNSNameState();
                         }
                         data = p->data + (p->dsize - bytes_unused);
                     }
@@ -951,7 +1000,103 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
     }
 }
 
-static void snort_dns(Packet* p)
+SfIp DnsResponseIp::get_ip()
+{
+    SfIp ip;
+    int family = 0;
+    switch (type)
+    {
+        case DNS_RR_TYPE_A:
+            family = AF_INET;
+            break;
+        case DNS_RR_TYPE_AAAA:
+            family = AF_INET6;
+            break;
+    }
+
+    if (family and strlen((const char*)data))
+        ip.set(data, family);
+
+    return ip;
+}
+
+FqdnTtl DnsResponseFqdn::get_fqdn()
+{
+    std::string dns_name;
+    ParseDNSName(data, bytes_unused, dnsSessionData.get(), true);
+
+    if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE)
+        dnsSessionData->curr_txt.get_dns_name(dns_name);
+
+    return FqdnTtl(dns_name, dnsSessionData->curr_rr.ttl);
+}
+
+void DnsResponseFqdn::update_ttl(uint32_t ttl)
+{
+    dnsSessionData->curr_rr.ttl = ttl;
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Dns : public Inspector
+{
+public:
+    Dns(DnsModule*);
+    ~Dns() override;
+
+    void eval(Packet*) override;
+    StreamSplitter* get_splitter(bool) override;
+    bool configure(snort::SnortConfig*) override;
+    void show(const snort::SnortConfig*) const override;
+    static unsigned get_pub_id() { return pub_id; }
+
+private:
+    const DnsConfig* config = nullptr;
+    static unsigned pub_id;
+};
+
+unsigned Dns::pub_id = 0;
+
+Dns::Dns(DnsModule* m)
+{
+    config = m->get_config();
+    assert(config);
+}
+
+Dns::~Dns()
+{
+    delete config;
+}
+
+void Dns::show(const SnortConfig*) const
+{
+    config->show();
+}
+
+void Dns::eval(Packet* p)
+{
+    // precondition - what we registered for
+    assert((p->is_udp() and p->dsize and p->data) or p->has_tcp_data());
+    assert(p->flow);
+
+    ++dnsstats.packets;
+    snort_dns(p, config);
+}
+
+bool Dns::configure(snort::SnortConfig*)
+{
+    pub_id = DataBus::get_id(dns_pub_key);
+    return true;
+}
+
+StreamSplitter* Dns::get_splitter(bool c2s)
+{
+    return new DnsSplitter(c2s);
+}
+
+static void snort_dns(Packet* p, const DnsConfig* dns_config)
 {
     Profile profile(dnsPerfStats);
 
@@ -989,9 +1134,14 @@ static void snort_dns(Packet* p)
     if (dnsSessionData->flags & DNS_FLAG_NOT_DNS)
         return;
 
+    dnsSessionData->dns_config = dns_config;
     if ( from_server )
     {
-        ParseDNSResponseMessage(p, dnsSessionData);
+        bool needNextPacket = false;
+        ParseDNSResponseMessage(p, dnsSessionData, needNextPacket);
+
+        if (!needNextPacket and dnsSessionData->has_events())
+            DataBus::publish(Dns::get_pub_id(), DnsEventIds::DNS_RESPONSE_DATA, dnsSessionData->dns_events);
     }
     else
     {
@@ -999,37 +1149,6 @@ static void snort_dns(Packet* p)
     }
 }
 
-//-------------------------------------------------------------------------
-// class stuff
-//-------------------------------------------------------------------------
-
-class Dns : public Inspector
-{
-public:
-    Dns(DnsModule*);
-
-    void eval(Packet*) override;
-    StreamSplitter* get_splitter(bool) override;
-};
-
-Dns::Dns(DnsModule*)
-{ }
-
-void Dns::eval(Packet* p)
-{
-    // precondition - what we registered for
-    assert((p->is_udp() and p->dsize and p->data) or p->has_tcp_data());
-    assert(p->flow);
-
-    ++dnsstats.packets;
-    snort_dns(p);
-}
-
-StreamSplitter* Dns::get_splitter(bool c2s)
-{
-    return new DnsSplitter(c2s);
-}
-
 //-------------------------------------------------------------------------
 // api stuff
 //-------------------------------------------------------------------------
index b981e5bb8066dba803218da10ce7cfb534512f03..4dceb409ffe3a4f276f80cb4cf479c2cef2eebae 100644 (file)
@@ -24,6 +24,8 @@
 
 #include "flow/flow.h"
 
+#include "pub_sub/dns_events.h"
+
 // Implementation header with definitions, datatypes and flowdata class for
 // DNS service inspector.
 
 
 struct DNSHdr
 {
-    uint16_t id;
-    uint16_t flags;
-    uint16_t questions;
-    uint16_t answers;
-    uint16_t authorities;
-    uint16_t additionals;
+    uint16_t id = 0;
+    uint16_t flags = 0;
+    uint16_t questions = 0;
+    uint16_t answers = 0;
+    uint16_t authorities = 0;
+    uint16_t additionals = 0;
 };
 
 #define DNS_HDR_FLAG_REPLY_CODE_MASK        0x000F
@@ -54,29 +56,36 @@ struct DNSHdr
 
 struct DNSQuestion
 {
-    uint16_t type;
-    uint16_t dns_class;
+    uint16_t type = 0;
+    uint16_t dns_class = 0;
 };
 
 struct DNSRR
 {
-    uint16_t type;
-    uint16_t dns_class;
-    uint32_t ttl;
-    uint16_t length;
+    uint16_t type = 0;
+    uint16_t dns_class = 0;
+    uint32_t ttl = 0;
+    uint16_t length = 0;
 };
 
 // FIXIT-L replace alerted/relative to bool?
 struct DNSNameState
 {
-    uint32_t txt_count;
-    uint32_t total_txt_len;
-    uint8_t txt_len;
-    uint8_t txt_bytes_seen;
-    uint8_t name_state;
-    uint8_t alerted;
-    uint16_t offset;
-    uint8_t relative;
+    uint32_t txt_count = 0;
+    uint32_t total_txt_len = 0;
+    uint8_t txt_len = 0;
+    uint8_t txt_bytes_seen = 0;
+    uint8_t name_state = 0;
+    uint8_t alerted = 0;
+    uint16_t offset = 0;
+    uint8_t relative = 0;
+    std::string dns_name;
+
+    void get_dns_name(std::string& name) const
+    {
+        if (dns_name.size())
+            name = dns_name;
+    }
 };
 
 // FIXIT-L  remove obsolete flags?
@@ -96,6 +105,7 @@ struct DNSNameState
 #define DNS_RR_TYPE_MINFO                   0x000e // experimental
 #define DNS_RR_TYPE_MX                      0x000f
 #define DNS_RR_TYPE_TXT                     0x0010
+#define DNS_RR_TYPE_AAAA                    0x001c
 
 #define DNS_FLAG_NOT_DNS                0x01
 
@@ -149,21 +159,68 @@ struct DNSNameState
 #define DNS_RESP_STATE_AUTH_RR          0x50
 #define DNS_RESP_STATE_ADD_RR           0x60
 
+class DnsConfig;
+struct DNSData;
+
+class DnsResponseFqdn
+{
+public:
+    DnsResponseFqdn()
+    {}
+
+    DnsResponseFqdn(const unsigned char* data, uint16_t bytes_unused, DNSData* dnsSessionData) :
+        data(data), bytes_unused(bytes_unused), dnsSessionData(std::make_shared<DNSData>(*dnsSessionData))
+    {}
+
+    FqdnTtl get_fqdn();
+    void update_ttl(uint32_t ttl);
+
+private:
+    const unsigned char* data = nullptr;
+    uint16_t bytes_unused = 0;
+    std::shared_ptr<DNSData> dnsSessionData;
+};
+
 // Per-session data block containing current state
 // of the DNS inspector for the session.
 struct DNSData
 {
-    uint32_t state;               // The current state of the session.
-    uint16_t curr_rec;            // Record number for the current record
-    uint16_t curr_rec_length;
-    uint16_t bytes_seen_curr_rec;
-    uint16_t length;
-    uint8_t curr_rec_state;
+    uint32_t state = 0;               // The current state of the session.
+    uint16_t curr_rec = 0;            // Record number for the current record
+    uint16_t curr_rec_length = 0;
+    uint16_t bytes_seen_curr_rec = 0;
+    uint16_t length = 0;
+    uint16_t bytes_unused = 0;
+    uint8_t curr_rec_state = 0;
     DNSHdr hdr;                   // Copy of the data from the DNS Header
     DNSQuestion curr_q;
     DNSRR curr_rr;
     DNSNameState curr_txt;
-    uint8_t flags;
+    uint8_t flags = 0;
+    std::vector<unsigned char> data;
+    const DnsConfig* dns_config = nullptr;
+    snort::DnsResponseDataEvents dns_events;
+    DnsResponseFqdn cur_fqdn_event;
+
+    bool publish_response() const;
+    bool has_events() const;
+};
+
+class DnsResponseIp
+{
+public:
+    DnsResponseIp()
+    {}
+
+    DnsResponseIp(const unsigned char* data, uint16_t type) :
+        data(data), type(type)
+    {}
+
+    snort::SfIp get_ip();
+
+private:
+    const unsigned char* data = nullptr;
+    uint16_t type = 0;
 };
 
 class DnsFlowData : public snort::FlowData
diff --git a/src/service_inspectors/dns/dns_config.cc b/src/service_inspectors/dns/dns_config.cc
new file mode 100644 (file)
index 0000000..567c55c
--- /dev/null
@@ -0,0 +1,34 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// dns_config.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dns_config.h"
+
+#include "log/messages.h"
+
+using namespace snort;
+
+void DnsConfig::show() const
+{
+    ConfigLogger::log_value("publish_response", publish_response);
+}
diff --git a/src/service_inspectors/dns/dns_config.h b/src/service_inspectors/dns/dns_config.h
new file mode 100644 (file)
index 0000000..0ac9d47
--- /dev/null
@@ -0,0 +1,35 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// dns_config.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef DNS_CONFIG_H
+#define DNS_CONFIG_H
+
+class DnsConfig
+{
+public:
+    DnsConfig() = default;
+    ~DnsConfig() = default;
+
+    void show() const;
+
+    bool publish_response = false;
+};
+
+#endif
index a45eceec1085a9de7e1314645887796e1519e582..b4b12cc0e4a7d83571f440e4d46ea1467a0b114b 100644 (file)
@@ -24,6 +24,8 @@
 
 #include "dns_module.h"
 
+#include "dns_config.h"
+
 using namespace snort;
 using namespace std;
 
@@ -36,6 +38,8 @@ using namespace std;
 
 static const Parameter s_params[] =
 {
+    { "publish_response", Parameter::PT_BOOL, nullptr, "false", "parse and publish dns responses" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -55,6 +59,29 @@ static const RuleMap dns_rules[] =
 DnsModule::DnsModule() : Module(DNS_NAME, DNS_HELP, s_params)
 { }
 
+DnsModule::~DnsModule()
+{
+    delete config;
+}
+
+bool DnsModule::begin(const char*, int, SnortConfig*)
+{
+    if (!config)
+        config = new DnsConfig;
+
+    return true;
+}
+
+bool DnsModule::set(const char*, snort::Value& val, snort::SnortConfig*)
+{
+    if (val.is("publish_response"))
+    {
+        config->publish_response = val.get_bool();
+    }
+
+    return true;
+}
+
 const RuleMap* DnsModule::get_rules() const
 { return dns_rules; }
 
@@ -67,3 +94,10 @@ PegCount* DnsModule::get_counts() const
 ProfileStats* DnsModule::get_profile() const
 { return &dnsPerfStats; }
 
+const DnsConfig* DnsModule::get_config()
+{
+    DnsConfig* tmp = config;
+    config = nullptr;
+    return tmp;
+}
+
index dd9b43b8ef0127c68df6a0e9054b706fc9d331f8..c14bceb86adc0743f9dd843adee4579c6c2c37d8 100644 (file)
@@ -40,6 +40,7 @@ struct SnortConfig;
 #define DNS_NAME "dns"
 #define DNS_HELP "dns inspection"
 
+class DnsConfig;
 
 struct DnsStats
 {
@@ -58,9 +59,10 @@ class DnsModule : public snort::Module
 {
 public:
     DnsModule();
+    ~DnsModule() override;
 
-    bool set(const char*, snort::Value&, snort::SnortConfig*) override
-    { return false; }
+    bool begin(const char*, int, snort::SnortConfig*) override;
+    bool set(const char*, snort::Value&, snort::SnortConfig*) override;
 
     unsigned get_gid() const override
     { return GID_DNS; }
@@ -69,12 +71,16 @@ public:
     const PegInfo* get_pegs() const override;
     PegCount* get_counts() const override;
     snort::ProfileStats* get_profile() const override;
+    const DnsConfig* get_config();
 
     Usage get_usage() const override
     { return INSPECT; }
 
     bool is_bindable() const override
     { return true; }
+
+private:
+    DnsConfig* config = nullptr;
 };
 
 #endif