2020-02-09 17:02:48 +08:00
|
|
|
package dialer
|
|
|
|
|
2020-02-15 21:42:46 +08:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"net"
|
|
|
|
"time"
|
2020-02-09 17:02:48 +08:00
|
|
|
|
2020-02-15 21:42:46 +08:00
|
|
|
"github.com/Dreamacro/clash/common/singledo"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DialerHookFunc = func(dialer *net.Dialer)
|
|
|
|
type DialHookFunc = func(dialer *net.Dialer, network string, ip net.IP)
|
2020-02-09 17:02:48 +08:00
|
|
|
type ListenConfigHookFunc = func(*net.ListenConfig)
|
2020-02-15 21:42:46 +08:00
|
|
|
type ListenPacketHookFunc = func() net.IP
|
|
|
|
|
|
|
|
var (
|
|
|
|
DialerHook DialerHookFunc
|
|
|
|
DialHook DialHookFunc
|
|
|
|
ListenConfigHook ListenConfigHookFunc
|
|
|
|
ListenPacketHook ListenPacketHookFunc
|
|
|
|
)
|
2020-02-09 17:02:48 +08:00
|
|
|
|
|
|
|
var (
|
2020-02-15 21:42:46 +08:00
|
|
|
ErrAddrNotFound = errors.New("addr not found")
|
|
|
|
ErrNetworkNotSupport = errors.New("network not support")
|
2020-02-09 17:02:48 +08:00
|
|
|
)
|
2020-02-15 21:42:46 +08:00
|
|
|
|
|
|
|
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 ListenPacketWithInterface(name string) ListenPacketHookFunc {
|
|
|
|
single := singledo.NewSingle(5 * time.Second)
|
|
|
|
|
|
|
|
return func() net.IP {
|
|
|
|
elm, err, _ := single.Do(func() (interface{}, error) {
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return addrs, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs := elm.([]net.Addr)
|
|
|
|
|
|
|
|
for _, elm := range addrs {
|
|
|
|
addr, ok := elm.(*net.IPNet)
|
|
|
|
if !ok || addr.IP.To4() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr.IP
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func DialerWithInterface(name string) DialHookFunc {
|
|
|
|
single := singledo.NewSingle(5 * time.Second)
|
|
|
|
|
|
|
|
return func(dialer *net.Dialer, network string, ip net.IP) {
|
|
|
|
elm, err, _ := single.Do(func() (interface{}, error) {
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return addrs, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs := elm.([]net.Addr)
|
|
|
|
|
|
|
|
switch network {
|
|
|
|
case "tcp", "tcp4", "tcp6":
|
|
|
|
if addr, err := lookupTCPAddr(ip, addrs); err == nil {
|
|
|
|
dialer.LocalAddr = addr
|
|
|
|
}
|
|
|
|
case "udp", "udp4", "udp6":
|
|
|
|
if addr, err := lookupUDPAddr(ip, addrs); err == nil {
|
|
|
|
dialer.LocalAddr = addr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|