mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-03 03:47:39 +03:00
Fix default interface check for darwin
This commit is contained in:
parent
53f50347e0
commit
d744d03d93
1 changed files with 101 additions and 20 deletions
|
@ -5,10 +5,9 @@ import (
|
|||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -85,32 +84,114 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var defaultInterface *net.Interface
|
||||
for _, rawRouteMessage := range routeMessages {
|
||||
routeMessage := rawRouteMessage.(*route.RouteMessage)
|
||||
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
|
||||
continue
|
||||
}
|
||||
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
|
||||
if !isIPv4Destination {
|
||||
continue
|
||||
}
|
||||
if destination.IP != netip.IPv4Unspecified().As4() {
|
||||
continue
|
||||
}
|
||||
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
|
||||
if !isIPv4Mask {
|
||||
continue
|
||||
}
|
||||
ones, _ := net.IPMask(mask.IP[:]).Size()
|
||||
if ones != 0 {
|
||||
continue
|
||||
}
|
||||
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "ios" && strings.HasPrefix(routeInterface.Name, "utun") {
|
||||
if routeMessage.Flags&unix.RTF_UP == 0 {
|
||||
continue
|
||||
}
|
||||
if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) {
|
||||
addr, loaded := it.(*route.Inet4Addr)
|
||||
return addr, loaded
|
||||
}), func(addr *route.Inet4Addr) bool {
|
||||
return addr.IP == netip.IPv4Unspecified().As4()
|
||||
}) {
|
||||
oldInterface := m.defaultInterfaceName
|
||||
oldIndex := m.defaultInterfaceIndex
|
||||
|
||||
m.defaultInterfaceIndex = routeMessage.Index
|
||||
m.defaultInterfaceName = routeInterface.Name
|
||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||
return nil
|
||||
}
|
||||
m.emit(EventInterfaceUpdate)
|
||||
return nil
|
||||
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
|
||||
continue
|
||||
}
|
||||
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
|
||||
continue
|
||||
}
|
||||
defaultInterface = routeInterface
|
||||
break
|
||||
}
|
||||
if defaultInterface == nil {
|
||||
defaultInterface, err = getDefaultInterfaceBySocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return ErrNoRoute
|
||||
oldInterface := m.defaultInterfaceName
|
||||
oldIndex := m.defaultInterfaceIndex
|
||||
m.defaultInterfaceIndex = defaultInterface.Index
|
||||
m.defaultInterfaceName = defaultInterface.Name
|
||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||
return nil
|
||||
}
|
||||
m.emit(EventInterfaceUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDefaultInterfaceBySocket() (*net.Interface, error) {
|
||||
socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create file descriptor")
|
||||
}
|
||||
defer unix.Close(socketFd)
|
||||
go unix.Connect(socketFd, &unix.SockaddrInet4{
|
||||
Addr: [4]byte{10, 255, 255, 255},
|
||||
Port: 80,
|
||||
})
|
||||
result := make(chan netip.Addr, 1)
|
||||
go func() {
|
||||
for {
|
||||
sockname, sockErr := unix.Getsockname(socketFd)
|
||||
if sockErr != nil {
|
||||
break
|
||||
}
|
||||
sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
|
||||
if !isInet4Sockaddr {
|
||||
break
|
||||
}
|
||||
addr := netip.AddrFrom4(sockaddr.Addr)
|
||||
if addr.IsUnspecified() {
|
||||
time.Sleep(time.Millisecond)
|
||||
continue
|
||||
}
|
||||
result <- addr
|
||||
break
|
||||
}
|
||||
}()
|
||||
var selectedAddr netip.Addr
|
||||
select {
|
||||
case selectedAddr = <-result:
|
||||
case <-time.After(time.Second):
|
||||
return nil, os.ErrDeadlineExceeded
|
||||
}
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "net.Interfaces")
|
||||
}
|
||||
for _, netInterface := range interfaces {
|
||||
interfaceAddrs, err := netInterface.Addrs()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "net.Interfaces.Addrs")
|
||||
}
|
||||
for _, interfaceAddr := range interfaceAddrs {
|
||||
ipNet, isIPNet := interfaceAddr.(*net.IPNet)
|
||||
if !isIPNet {
|
||||
continue
|
||||
}
|
||||
if ipNet.Contains(selectedAddr.AsSlice()) {
|
||||
return &netInterface, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, E.New("no interface found for address ", selectedAddr)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue