diff --git a/etc/fields.yml b/etc/fields.yml index 1d9c3d523d3..f6bac432295 100644 --- a/etc/fields.yml +++ b/etc/fields.yml @@ -269,6 +269,12 @@ event: The data describing the resource. The meaning of this data depends on the type and class of the resource record. + - name: dns.authorities + type: dict + description: > + An array containing a dictionary for each authority section from the + answer. + - name: dns.authorities_count type: int description: > @@ -288,6 +294,12 @@ event: description: Class of DNS data contained in this resource record. example: IN + - name: dns.answers + type: dict + description: > + An array containing a dictionary about each answer section returned by + the server. + - name: dns.answers.ttl description: > Time interval in seconds that this resource record may be cached @@ -300,6 +312,12 @@ event: The data describing the resource. The meaning of this data depends on the type and class of the resource record. + - name: dns.additionals + type: dict + description: > + An array containing a dictionary for each additional section from the + answer. + - name: dns.additionals_count type: int description: > diff --git a/tests/pcaps/dns_additional.pcap b/tests/pcaps/dns_additional.pcap new file mode 100644 index 00000000000..0e8962e9a3e Binary files /dev/null and b/tests/pcaps/dns_additional.pcap differ diff --git a/tests/pcaps/dns_google_com.pcap b/tests/pcaps/dns_google_com.pcap new file mode 100644 index 00000000000..15aea7e9776 Binary files /dev/null and b/tests/pcaps/dns_google_com.pcap differ diff --git a/tests/pcaps/dns_mx.pcap b/tests/pcaps/dns_mx.pcap new file mode 100644 index 00000000000..958f38e1349 Binary files /dev/null and b/tests/pcaps/dns_mx.pcap differ diff --git a/tests/pcaps/dns_not_found.pcap b/tests/pcaps/dns_not_found.pcap new file mode 100644 index 00000000000..07dc1169e4d Binary files /dev/null and b/tests/pcaps/dns_not_found.pcap differ diff --git a/tests/pcaps/dns_ns.pcap b/tests/pcaps/dns_ns.pcap new file mode 100644 index 00000000000..c4b1761c474 Binary files /dev/null and b/tests/pcaps/dns_ns.pcap differ diff --git a/tests/pcaps/dns_txt.pcap b/tests/pcaps/dns_txt.pcap new file mode 100644 index 00000000000..e9916bdb02b Binary files /dev/null and b/tests/pcaps/dns_txt.pcap differ diff --git a/tests/templates/packetbeat.yml.j2 b/tests/templates/packetbeat.yml.j2 index 7c16f8c29c2..b36d507d792 100644 --- a/tests/templates/packetbeat.yml.j2 +++ b/tests/templates/packetbeat.yml.j2 @@ -121,6 +121,13 @@ protocols: {% if mongodb_max_docs is not none %} max_docs: {{mongodb_max_docs}}{%endif %} {% if mongodb_max_doc_length is not none %} max_doc_length: {{mongodb_max_doc_length}}{%endif%} + dns: + ports: [{{ dns_ports|default([53])|join(", ") }}] +{% if dns_include_authorities %} include_authorities: true{% endif %} +{% if dns_include_additionals %} include_additionals: true{% endif %} +{% if dns_send_request %} send_request: true{% endif %} +{% if dns_send_response %} send_response: true{% endif %} + ############################# Filters ############################################ diff --git a/tests/test_0012_http_basicauth.py b/tests/test_0012_http_basicauth.py index a2b2525938c..896297649a4 100644 --- a/tests/test_0012_http_basicauth.py +++ b/tests/test_0012_http_basicauth.py @@ -6,6 +6,8 @@ class Test(TestCase): def test_http_auth(self): self.render_config_template( + dns_ports=[], # disable dns because the pcap + # contains the DNS query http_send_all_headers=1, http_strip_authorization=1, http_send_request=True diff --git a/tests/test_0032_dns.py b/tests/test_0032_dns.py new file mode 100644 index 00000000000..7755f43db10 --- /dev/null +++ b/tests/test_0032_dns.py @@ -0,0 +1,181 @@ +from pbtests.packetbeat import TestCase + +""" +Tests for the DNS protocol. +""" + + +class Test(TestCase): + def test_A(self): + """ + Should correctly interpret an A query to google.com + """ + self.render_config_template( + dns_ports=[53], + ) + self.run_packetbeat(pcap="dns_google_com.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert o["query"] == "class IN, type A, google.com" + assert o["dns.question.type"] == "A" + assert o["status"] == "OK" + assert len(o["dns.answers"]) == 16 + assert all(x["type"] == "A" for x in o["dns.answers"]) + + def test_A_not_found(self): + """ + Should correctly interpret an A query to google.com + """ + self.render_config_template( + dns_ports=[53], + ) + self.run_packetbeat(pcap="dns_not_found.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert o["query"] == "class IN, type A, nothing.elastic.co" + assert o["dns.question.type"] == "A" + assert o["status"] == "Error" + assert o["dns.response_code"] == "NXDOMAIN" + assert o["dns.answers_count"] == 0 + assert o["dns.authorities_count"] == 1 + assert "dns.authorities" not in o # include authorities defaults to 0 + + def test_MX(self): + """ + Should correctly interpret an MX query to elastic.co + """ + self.render_config_template( + dns_ports=[53], + ) + self.run_packetbeat(pcap="dns_mx.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert o["query"] == "class IN, type MX, elastic.co" + assert o["dns.question.type"] == "MX" + assert o["status"] == "OK" + + def test_NS(self): + """ + Should correctly interpret an NS query to elastic.co + """ + self.render_config_template( + dns_ports=[53], + ) + self.run_packetbeat(pcap="dns_ns.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert o["query"] == "class IN, type NS, elastic.co" + assert o["dns.question.type"] == "NS" + assert o["status"] == "OK" + + def test_TXT(self): + """ + Should correctly interpret an TXT query to elastic.co + """ + self.render_config_template( + dns_ports=[53], + ) + self.run_packetbeat(pcap="dns_txt.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert o["ip"] == "8.8.8.8" + assert o["query"] == "class IN, type TXT, elastic.co" + assert o["dns.question.type"] == "TXT" + assert o["status"] == "OK" + assert len(o["dns.answers"]) == 2 + assert all(x["type"] == "TXT" for x in o["dns.answers"]) + assert "request" not in o + assert "response" not in o + + def test_include_authorities(self): + """ + Should include DNS authorities when configured. + """ + self.render_config_template( + dns_ports=[53], + dns_include_authorities=True + ) + + self.run_packetbeat(pcap="dns_not_found.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["dns.authorities_count"] == 1 + assert "dns.authorities" in o + assert len(o["dns.authorities"]) == 1 + + def test_include_additionals(self): + """ + Should include DNS authorities when configured. + """ + self.render_config_template( + dns_ports=[53], + dns_include_additionals=True + ) + + self.run_packetbeat(pcap="dns_additional.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["dns.additionals_count"] == 1 + assert "dns.additionals" in o + assert len(o["dns.additionals"]) == 1 + + def test_send_request_response(self): + """ + Should correctly interpret an TXT query to elastic.co + """ + self.render_config_template( + dns_ports=[53], + dns_send_request=True, + dns_send_response=True + ) + self.run_packetbeat(pcap="dns_txt.pcap") + + objs = self.read_output() + assert len(objs) == 1 + o = objs[0] + + assert o["type"] == "dns" + assert o["transport"] == "udp" + assert o["method"] == "QUERY" + assert "request" in o + assert "response" in o + assert "elastic.co" in o["request"] + assert "include:_spf.google.com" in o["response"]