return (dns_ips.empty() or dns_fqdns.empty());
}
+void DnsResponseDataEvents::clear_data()
+{
+ dns_ips.clear();
+ dns_fqdns.clear();
+}
+
uint16_t DnsResponseEvent::get_trans_id() const
{
return session.hdr.id;
void add_fqdn(DnsResponseFqdn& event, uint32_t ttl);
void get_dns_data(IPFqdnCacheItem& ip_fqdn_cache_item);
bool empty() const;
+ void clear_data();
const Packet* get_packet() const override
{ return packet; }
DNS looks are DNS Response traffic over UDP and TCP and it requires Stream
inspector to be enabled for TCP decoding.
+
+DNS Over UDP:
+
+This has simpler packet scenarios. In a UDP flow between a client and a DNS server,
+the client may send one or multiple UDP packets to the server and each of the UDP
+packets contains a complete DNS query message which has a unique DNS transaction ID
+across this UDP flow. The server may send back one UDP packet containing a complete
+DNS response message matching a query transaction ID. This DNS inspector maintains
+minimum flow data for each DNS over UDP flow: it extracts the transaction ID from
+each DNS query message and saves it into the flow data object, and it parses a DNS
+response message to look for FQDN and IP mapping only when the response message's
+transaction ID matches one of the cached query transaction IDs. It marks the
+underlying UDP flow as "closed" when all the cached query transaction IDs have been
+matched.
+
+DNS Over TCP:
+
+This has relatively more complex packet scenarios. Each DNS query message sent from
+a DNS client may be split into multiple TCP packets and each DNS response message
+sent from a DNS server may also be split into multiple TCP packets, and there may
+be one or multiple pairs of DNS query and response messages exchanged within a single
+TCP connection. This DNS inspector creates a DNS session data object and saves it into
+the TCP connection's flow data cache and this session data object is reused to serve
+multiple DNS transactions in the connection: when a new DNS response message arrives,
+the DNS response parser first updates the data buffer with the new message data and
+clears the data fields in the session data's dns event object, then it will parse the
+response message to get the domain name and IP address mapping information and save it
+into the session data's dns event object. Eventually it will publish the enclosed DNS
+event object for the event subscribers to comsume. Each event subscriber may use the
+domain name and IP address mapping information in the event object to look up the
+actual domain name and IP address values saved in the session data object's data buffer.
+
if (dnsSessionData->length > 0)
dnsSessionData->curr_txt.offset += 2; // first two bytes are length in TCP
- if (parse_dns_name)
+ if (parse_dns_name && dnsSessionData->data.size() > dnsSessionData->curr_txt.offset)
{
- // parse recursively relative name
+ // If the name field is a pointer, then parse the name field at that offset only if
+ // the offset is within the bounds of the data buffer.
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);
uint16_t bytes_unused = p->dsize;
int i;
const unsigned char* data = p->data;
- if (dnsSessionData->dns_config->publish_response and dnsSessionData->data.empty())
+
+ // For DNS over TCP, it's possible that multiple DNS transactions may be processed in a single TCP connection.
+ // When a new transaction's DNS response message arrives, the reused DNS session's data field may not be empty,
+ // so we must use the field "state" to determine if we are processing a new DNS response message.
+ // For DNS over UDP, a new session data object is created for each DNS response message, so the following condition
+ // is always met as the data field is always empty.
+ if (dnsSessionData->dns_config->publish_response and (dnsSessionData->data.empty() or
+ dnsSessionData->state == DNS_RESP_STATE_LENGTH))
{
+ // We are at the beginning of a new DNS response message, so we need to clear the data field
+ // and the event object, which are reused by multiple DNS transactions in a single TCP connection.
dnsSessionData->data.resize(bytes_unused);
memcpy((void*)&dnsSessionData->data[0], data, bytes_unused);
dnsSessionData->bytes_unused = bytes_unused;
+ // For DNS over TCP, the reused event object may hold domain names and IP addresses extracted
+ // from previous DNS response message which must be cleared before processing a new DNS message.
+ dnsSessionData->dns_events.clear_data();
}
while (bytes_unused)