added test exercising the dns error path

This commit is contained in:
HoneyryderChuck 2023-05-21 23:45:38 +02:00
parent c71d4048af
commit 1fb4046d52
4 changed files with 96 additions and 53 deletions

View File

@ -132,6 +132,23 @@ module Requests
end
end
define_method :"test_resolver_#{resolver_type}_dns_error" do
start_test_servlet(DNSErrorServer, 0.2) do |slow_dns_server|
start_test_servlet(DNSErrorServer, 0.2) do |not_so_slow_dns_server|
nameservers = [slow_dns_server.nameserver, not_so_slow_dns_server.nameserver]
resolver_opts = options.merge(nameserver: nameservers, timeouts: [3])
HTTPX.plugin(SessionWithPool).wrap do |session|
uri = build_uri("/get")
response = session.get(uri, resolver_class: resolver_type, resolver_options: resolver_opts)
verify_error_response(response, /unknown DNS error/)
end
end
end
end
# this test mocks a DNS server invalid messages back
define_method :"test_resolver_#{resolver_type}_decoding_error" do
session = HTTPX.plugin(SessionWithPool)

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative "test"
# from https://gist.github.com/peterc/1425383
class DNSErrorServer < TestDNSResolver
private
def dns_response(query)
# Valid response header
response = "#{query[0, 2]}\x81\x02#{query[4, 2]}\x00\x00\x00\x00\x00\x00".b
# Append original question section
response << query[12..-1].b
# Use pointer to refer to domain name in question section
response << "\xc0\x0c".b
response
end
end

View File

@ -1,56 +1,13 @@
# frozen_string_literal: true
require "resolv"
require "socket"
require_relative "test"
# from https://gist.github.com/peterc/1425383
class SlowDNSServer
attr_reader :queries, :answers
def initialize(timeout)
@port = next_available_port
@can_log = ENV.key?("HTTPX_DEBUG")
@timeout = timeout
@queries = 0
@answers = 0
end
def nameserver
["127.0.0.1", @port]
end
def start
Socket.udp_server_loop(@port) do |query, src|
@queries += 1
sleep(@timeout)
src.reply(dns_response(query))
@answers += 1
end
end
class SlowDNSServer < TestDNSResolver
private
def extract_domain(data)
domain = +""
# Check "Opcode" of question header for valid question
if (data[2].ord & 120).zero?
# Read QNAME section of question section
# DNS header section is 12 bytes long, so data starts at offset 12
idx = 12
len = data[idx].ord
# Strings are rendered as a byte containing length, then text.. repeat until length of 0
until len.zero?
domain << "#{data[idx + 1, len]}."
idx += len + 1
len = data[idx].ord
end
end
domain
end
def dns_response(query)
domain = extract_domain(query)
ip = Resolv.getaddress(domain)
@ -87,12 +44,4 @@ class SlowDNSServer
response << rdata.b
response
end
def next_available_port
udp = UDPSocket.new
udp.bind("127.0.0.1", 0)
udp.addr[1]
ensure
udp.close
end
end

View File

@ -111,3 +111,58 @@ class TestHTTP2Server
end
end
end
class TestDNSResolver
attr_reader :queries, :answers
def initialize(timeout)
@port = next_available_port
@can_log = ENV.key?("HTTPX_DEBUG")
@timeout = timeout
@queries = 0
@answers = 0
end
def nameserver
["127.0.0.1", @port]
end
def start
Socket.udp_server_loop(@port) do |query, src|
@queries += 1
sleep(@timeout)
src.reply(dns_response(query))
@answers += 1
end
end
private
def extract_domain(data)
domain = +""
# Check "Opcode" of question header for valid question
if (data[2].ord & 120).zero?
# Read QNAME section of question section
# DNS header section is 12 bytes long, so data starts at offset 12
idx = 12
len = data[idx].ord
# Strings are rendered as a byte containing length, then text.. repeat until length of 0
until len.zero?
domain << "#{data[idx + 1, len]}."
idx += len + 1
len = data[idx].ord
end
end
domain
end
def next_available_port
udp = UDPSocket.new
udp.bind("127.0.0.1", 0)
udp.addr[1]
ensure
udp.close
end
end