#[cfg(test)]
#[allow(clippy::similar_names)]
mod test_13_transport_fallback {
use super::*;
use crate::wire::LocalRecord;
use std::net::TcpListener;
#[test]
fn reuses_idle_connected_udp_socket() {
let datagram_socket = UdpSocket::bind("127.0.0.1:0").expect("mock UDP bind");
let server_address = datagram_socket.local_addr().expect("mock DNS address");
datagram_socket
.set_read_timeout(Some(Duration::from_secs(2)))
.expect("mock UDP timeout");
let datagram_thread = thread::spawn(move || {
let mut peer_ports = Vec::new();
for address in [Ipv4Addr::new(192, 0, 2, 20), Ipv4Addr::new(192, 0, 2, 21)] {
let mut buffer = [0; 2048];
let (length, peer) = datagram_socket
.recv_from(&mut buffer)
.expect("mock UDP query");
peer_ports.push(peer.port());
let response = local_response(
&buffer[..length],
&[LocalRecord::A(address)],
30,
)
.expect("mock A response");
let response =
edns::add_test_response_opt(&response, 0, false).expect("response OPT");
datagram_socket
.send_to(&response, peer)
.expect("mock UDP response");
}
peer_ports
});
let resolver = Resolver::new(Config {
upstreams: vec![server_address],
fallback_upstreams: Vec::new(),
cache: false,
attempts: 1,
query_timeout: Duration::from_millis(500),
read_etc_hosts: false,
read_static_records: false,
dnssec: ValidationMode::No,
..Config::default()
});
for (id, name) in [(0x70f0, "pool-one.example"), (0x70f1, "pool-two.example")] {
let query = make_query(name, TYPE_A, id).expect("client query");
resolver
.query(&query, QueryMode::Full)
.expect("resolver response");
}
let peer_ports = datagram_thread.join().expect("mock UDP thread");
assert_eq!(peer_ports.len(), 2);
assert_eq!(peer_ports[0], peer_ports[1]);
}
#[test]
fn ignores_unrelated_udp_reply_and_uses_path_mtu_payload_size() {
let datagram_socket = UdpSocket::bind("127.0.0.1:0").expect("mock UDP bind");
let server_address = datagram_socket.local_addr().expect("mock DNS address");
datagram_socket
.set_read_timeout(Some(Duration::from_secs(2)))
.expect("mock UDP timeout");
let datagram_thread = thread::spawn(move || {
let mut buffer = [0; 2048];
let (length, peer) = datagram_socket
.recv_from(&mut buffer)
.expect("mock UDP query");
let query = &buffer[..length];
let opt = edns::inspect_opt(query)
.expect("query OPT")
.expect("EDNS query");
assert_eq!(opt.udp_payload_size, 65_508);
let mut unrelated = local_response(
query,
&[LocalRecord::A(Ipv4Addr::new(192, 0, 2, 10))],
30,
)
.expect("unrelated response");
unrelated[0..2].copy_from_slice(&0x9999_u16.to_be_bytes());
datagram_socket
.send_to(&unrelated, peer)
.expect("unrelated UDP response");
let response = local_response(
query,
&[LocalRecord::A(Ipv4Addr::new(192, 0, 2, 11))],
30,
)
.expect("matching response");
datagram_socket
.send_to(&response, peer)
.expect("matching UDP response");
});
let resolver = Resolver::new(Config {
upstreams: vec![server_address],
fallback_upstreams: Vec::new(),
cache: false,
attempts: 1,
query_timeout: Duration::from_millis(500),
read_etc_hosts: false,
read_static_records: false,
dnssec: ValidationMode::No,
..Config::default()
});
let query = make_query("udp-filter.example", TYPE_A, 0x7100).expect("client query");
let response = resolver
.query(&query, QueryMode::Full)
.expect("resolver response");
let records = extract_address_records(&response, Some(2)).expect("address records");
assert_eq!(
records.addresses,
vec![IpAddr::V4(Ipv4Addr::new(192, 0, 2, 11))]
);
datagram_thread.join().expect("mock UDP thread");
}
#[test]
fn truncated_udp_uses_tcp_without_permanently_switching_transport() {
let stream_listener = TcpListener::bind("127.0.0.1:0").expect("mock TCP bind");
let server_address = stream_listener.local_addr().expect("mock DNS address");
let datagram_socket = UdpSocket::bind(server_address).expect("mock UDP bind");
datagram_socket
.set_read_timeout(Some(Duration::from_secs(2)))
.expect("mock UDP timeout");
let datagram_thread = thread::spawn(move || {
let mut buffer = [0; 2048];
let (length, peer) = datagram_socket
.recv_from(&mut buffer)
.expect("mock UDP query");
let query = &buffer[..length];
datagram_socket
.send_to(&truncated_response(query), peer)
.expect("mock truncated response");
});
let stream_thread = thread::spawn(move || {
let (mut stream, _) = stream_listener.accept().expect("mock TCP accept");
stream
.set_read_timeout(Some(Duration::from_secs(2)))
.expect("mock TCP timeout");
let query = read_tcp_query(&mut stream);
let response = local_response(
&query,
&[LocalRecord::A(Ipv4Addr::new(192, 0, 2, 90))],
30,
)
.expect("mock A response");
let response =
edns::add_test_response_opt(&response, 0, false).expect("response OPT");
write_tcp_response(&mut stream, &response);
});
let resolver = Resolver::new(Config {
upstreams: vec![server_address],
fallback_upstreams: Vec::new(),
cache: false,
attempts: 1,
query_timeout: Duration::from_millis(250),
read_etc_hosts: false,
read_static_records: false,
dnssec: ValidationMode::No,
..Config::default()
});
let query = make_query("truncated.example", TYPE_A, 0x7101).expect("client query");
let response = resolver
.query(&query, QueryMode::Full)
.expect("resolver response");
let records = extract_address_records(&response, Some(2)).expect("address records");
assert_eq!(
records.addresses,
vec![IpAddr::V4(Ipv4Addr::new(192, 0, 2, 90))]
);
let states = resolver.states();
let state = states
.get(&ServerKey::new(ScopeKind::Global, server_address))
.expect("server state");
assert_eq!(state.transport.mode(), TransportMode::Udp);
assert!(state.transport.packet_truncated());
drop(states);
datagram_thread.join().expect("mock UDP thread");
stream_thread.join().expect("mock TCP thread");
}
#[test]
fn repeated_udp_loss_reaches_plain_tcp() {
let stream_listener = TcpListener::bind("127.0.0.1:0").expect("mock TCP bind");
let server_address = stream_listener.local_addr().expect("mock DNS address");
let datagram_socket = UdpSocket::bind(server_address).expect("mock UDP bind");
datagram_socket
.set_read_timeout(Some(Duration::from_secs(3)))
.expect("mock UDP timeout");
let datagram_thread = thread::spawn(move || {
for index in 0..9 {
let mut buffer = [0; 2048];
let (length, _) = datagram_socket
.recv_from(&mut buffer)
.expect("mock UDP query");
let opt = edns::inspect_opt(&buffer[..length]).expect("query OPT");
match index {
0..=2 => assert!(opt.expect("DNSSEC OPT").dnssec_ok()),
3..=5 => assert!(!opt.expect("EDNS0 OPT").dnssec_ok()),
6..=8 => assert!(opt.is_none()),
_ => unreachable!(),
}
}
});
let stream_thread = thread::spawn(move || {
let (mut stream, _) = stream_listener.accept().expect("mock TCP accept");
stream
.set_read_timeout(Some(Duration::from_secs(3)))
.expect("mock TCP timeout");
let query = read_tcp_query(&mut stream);
assert!(edns::inspect_opt(&query).expect("TCP query OPT").is_none());
let response = local_response(
&query,
&[LocalRecord::A(Ipv4Addr::new(192, 0, 2, 91))],
30,
)
.expect("mock A response");
write_tcp_response(&mut stream, &response);
});
let resolver = Resolver::new(Config {
upstreams: vec![server_address],
fallback_upstreams: Vec::new(),
cache: false,
attempts: 7,
query_timeout: Duration::from_millis(100),
read_etc_hosts: false,
read_static_records: false,
dnssec: ValidationMode::AllowDowngrade,
..Config::default()
});
let query = make_query("transport.example", TYPE_A, 0x7102).expect("client query");
let response = resolver
.query(&query, QueryMode::Full)
.expect("resolver response");
let records = extract_address_records(&response, Some(2)).expect("address records");
assert_eq!(
records.addresses,
vec![IpAddr::V4(Ipv4Addr::new(192, 0, 2, 91))]
);
let states = resolver.states();
assert_eq!(
states
.get(&ServerKey::new(ScopeKind::Global, server_address))
.expect("server state")
.transport
.mode(),
TransportMode::Tcp
);
drop(states);
datagram_thread.join().expect("mock UDP thread");
stream_thread.join().expect("mock TCP thread");
}
fn truncated_response(query: &[u8]) -> Vec<u8> {
let end = wire::question_end(query).expect("question end");
let mut response = query[..end].to_vec();
let query_flags = u16::from_be_bytes([query[2], query[3]]);
let flags = (query_flags & 0x0100) | 0x8000 | 0x0200 | 0x0080;
response[2..4].copy_from_slice(&flags.to_be_bytes());
response[6..12].fill(0);
response
}
fn read_tcp_query(stream: &mut TcpStream) -> Vec<u8> {
let mut length = [0; 2];
stream.read_exact(&mut length).expect("TCP query length");
let mut query = vec![0; usize::from(u16::from_be_bytes(length))];
stream.read_exact(&mut query).expect("TCP query body");
query
}
fn write_tcp_response(stream: &mut TcpStream, response: &[u8]) {
let length = u16::try_from(response.len()).expect("TCP response length");
stream
.write_all(&length.to_be_bytes())
.expect("TCP response length write");
stream
.write_all(response)
.expect("TCP response body write");
}
}