shadowsocks/shadowsocks-rust · error

Invalid IPv6 address

Error message

Invalid IPv6 address

What it means

Identical logic to the Unix variant: when creating an outbound UDP socket with IPv4 address family but a bind_local_addr that is an IPv6 address, only IPv4-mapped IPv6 addresses can be converted down to IPv4. A non-mapped IPv6 address (like "::" or a global IPv6 addr) cannot serve as an IPv4 bind address, so create_outbound_udp_socket returns ErrorKind::InvalidInput with "Invalid IPv6 address".

Source

Thrown at crates/shadowsocks/src/net/sys/windows/mod.rs:473

    };
    if !allow_fragmentation && let Err(err) = set_disable_ip_fragmentation(addr_family, &socket) {
        warn!("failed to disable IP fragmentation, error: {}", err);
    }
    disable_connection_reset(&socket)?;

    Ok(socket)
}

/// Create a `UdpSocket` for connecting to `addr`
#[inline(always)]
pub async fn create_outbound_udp_socket(af: AddrFamily, opts: &ConnectOpts) -> io::Result<UdpSocket> {
    let bind_addr = match (af, opts.bind_local_addr) {
        (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(),
        (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => {
            // Map IPv6 bind_local_addr to IPv4 if AF is IPv4
            match addr.ip().to_ipv4_mapped() {
                Some(addr) => SocketAddr::new(addr.into(), 0),
                None => return Err(io::Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")),
            }
        }
        (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(),
        (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => {
            // Map IPv4 bind_local_addr to IPv6 if AF is IPv6
            SocketAddr::new(addr.ip().to_ipv6_mapped().into(), 0)
        }
        (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0),
        (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
    };

    bind_outbound_udp_socket(&bind_addr, opts).await
}

/// Create a `UdpSocket` binded to `bind_addr`
pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, opts: &ConnectOpts) -> io::Result<UdpSocket> {
    let af = AddrFamily::from(bind_addr);

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Set bind_local_addr to an IPv4 address (e.g. "0.0.0.0") for IPv4 outbound sockets
  2. Use an IPv4-mapped IPv6 literal ("::ffff:0.0.0.0") if you want to express IPv4-any in v6 form
  3. Remove bind_local_addr entirely to let the OS pick the source address
  4. Make the bind address family consistent with the server address family in your config

Example fix

# before
bind_address = "::"
# after
bind_address = "0.0.0.0"  # matches IPv4 outbound
Defensive patterns

Strategy: validation

Validate before calling

fn bind_compatible(af: shadowsocks::net::AddrFamily, bind: Option<std::net::SocketAddr>) -> bool {
    match (af, bind) {
        (shadowsocks::net::AddrFamily::Ipv4, Some(std::net::SocketAddr::V6(v6))) => v6.ip().to_ipv4_mapped().is_some(),
        _ => true,
    }
}
assert!(bind_compatible(AddrFamily::Ipv4, opts.bind_local_addr), "bind_local_addr must be IPv4 or IPv4-mapped for Ipv4 outbound");

Type guard

fn is_ipv4_usable(addr: &std::net::SocketAddr) -> bool {
    match addr {
        std::net::SocketAddr::V4(_) => true,
        std::net::SocketAddr::V6(v6) => v6.ip().to_ipv4_mapped().is_some(),
    }
}

Try / catch

match create_outbound_udp_socket(context, AddrFamily::Ipv4, &opts).await {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("Invalid IPv6") => {
        let mut fixed = opts.clone();
        fixed.bind_local_addr = Some("0.0.0.0:0".parse().unwrap());
        create_outbound_udp_socket(context, AddrFamily::Ipv4, &fixed).await
    }
    r => r,
}

Prevention

When it happens

Trigger: create_outbound_udp_socket on Windows with AddrFamily::Ipv4 and opts.bind_local_addr set to a non-IPv4-mapped IPv6 SocketAddr, e.g. bind_local_addr = "::" while the remote server is reached over IPv4.

Common situations: Config with bind_address = "::" intending dual-stack, but remote resolves to IPv4; reusing an IPv6 bind config for an IPv4 server; environment where Windows chooses IPv4 for the outbound route.

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/f93ebdf8008d92e1. Report an issue: GitHub.