{"record":{"id":"5d1ca5c50ab676fc","repo":"shadowsocks/shadowsocks-rust","slug":"source-and-destination-type-unmatch","errorCode":null,"errorMessage":"source and destination type unmatch","messagePattern":"source and destination type unmatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks-service/src/local/tun/udp.rs","lineNumber":107,"sourceCode":"impl UdpTunInboundWriter {\n    fn new(tun_tx: mpsc::Sender<BytesMut>) -> Self {\n        Self { tun_tx }\n    }\n}\n\nimpl UdpInboundWrite for UdpTunInboundWriter {\n    async fn send_to(&self, peer_addr: SocketAddr, remote_addr: &Address, data: &[u8]) -> io::Result<()> {\n        let addr = match *remote_addr {\n            Address::SocketAddress(sa) => {\n                // Try to convert IPv4 mapped IPv6 address if server is running on dual-stack mode\n                match (peer_addr, sa) {\n                    (SocketAddr::V4(..), SocketAddr::V4(..)) | (SocketAddr::V6(..), SocketAddr::V6(..)) => sa,\n                    (SocketAddr::V4(..), SocketAddr::V6(v6)) => {\n                        // If peer is IPv4, then remote_addr can only be IPv4-mapped-IPv6\n                        match to_ipv4_mapped(v6.ip()) {\n                            Some(v4) => SocketAddr::new(IpAddr::from(v4), v6.port()),\n                            None => {\n                                return Err(io::Error::new(\n                                    ErrorKind::InvalidData,\n                                    \"source and destination type unmatch\",\n                                ));\n                            }\n                        }\n                    }\n                    (SocketAddr::V6(..), SocketAddr::V4(v4)) => {\n                        // Convert remote_addr to IPv4-mapped-IPv6\n                        SocketAddr::new(IpAddr::from(v4.ip().to_ipv6_mapped()), v4.port())\n                    }\n                }\n            }\n            Address::DomainNameAddress(..) => {\n                let err = io::Error::new(\n                    ErrorKind::InvalidInput,\n                    \"tun destination must not be an domain name address\",\n                );\n                return Err(err);","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks-service/src/local/tun/udp.rs#L89-L125","documentation":"In the tun UDP relay's send_to, when the peer/source address is IPv4 but the resolved destination is a non-IPv4-mapped IPv6 address, the code cannot build a consistent IP packet, so it returns InvalidData 'source and destination type unmatch'. The tun device requires source and destination families to agree.","triggerScenarios":"send_to where remote_addr (source) is SocketAddr::V4 and the destination Address resolves to an IPv6 address that is not IPv4-mapped (::ffff:x.x.x.x). The to_ipv4_mapped conversion returns None and the error is returned.","commonSituations":"Proxying a remote whose resolved address is a native IPv6 address while the local client/peer is on the IPv4 tun subnet; dual-stack misconfiguration on the tun interface.","solutions":["Ensure the destination resolves to an IPv4 or IPv4-mapped-IPv6 address when the source is IPv4","Force IPv4 resolution (prefer A records) for remotes accessed from the IPv4 side of the tun","Configure the tun address pool to include IPv6 if you need to proxy native IPv6 destinations","Reject or skip such UDP flows earlier in the relay with a clearer log message"],"exampleFix":"// before: destination is 2001:db8::1, source is IPv4 -> error\n// after: resolve/convert destination to IPv4-mapped or use an IPv6-capable source\nlet dst = SocketAddr::new(IpAddr::V6(to_ipv6_mapped(v4.ip())), v4.port()); // align families","handlingStrategy":"type-guard","validationCode":"fn families_compatible(src: &SocketAddr, dst: &Address) -> bool {\n    match dst { Address::DomainNameAddress(..) => false,\n      Address::SocketAddr(d) => matches!((src.is_ipv4(), d.is_ipv4()), (true,true)|(false,false)) || d.ip().to_ipv4_mapped().is_some() } }","typeGuard":"fn as_v4_mapped(sa: SocketAddr) -> Option<SocketAddr> {\n    match sa { SocketAddr::V6(v6) => v6.ip().to_ipv4_mapped().map(|v4| SocketAddr::new(v4.into(), v6.port())), v4 => Some(v4) } }","tryCatchPattern":"match relay.send_to(dest, src, &data).await { Err(e) if e.kind()==InvalidData => log::debug!(\"family mismatch, dropped\"), r => r? }","preventionTips":["Keep tun v4 and v6 source pools consistent with expected destinations","Prefer IPv4-mapped conversion early in the pipeline","Pre-resolve AAAA vs A based on the peer family","Log family mismatches to tune resolution policy"],"tags":["network","udp","tun","ipv6","address-family"],"backgroundTag":"incompatible-source-type","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}