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