mirror of https://github.com/Dreamacro/clash.git
119 lines
2.2 KiB
Go
119 lines
2.2 KiB
Go
package dialer
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/Dreamacro/clash/common/singledo"
|
|
)
|
|
|
|
// In some OS, such as Windows, it takes a little longer to get interface information
|
|
var ifaceSingle = singledo.NewSingle(time.Second * 20)
|
|
|
|
var (
|
|
errPlatformNotSupport = errors.New("unsupport platform")
|
|
)
|
|
|
|
func lookupTCPAddr(ip net.IP, addrs []net.Addr) (*net.TCPAddr, error) {
|
|
ipv4 := ip.To4() != nil
|
|
|
|
for _, elm := range addrs {
|
|
addr, ok := elm.(*net.IPNet)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
addrV4 := addr.IP.To4() != nil
|
|
|
|
if addrV4 && ipv4 {
|
|
return &net.TCPAddr{IP: addr.IP, Port: 0}, nil
|
|
} else if !addrV4 && !ipv4 {
|
|
return &net.TCPAddr{IP: addr.IP, Port: 0}, nil
|
|
}
|
|
}
|
|
|
|
return nil, ErrAddrNotFound
|
|
}
|
|
|
|
func lookupUDPAddr(ip net.IP, addrs []net.Addr) (*net.UDPAddr, error) {
|
|
ipv4 := ip.To4() != nil
|
|
|
|
for _, elm := range addrs {
|
|
addr, ok := elm.(*net.IPNet)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
addrV4 := addr.IP.To4() != nil
|
|
|
|
if addrV4 && ipv4 {
|
|
return &net.UDPAddr{IP: addr.IP, Port: 0}, nil
|
|
} else if !addrV4 && !ipv4 {
|
|
return &net.UDPAddr{IP: addr.IP, Port: 0}, nil
|
|
}
|
|
}
|
|
|
|
return nil, ErrAddrNotFound
|
|
}
|
|
|
|
func fallbackBindToDialer(dialer *net.Dialer, network string, ip net.IP, name string) error {
|
|
if !ip.IsGlobalUnicast() {
|
|
return nil
|
|
}
|
|
|
|
iface, err, _ := ifaceSingle.Do(func() (interface{}, error) {
|
|
return net.InterfaceByName(name)
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
addrs, err := iface.(*net.Interface).Addrs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch network {
|
|
case "tcp", "tcp4", "tcp6":
|
|
if addr, err := lookupTCPAddr(ip, addrs); err == nil {
|
|
dialer.LocalAddr = addr
|
|
} else {
|
|
return err
|
|
}
|
|
case "udp", "udp4", "udp6":
|
|
if addr, err := lookupUDPAddr(ip, addrs); err == nil {
|
|
dialer.LocalAddr = addr
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func fallbackBindToListenConfig(name string) (string, error) {
|
|
iface, err, _ := ifaceSingle.Do(func() (interface{}, error) {
|
|
return net.InterfaceByName(name)
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
addrs, err := iface.(*net.Interface).Addrs()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, elm := range addrs {
|
|
addr, ok := elm.(*net.IPNet)
|
|
if !ok || addr.IP.To4() == nil {
|
|
continue
|
|
}
|
|
|
|
return net.JoinHostPort(addr.IP.String(), "0"), nil
|
|
}
|
|
|
|
return "", ErrAddrNotFound
|
|
}
|