1#!/usr/bin/python3 2 3# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4# 5# SPDX-License-Identifier: MPL-2.0 6# 7# This Source Code Form is subject to the terms of the Mozilla Public 8# License, v. 2.0. If a copy of the MPL was not distributed with this 9# file, you can obtain one at https://mozilla.org/MPL/2.0/. 10# 11# See the COPYRIGHT file distributed with this work for additional 12# information regarding copyright ownership. 13 14import os 15import re 16import subprocess 17 18import pytest 19 20pytest.importorskip("dns", minversion="2.0.0") 21import dns.resolver 22 23 24def run_rndc(server, rndc_command): 25 """ 26 Send the specified 'rndc_command' to 'server' with a timeout of 10 seconds 27 """ 28 rndc = os.getenv("RNDC") 29 port = os.getenv("CONTROLPORT") 30 31 cmdline = [rndc, "-c", "../_common/rndc.conf", "-p", port, "-s", server] 32 cmdline.extend(rndc_command) 33 34 subprocess.check_output(cmdline, stderr=subprocess.STDOUT, timeout=10) 35 36 37def test_dnstap_dispatch_socket_addresses(named_port): 38 # Prepare for querying ns3. 39 resolver = dns.resolver.Resolver() 40 resolver.nameservers = ["10.53.0.3"] 41 resolver.port = named_port 42 43 # Send some query to ns3 so that it records something in its dnstap file. 44 ans = resolver.resolve("mail.example.", "A") 45 assert ans[0].address == "10.0.0.2" 46 47 # Before continuing, roll dnstap file to ensure it is flushed to disk. 48 run_rndc("10.53.0.3", ["dnstap", "-roll", "1"]) 49 50 # Move the dnstap file aside so that it is retained for troubleshooting. 51 os.rename(os.path.join("ns3", "dnstap.out.0"), "dnstap.out.resolver_addresses") 52 53 # Read the contents of the dnstap file using dnstap-read. 54 output = subprocess.check_output( 55 [os.getenv("DNSTAPREAD"), "dnstap.out.resolver_addresses"], encoding="utf-8" 56 ) 57 58 # Check whether all frames contain the expected addresses. 59 # 60 # Expected dnstap-read output format: 61 # 62 # 22-Jun-2022 12:09:06.168 RR 10.53.0.3:0 -> 10.53.0.1:7523 TCP ... 63 # 22-Jun-2022 12:09:06.168 RR 10.53.0.3:0 <- 10.53.0.1:7523 TCP ... 64 # 22-Jun-2022 12:09:06.168 RQ 10.53.0.3:56306 -> 10.53.0.2:7523 UDP ... 65 # 22-Jun-2022 12:09:06.168 RQ 10.53.0.3:56306 <- 10.53.0.2:7523 UDP ... 66 # 67 bad_frames = [] 68 inspected_frames = 0 69 addr_regex = r"^10\.53\.0\.[0-9]+:[0-9]{1,5}$" 70 for line in output.splitlines(): 71 _, _, frame_type, addr1, _, addr2, _ = line.split(" ", 6) 72 # Only inspect RESOLVER_QUERY and RESOLVER_RESPONSE frames. 73 if frame_type not in ("RQ", "RR"): 74 continue 75 inspected_frames += 1 76 if not re.match(addr_regex, addr1) or not re.match(addr_regex, addr2): 77 bad_frames.append(line) 78 79 assert ( 80 len(bad_frames) == 0 81 ), "{} out of {} inspected frames contain unexpected addresses:\n\n{}".format( 82 len(bad_frames), inspected_frames, "\n".join(bad_frames) 83 ) 84