Compare commits

..
4 changed files with 69 additions and 63 deletions
+59 -17
View File
@@ -8,23 +8,65 @@ import (
var privateIPMatcher = sync.OnceValue(func() IPMatcher { var privateIPMatcher = sync.OnceValue(func() IPMatcher {
return common.Must2(IPReg.BuildIPMatcher(common.Must2(ParseIPRules([]string{ return common.Must2(IPReg.BuildIPMatcher(common.Must2(ParseIPRules([]string{
"0.0.0.0/8", // https://www.iana.org/assignments/iana-ipv4-special-registry
"10.0.0.0/8", "0.0.0.0/8", // "This network"
"100.64.0.0/10", "0.0.0.0/32", // "This host on this network"
"127.0.0.0/8", "10.0.0.0/8", // Private-Use
"169.254.0.0/16", "100.64.0.0/10", // Shared Address Space
"172.16.0.0/12", "127.0.0.0/8", // Loopback
"192.0.0.0/24", "169.254.0.0/16", // Link Local
"192.0.2.0/24", "172.16.0.0/12", // Private-Use
"192.88.99.0/24", // "192.0.0.0/24", // IETF Protocol Assignments
"192.168.0.0/16", "192.0.0.0/29", // IPv4 Service Continuity Prefix
"198.18.0.0/15", "192.0.0.8/32", // IPv4 dummy address
"198.51.100.0/24", "192.0.0.9/32", // Port Control Protocol Anycast
"203.0.113.0/24", "192.0.0.10/32", // Traversal Using Relays around NAT Anycast
"224.0.0.0/3", "192.0.0.170/32", // NAT64/DNS64 Discovery
"::/127", "192.0.0.171/32", // NAT64/DNS64 Discovery
"fc00::/7", "192.0.2.0/24", // Documentation (TEST-NET-1)
"fe80::/10", // "192.31.196.0/24", // AS112-v4
"192.52.193.0/24", // AMT
// "192.88.99.0/24", // Deprecated (6to4 Relay Anycast)
"192.88.99.2/32", // 6a44-relay anycast address
"192.168.0.0/16", // Private-Use
// "192.175.48.0/24", // Direct Delegation AS112 Service
"198.18.0.0/15", // Benchmarking
"198.51.100.0/24", // Documentation (TEST-NET-2)
"203.0.113.0/24", // Documentation (TEST-NET-3)
"240.0.0.0/4", // Reserved
"255.255.255.255/32", // Limited Broadcast
// https://www.iana.org/assignments/multicast-addresses
"224.0.0.0/4",
// https://www.iana.org/assignments/iana-ipv6-special-registry
"::1/128", // Loopback Address
"::/128", // Unspecified Address
// "::ffff:0:0/96", // IPv4-mapped Address
"64:ff9b::/96", // IPv4-IPv6 Translat.
"64:ff9b:1::/48", // IPv4-IPv6 Translat.
"100::/64", // Discard-Only Address Block
"100:0:0:1::/64", // Dummy IPv6 Prefix
// "2001::/23", // IETF Protocol Assignments
"2001::/32", // TEREDO
"2001:1::1/128", // Port Control Protocol Anycast
"2001:1::2/128", // Traversal Using Relays around NAT Anycast
"2001:1::3/128", // DNS-SD Service Registration Protocol Anycast
"2001:2::/48", // Benchmarking
"2001:3::/32", // AMT
// "2001:4:112::/48", // AS112-v6
// "2001:10::/28", // Deprecated (previously ORCHID)
"2001:20::/28", // ORCHIDv2
"2001:30::/28", // Drone Remote ID Protocol Entity Tags (DETs) Prefix
"2001:db8::/32", // Documentation
"2002::/16", // 6to4
// "2620:4f:8000::/48", // Direct Delegation AS112 Service
"3fff::/20", // Documentation
"5f00::/16", // Segment Routing (SRv6) SIDs
"fc00::/7", // Unique-Local
"fe80::/10", // Link-Local Unicast
// https://www.iana.org/assignments/ipv6-multicast-addresses
"ff00::/8", "ff00::/8",
})))) }))))
}) })
+3 -2
View File
@@ -237,10 +237,11 @@ type OutboundDetourConfig struct {
func requiresTransportSecurity(address *Address) bool { func requiresTransportSecurity(address *Address) bool {
if address == nil || address.Address == nil { if address == nil || address.Address == nil {
return false return true
} }
if address.Family().IsIP() { if address.Family().IsIP() {
return !geodata.GetPrivateIPMatcher().Match(address.IP()) ip := address.IP()
return !ip.IsPrivate() && !ip.IsLoopback() && !ip.IsLinkLocalUnicast()
} }
domain := strings.TrimSuffix(strings.ToLower(address.Domain()), ".") domain := strings.TrimSuffix(strings.ToLower(address.Domain()), ".")
return !geodata.GetPrivateDomainMatcher().MatchAny(domain) return !geodata.GetPrivateDomainMatcher().MatchAny(domain)
+7 -4
View File
@@ -558,16 +558,19 @@ func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
} }
func setinterface(network, address string, fd uintptr, iface *net.Interface) error { func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
var err1, err2 error
switch network { switch network {
case "tcp6", "udp6", "ip6": case "tcp6", "udp6", "ip6":
// IPV6_BOUND_IF binds the IPv4 traffic of a dual-stack socket as err1 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index)
// well, and an IPv6 socket rejects IP_BOUND_IF with EINVAL. fallthrough
return unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index)
case "tcp4", "udp4", "ip4": case "tcp4", "udp4", "ip4":
return unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index) err2 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index)
default: default:
panic(network + " " + address) panic(network + " " + address)
} }
return errors.Join(err1, err2)
} }
func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, error) { func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, error) {
-40
View File
@@ -3,10 +3,7 @@
package tun package tun
import ( import (
"context"
"net"
"sync" "sync"
"syscall"
"testing" "testing"
"time" "time"
@@ -234,40 +231,3 @@ func TestWaitKqueueReportsPersistentFailure(t *testing.T) {
} }
} }
} }
// TestSetinterfaceSocketFamilies binds each kind of socket Xray's dialers
// open to lo0, including the dual-stack one Go opens for UDP.
func TestSetinterfaceSocketFamilies(t *testing.T) {
lo, err := net.InterfaceByName("lo0")
if err != nil {
t.Skip(err)
}
lc := net.ListenConfig{Control: func(network, address string, c syscall.RawConn) error {
var err error
if controlErr := c.Control(func(fd uintptr) {
err = setinterface(network, address, fd, lo)
}); controlErr != nil {
return controlErr
}
return err
}}
for _, s := range []struct{ network, address string }{
{"udp", "0.0.0.0:0"}, // dual-stack
{"udp4", "127.0.0.1:0"},
{"udp6", "[::1]:0"},
{"tcp4", "127.0.0.1:0"},
{"tcp6", "[::1]:0"},
} {
var closer interface{ Close() error }
if s.network[:3] == "udp" {
closer, err = lc.ListenPacket(context.Background(), s.network, s.address)
} else {
closer, err = lc.Listen(context.Background(), s.network, s.address)
}
if err != nil {
t.Errorf("%s %s: %v", s.network, s.address, err)
continue
}
closer.Close()
}
}