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 14# pylint: disable=unused-variable 15 16import socket 17import time 18 19import pytest 20 21pytest.importorskip("dns", minversion="2.0.0") 22import dns.edns 23import dns.message 24import dns.name 25import dns.query 26import dns.rdataclass 27import dns.rdatatype 28 29import isctest.mark # pylint: disable=import-error 30 31pytestmark = pytest.mark.extra_artifacts( 32 [ 33 "ns1/large.db", 34 ] 35) 36 37TIMEOUT = 10 38 39 40def create_msg(qname, qtype): 41 msg = dns.message.make_query( 42 qname, qtype, want_dnssec=True, use_edns=0, payload=4096 43 ) 44 return msg 45 46 47def timeout(): 48 return time.time() + TIMEOUT 49 50 51def test_initial_timeout(named_port): 52 # 53 # The initial timeout is 2.5 seconds, so this should timeout 54 # 55 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 56 sock.connect(("10.53.0.1", named_port)) 57 58 time.sleep(3) 59 60 msg = create_msg("example.", "A") 61 62 with pytest.raises(EOFError): 63 try: 64 dns.query.send_tcp(sock, msg, timeout()) 65 dns.query.receive_tcp(sock, timeout()) 66 except ConnectionError as e: 67 raise EOFError from e 68 69 70def test_idle_timeout(named_port): 71 # 72 # The idle timeout is 5 seconds, so the third message should fail 73 # 74 msg = create_msg("example.", "A") 75 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 76 sock.connect(("10.53.0.1", named_port)) 77 78 time.sleep(1) 79 80 dns.query.send_tcp(sock, msg, timeout()) 81 dns.query.receive_tcp(sock, timeout()) 82 83 time.sleep(2) 84 85 dns.query.send_tcp(sock, msg, timeout()) 86 dns.query.receive_tcp(sock, timeout()) 87 88 time.sleep(6) 89 90 with pytest.raises(EOFError): 91 try: 92 dns.query.send_tcp(sock, msg, timeout()) 93 dns.query.receive_tcp(sock, timeout()) 94 except ConnectionError as e: 95 raise EOFError from e 96 97 98def test_keepalive_timeout(named_port): 99 # 100 # Keepalive is 7 seconds, so the third message should succeed. 101 # 102 msg = create_msg("example.", "A") 103 kopt = dns.edns.GenericOption(11, b"\x00") 104 msg.use_edns(edns=True, payload=4096, options=[kopt]) 105 106 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 107 sock.connect(("10.53.0.1", named_port)) 108 109 time.sleep(1) 110 111 dns.query.send_tcp(sock, msg, timeout()) 112 dns.query.receive_tcp(sock, timeout()) 113 114 time.sleep(2) 115 116 dns.query.send_tcp(sock, msg, timeout()) 117 dns.query.receive_tcp(sock, timeout()) 118 119 time.sleep(6) 120 121 dns.query.send_tcp(sock, msg, timeout()) 122 dns.query.receive_tcp(sock, timeout()) 123 124 125def test_pipelining_timeout(named_port): 126 # 127 # The pipelining should only timeout after the last message is received 128 # 129 msg = create_msg("example.", "A") 130 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 131 sock.connect(("10.53.0.1", named_port)) 132 133 time.sleep(1) 134 135 # Send and receive 25 DNS queries 136 for _ in range(25): 137 dns.query.send_tcp(sock, msg, timeout()) 138 for _ in range(25): 139 dns.query.receive_tcp(sock, timeout()) 140 141 time.sleep(3) 142 143 # Send and receive 25 DNS queries 144 for _ in range(25): 145 dns.query.send_tcp(sock, msg, timeout()) 146 for _ in range(25): 147 dns.query.receive_tcp(sock, timeout()) 148 149 time.sleep(6) 150 151 with pytest.raises(EOFError): 152 try: 153 dns.query.send_tcp(sock, msg, timeout()) 154 dns.query.receive_tcp(sock, timeout()) 155 except ConnectionError as e: 156 raise EOFError from e 157 158 159def test_long_axfr(named_port): 160 # 161 # The timers should not fire during AXFR, thus the connection should not 162 # close abruptly 163 # 164 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 165 sock.connect(("10.53.0.1", named_port)) 166 167 name = dns.name.from_text("example.") 168 msg = create_msg("example.", "AXFR") 169 dns.query.send_tcp(sock, msg, timeout()) 170 171 # Receive the initial DNS message with SOA 172 (response, _) = dns.query.receive_tcp(sock, timeout(), one_rr_per_rrset=True) 173 soa = response.get_rrset( 174 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 175 ) 176 assert soa is not None 177 178 # Pull DNS message from wire until the second SOA is received 179 while True: 180 (response, _) = dns.query.receive_tcp( 181 sock, timeout(), one_rr_per_rrset=True 182 ) 183 soa = response.get_rrset( 184 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 185 ) 186 if soa is not None: 187 break 188 assert soa is not None 189 190 191@isctest.mark.flaky(max_runs=3) 192def test_send_timeout(named_port): 193 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 194 sock.connect(("10.53.0.1", named_port)) 195 196 # Send and receive single large RDATA over TCP 197 msg = create_msg("large.example.", "TXT") 198 dns.query.send_tcp(sock, msg, timeout()) 199 dns.query.receive_tcp(sock, timeout()) 200 201 # Send and receive 28 large (~32k) DNS queries that should 202 # fill the default maximum 208k TCP send buffer 203 for _ in range(28): 204 dns.query.send_tcp(sock, msg, timeout()) 205 206 # configure idle interval is 5 seconds, sleep 6 to make sure we are 207 # above the interval 208 time.sleep(6) 209 210 with pytest.raises(EOFError): 211 try: 212 dns.query.send_tcp(sock, msg, timeout()) 213 dns.query.receive_tcp(sock, timeout()) 214 except ConnectionError as e: 215 raise EOFError from e 216 217 218@isctest.mark.long_test 219def test_max_transfer_idle_out(named_port): 220 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 221 sock.connect(("10.53.0.1", named_port)) 222 223 name = dns.name.from_text("example.") 224 msg = create_msg("example.", "AXFR") 225 dns.query.send_tcp(sock, msg, timeout()) 226 227 # Receive the initial DNS message with SOA 228 (response, _) = dns.query.receive_tcp(sock, timeout(), one_rr_per_rrset=True) 229 soa = response.get_rrset( 230 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 231 ) 232 assert soa is not None 233 234 time.sleep(61) # max-transfer-idle-out is 1 minute 235 236 with pytest.raises(ConnectionResetError): 237 # Process queued TCP messages 238 while True: 239 (response, _) = dns.query.receive_tcp( 240 sock, timeout(), one_rr_per_rrset=True 241 ) 242 soa = response.get_rrset( 243 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 244 ) 245 if soa is not None: 246 break 247 assert soa is None 248 249 250@isctest.mark.long_test 251def test_max_transfer_time_out(named_port): 252 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 253 sock.connect(("10.53.0.1", named_port)) 254 255 name = dns.name.from_text("example.") 256 msg = create_msg("example.", "AXFR") 257 dns.query.send_tcp(sock, msg, timeout()) 258 259 # Receive the initial DNS message with SOA 260 (response, _) = dns.query.receive_tcp(sock, timeout(), one_rr_per_rrset=True) 261 soa = response.get_rrset( 262 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 263 ) 264 assert soa is not None 265 266 # The loop should timeout at the 5 minutes (max-transfer-time-out) 267 with pytest.raises(EOFError): 268 while True: 269 time.sleep(1) 270 (response, _) = dns.query.receive_tcp( 271 sock, timeout(), one_rr_per_rrset=True 272 ) 273 soa = response.get_rrset( 274 dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA 275 ) 276 if soa is not None: 277 break 278 assert soa is None 279