shadowsocks/shadowsocks-rust · error

unexpected response from 8.8.8.8:53

Error message

unexpected response from 8.8.8.8:53

What it means

check_request_udp probes a proxy with a UDP DNS query to 8.8.8.8:53 and validates that the answer is at least 12 bytes (DNS header size) and that its transaction ID matches the sent 0x1234. If the packet is too short or the ID differs, the reply is not a valid DNS response for our query, so InvalidData is thrown.

Source

Thrown at crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs:962

        .await?;

        let mut control = UdpSocketControlData::default();
        control.client_session_id = rand::random::<u64>();
        control.packet_id = 1;
        client.send_with_ctrl(&addr, &control, DNS_QUERY).await?;

        let mut buffer = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE];
        let (n, ..) = client.recv(&mut buffer).await?;

        let dns_answer = &buffer[..n];

        // DNS packet must have at least 6 * 2 bytes
        if dns_answer.len() < 12 || &dns_answer[0..2] != b"\x12\x34" {
            use std::io::{Error, ErrorKind};

            debug!("unexpected response from 8.8.8.8:53, {:?}", ByteStr::new(dns_answer));

            let err = Error::new(ErrorKind::InvalidData, "unexpected response from 8.8.8.8:53");
            return Err(err);
        }

        Ok(())
    }

    async fn check_request(&self) -> io::Result<()> {
        match self.server_type {
            ServerType::Tcp => self.check_request_tcp_firefox().await,
            ServerType::Udp => self.check_request_udp().await,
        }
    }

    async fn check_delay(&self) -> io::Result<u32> {
        let start = Instant::now();

        // Send HTTP GET and read the first byte
        let res = time::timeout(self.max_server_rtt, self.check_request()).await;

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Confirm the server supports UDP relay (udp_assist / udp_enabled) before using it for UDP traffic.
  2. Test plain UDP DNS to 8.8.8.8 from the server host; open port 53 egress if blocked.
  3. Check for DNS hijacking on the path (compare response with dig @8.8.8.8 directly).
  4. Exclude the node from UDP-eligible servers in the balancer.
Defensive patterns

Strategy: fallback

Validate before calling

// before probing UDP, confirm server advertises UDP relay support
if !server.udp_assist() { /* exclude from UDP balancer */ }

Try / catch

match udp_check(&server).await {
    Ok(()) => udp_balancer.add_server(server, estimate),
    Err(_) => {
        // fall back to TCP relay for DNS or exclude node from UDP pool
        exclude_from_udp_pool(server);
    }
}

Prevention

When it happens

Trigger: check_request (UDP path) receiving a UDP reply that is <12 bytes, empty, or whose first two bytes are not \x12\x34 — e.g. ICMP port-unreachable payloads, hijacked answers, or a different server's response.

Common situations: Proxy nodes that don't support UDP relay (ASSOCIATE), firewalls dropping UDP, ISP DNS hijacking injecting spoofed responses, or servers where port 53 egress is blocked.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/223356fd77b48743. Report an issue: GitHub.