diff --git a/docs/device_report.md b/docs/device_report.md index a461268da2..83c4ff8fa4 100644 --- a/docs/device_report.md +++ b/docs/device_report.md @@ -56,7 +56,7 @@ Overall device result FAIL |---|---|---|---|---|---| |Required|1|0|0|0|0| |Recommended|1|0|0|0|1| -|Other|6|2|20|1|2| +|Other|6|2|21|1|2| |Result|Test|Category|Expectation|Notes| |---|---|---|---|---| @@ -67,6 +67,7 @@ Overall device result FAIL |skip|cloud.udmi.state|Other|Other|No device id| |skip|cloud.udmi.system|Other|Other|No device id| |info|communication.type.broadcast|Other|Other|Broadcast packets received. Unicast packets received.| +|skip|connection.dns.hostname_connect|Other|Other|Device did not send any DNS requests| |fail|connection.mac_oui|Other|Other|Manufacturer prefix not found!| |pass|connection.min_send|Other|Other|ARP packets received. Data packets were sent at a frequency of less than 5 minutes| |pass|connection.network.ntp_support|Other|Other|Using NTPv4.| @@ -577,6 +578,12 @@ Mac OUI Test -------------------- RESULT fail connection.mac_oui Manufacturer prefix not found! +-------------------- +connection.dns.hostname_connect +-------------------- +Check device uses the DNS server from DHCP and resolves hostnames +-------------------- +RESULT skip connection.dns.hostname_connect Device did not send any DNS requests ``` #### Module Config diff --git a/subset/network/README.md b/subset/network/README.md index d4868e113d..677a519931 100644 --- a/subset/network/README.md +++ b/subset/network/README.md @@ -65,3 +65,12 @@ static resource on the source code repo. ### Conditions for mac_oui - pass -> if the MAC OUI matches the mac prefix IEEE registration. - fail -> if the MAC OUI does not match with any of the mac prefixes. + + +## DNS Tests +Check Device uses the DNS server from DHCP and resolves hostnames + +### Conditions for connection.dns.hostname_connect + - pass -> if the device uses the DNS server from DHCP, and resolves a hostname + - fail -> device uses a DNS serveer other than the server fron DHCP + - skip -> device did not send any DNS requests \ No newline at end of file diff --git a/subset/network/dns_tests.py b/subset/network/dns_tests.py new file mode 100644 index 0000000000..f8ebfde258 --- /dev/null +++ b/subset/network/dns_tests.py @@ -0,0 +1,183 @@ +""" + This script can be called to run DNS related test. + +""" +from __future__ import absolute_import +import subprocess +import sys + +import re +import datetime + +arguments = sys.argv + +test_request = str(arguments[1]) +cap_pcap_file = str(arguments[2]) +device_address = str(arguments[3]) + +report_filename = 'dns_tests.txt' +min_packet_length_bytes = 20 +max_packets_in_report = 10 +port_list = [] +ignore = '%%' +summary_text = '' +result = 'fail' +dash_break_line = '--------------------\n' + +DESCRIPTION_HOSTNAME_CONNECT = 'Check device uses the DNS server from DHCP and resolves hostnames' + +TCPDUMP_DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f" + +IP_REGEX = r'(([0-9]{1,3}\.){3}[0-9]{1,3})' +RDATA_REGEX = r'' + +DNS_SERVER_HOST = '.2' + + +def write_report(string_to_append): + print(string_to_append.strip()) + with open(report_filename, 'a+') as file_open: + file_open.write(string_to_append) + + +def exec_tcpdump(tcpdump_filter, capture_file=None): + """ + Args + tcpdump_filter: Filter to pass onto tcpdump file + capture_file: Optional capture file to look + + Returns + List of packets matching the filter + """ + + capture_file = cap_pcap_file if capture_file is None else capture_file + command = 'tcpdump -tttt -n -r {} {}'.format(capture_file, tcpdump_filter) + + process = subprocess.Popen(command, + universal_newlines=True, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + text = str(process.stdout.read()).rstrip() + + if text: + return text.split("\n") + + return [] + + +def add_summary(text): + global summary_text + summary_text = summary_text + " " + text if summary_text else text + + +def get_dns_server_from_ip(ip_address): + """ + Returns the IP address of the DNS server provided by DAQ + + Args + ip_address: IP address of the device under test + + Returns + IP address of DNS server + """ + + return re.sub(r'\.\d+$', DNS_SERVER_HOST, ip_address) + + +def check_communication_for_response(response_line): + """ + Given a line from the TCPdump output for DNS responses + Look through the packet capture to see if any communitication to the + IP addresses from the DNS + + Args + tcpdump_line: Line from tcpdump filtered to DNS resposnes + + Returns + True/False if the device has communicated with an IP from the + DNS response after it has recieved it + """ + + response_time = datetime.datetime.strptime(response_line[:26], TCPDUMP_DATE_FORMAT) + + # Use regex to extract all IP addresses in the response + matches = re.findall(IP_REGEX, response_line) + + # The first two IP addresses are the source/destination + ip_addresses = matches[2:] + + for address in ip_addresses: + packets = exec_tcpdump('dst host {}'.format(address[0])) + for packet in packets: + packet_time = datetime.datetime.strptime(packet[:26], TCPDUMP_DATE_FORMAT) + if packet_time > response_time: + return True + + return False + + +def test_dns(target_ip): + """ Runs the connection.dns.hostname_connect test + + Checks that: + i) the device sends DNS requests + ii) the device uses the DNS server from DHCP + iii) the device uses an IP address recieved from the DNS server + + Args + target_ip: IP address of the device + """ + + # Get server IP of the DHCP server + dhcp_dns_ip = get_dns_server_from_ip(target_ip) + + # Check if the device has sent any DNS requests + filter_to_dns = 'dst port 53 and src host {}'.format(target_ip) + to_dns = exec_tcpdump(filter_to_dns) + num_query_dns = len(to_dns) + + if num_query_dns == 0: + add_summary('Device did not send any DNS requests') + return 'skip' + + # Check if the device only sent DNS requests to the DHCP Server + filter_to_dhcp_dns = 'dst port 53 and src host {} and dst host {}' \ + .format(target_ip, dhcp_dns_ip) + + to_dhcp_dns = exec_tcpdump(filter_to_dhcp_dns) + num_query_dhcp_dns = len(to_dhcp_dns) + + if num_query_dns > num_query_dhcp_dns: + add_summary('Device sent DNS requests to servers other than the DHCP provided server') + return 'fail' + + # Retrieve responses from DNS + filter_dns_response = 'src port 53 and src host {}'.format(dhcp_dns_ip) + dns_responses = exec_tcpdump(filter_dns_response) + + num_dns_responses = len(dns_responses) + + if num_dns_responses == 0: + add_summary('No results recieved from DNS server') + return 'fail' + + # Check that the device has sent data packets to any of the IP addresses it has recieved + # it has recieved from the DNS requests + + for response in dns_responses: + if check_communication_for_response(response): + add_summary('Device sends DNS requests and resolves host names') + return 'pass' + + add_summary('Device did not send data to IP addresses retrieved from the DNS server') + return 'fail' + + +write_report("{b}{t}\n{b}".format(b=dash_break_line, t=test_request)) + +if test_request == 'connection.dns.hostname_connect': + write_report("{d}\n{b}".format(b=dash_break_line, d=DESCRIPTION_HOSTNAME_CONNECT)) + result = test_dns(device_address) + +write_report("RESULT {r} {t} {s}\n".format(r=result, t=test_request, s=summary_text.strip())) diff --git a/subset/network/test_network b/subset/network/test_network index e3a9344ffb..15b642f456 100755 --- a/subset/network/test_network +++ b/subset/network/test_network @@ -20,3 +20,7 @@ cat ntp_tests.txt >> $REPORT # MACOUI Test ./run_macoui_test $TARGET_MAC $REPORT +# DNS Tests +python dns_tests.py connection.dns.hostname_connect $MONITOR $TARGET_IP + +cat dns_tests.txt >> $REPORT diff --git a/testing/test_aux.out b/testing/test_aux.out index 36df1a799f..8a6bd9e9cf 100644 --- a/testing/test_aux.out +++ b/testing/test_aux.out @@ -58,16 +58,19 @@ RESULT info communication.type.broadcast Broadcast packets received. Unicast pac RESULT pass connection.network.ntp_support Using NTPv4. RESULT pass connection.network.ntp_update Device clock synchronized. RESULT fail connection.mac_oui Manufacturer prefix not found! +RESULT skip connection.dns.hostname_connect Device did not send any DNS requests RESULT pass connection.min_send ARP packets received. Data packets were sent at a frequency of less than 5 minutes RESULT info communication.type.broadcast Broadcast packets received. Unicast packets received. RESULT fail connection.network.ntp_support Not using NTPv4. RESULT fail connection.network.ntp_update Device clock not synchronized with local NTP server. RESULT pass connection.mac_oui Manufacturer: Google found for address 3c:5a:b4:1e:8f:0b +RESULT fail connection.dns.hostname_connect Device sent DNS requests to servers other than the DHCP provided server RESULT pass connection.min_send ARP packets received. Data packets were sent at a frequency of less than 5 minutes RESULT info communication.type.broadcast Broadcast packets received. Unicast packets received. RESULT skip connection.network.ntp_support No NTP packets received. RESULT skip connection.network.ntp_update Not enough NTP packets received. RESULT pass connection.mac_oui Manufacturer: Google found for address 3c:5a:b4:1e:8f:0a +RESULT pass connection.dns.hostname_connect Device sends DNS requests and resolves host names dhcp requests 1 1 1 1 3c5ab41e8f0a: [] 3c5ab41e8f0b: ['3c5ab41e8f0b:ping:TimeoutError'] diff --git a/testing/test_aux.sh b/testing/test_aux.sh index afddb3ae3c..d00e8267aa 100755 --- a/testing/test_aux.sh +++ b/testing/test_aux.sh @@ -67,9 +67,9 @@ interfaces: faux-1: opts: brute broadcast_client ntpv4 faux-2: - opts: nobrute expiredtls bacnetfail pubber passwordfail ntpv3 opendns ssh + opts: nobrute expiredtls bacnetfail pubber passwordfail ntpv3 opendns ssh curl faux-3: - opts: tls macoui passwordpass bacnet pubber broadcast_client ssh + opts: tls macoui passwordpass bacnet pubber broadcast_client ssh curl long_dhcp_response_sec: 0 monitor_scan_sec: 20 EOF @@ -117,8 +117,7 @@ capture_test_results macoui capture_test_results tls capture_test_results password capture_test_results discover -capture_test_results networ -capture_test_results ntp +capture_test_results network # Capture peripheral logs more inst/run-*/scans/ip_triggers.txt | cat