shadowsocks/shadowsocks-rust · error

dns

Error message

dns

What it means

This panic fires from `.expect("dns")` on `config.set_dns_formatted(dns)` when the value of the `--dns` CLI flag cannot be parsed as a DNS server specification. The library expects a formatted string like `"1.1.1.1","8.8.8.8:53,udp"` (address with optional port and protocol) and panics rather than returning an error for malformed input.

Source

Thrown at src/service/server.rs:467

        #[cfg(all(unix, not(target_os = "android")))]
        match matches.get_one::<u64>("NOFILE") {
            Some(nofile) => config.nofile = Some(*nofile),
            None => {
                if config.nofile.is_none() {
                    crate::sys::adjust_nofile();
                }
            }
        }

        if let Some(acl_file) = matches.get_one::<String>("ACL") {
            let acl = AccessControl::load_from_file(acl_file)
                .map_err(|err| ShadowsocksError::LoadAclFailure(format!("loading ACL \"{acl_file}\", {err}")))?;
            config.acl = Some(acl);
        }

        if let Some(dns) = matches.get_one::<String>("DNS") {
            config.set_dns_formatted(dns).expect("dns");
        }

        if let Some(dns_cache_size) = matches.get_one::<usize>("DNS_CACHE_SIZE") {
            config.dns_cache_size = Some(*dns_cache_size);
        }

        if matches.get_flag("IPV6_FIRST") {
            config.ipv6_first = true;
        }

        if let Some(udp_timeout) = matches.get_one::<u64>("UDP_TIMEOUT") {
            config.udp_timeout = Some(Duration::from_secs(*udp_timeout));
        }

        if let Some(udp_max_assoc) = matches.get_one::<usize>("UDP_MAX_ASSOCIATIONS") {
            config.udp_max_associations = Some(*udp_max_assoc);
        }

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Use a valid formatted DNS string, e.g. `--dns '"1.1.1.1"'` or `--dns '"8.8.8.8:53,udp"'` with proper shell quoting.
  2. Prefer IP addresses over hostnames in the --dns value.
  3. Verify shell quoting so commas and embedded double-quotes survive to the process.
  4. Remove --dns to use the system DNS resolution defaults.
  5. When modifying code, replace `.expect("dns")` with proper error propagation into ShadowsocksError.

Example fix

// before (shell eats the quotes -> malformed)
ssserver --dns "1.1.1.1"
// after
ssserver --dns '"1.1.1.1"'
# or with protocol
ssserver --dns '"8.8.8.8:53,udp"'
Defensive patterns

Strategy: validation

Validate before calling

// Validate --dns before launching: quoted address, optional :port, optional ,udp|,tcp suffix
let dns = std::env::args().nth(/* position of --dns value */ 0).unwrap_or_default();
assert!(dns.starts_with('"') && dns.ends_with('"'), "--dns must be a quoted spec like \"8.8.8.8:53,udp\"");

Type guard

fn looks_like_dns_spec(s: &str) -> bool {
    let inner = s.trim().trim_matches('"');
    inner.parse::<std::net::IpAddr>().is_ok()
        || inner.split_once(':').map(|(ip, _)| ip.parse::<std::net::IpAddr>().is_ok()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Running the server with `--dns` set to a malformed value, e.g. a bare hostname (`--dns dns.google`), an empty string, a value with wrong quoting/comma syntax, or an unsupported protocol suffix (`--dns "1.1.1.1,xxx"`).

Common situations: Copy-pasted DNS configs losing their quotes in shell escaping, using a hostname instead of an IP, missing the `,udp`/`,tcp` suffix rules, or shell stripping quotes so the parser sees a single malformed token.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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