fix: dhcp not working on windows

This commit is contained in:
wwqgtxx 2023-10-26 10:27:38 +08:00
parent cf93f69f40
commit 4314b37d04
6 changed files with 83 additions and 47 deletions

View file

@ -14,5 +14,15 @@ func ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, er
listenAddr = "255.255.255.255:68" listenAddr = "255.255.255.255:68"
} }
return dialer.ListenPacket(ctx, "udp4", listenAddr, dialer.WithInterface(ifaceName), dialer.WithAddrReuse(true)) options := []dialer.Option{
dialer.WithInterface(ifaceName),
dialer.WithAddrReuse(true),
}
// fallback bind on windows, because syscall bind can not receive broadcast
if runtime.GOOS == "windows" {
options = append(options, dialer.WithFallbackBind(true))
}
return dialer.ListenPacket(ctx, "udp4", listenAddr, options...)
} }

View file

@ -3,6 +3,7 @@ package dialer
import ( import (
"net" "net"
"net/netip" "net/netip"
"strconv"
"strings" "strings"
"github.com/Dreamacro/clash/component/iface" "github.com/Dreamacro/clash/component/iface"
@ -49,3 +50,52 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination
return nil, iface.ErrAddrNotFound return nil, iface.ErrAddrNotFound
} }
func fallbackBindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
if !destination.IsGlobalUnicast() {
return nil
}
local := uint64(0)
if dialer.LocalAddr != nil {
_, port, err := net.SplitHostPort(dialer.LocalAddr.String())
if err == nil {
local, _ = strconv.ParseUint(port, 10, 16)
}
}
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))
if err != nil {
return err
}
dialer.LocalAddr = addr
return nil
}
func fallbackBindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) {
_, port, err := net.SplitHostPort(address)
if err != nil {
port = "0"
}
local, _ := strconv.ParseUint(port, 10, 16)
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))
if err != nil {
return "", err
}
return addr.String(), nil
}
func fallbackParseNetwork(network string, addr netip.Addr) string {
// fix fallbackBindIfaceToListenConfig() force bind to an ipv4 address
if !strings.HasSuffix(network, "4") &&
!strings.HasSuffix(network, "6") &&
addr.Unmap().Is6() {
network += "6"
}
return network
}

View file

@ -5,55 +5,16 @@ package dialer
import ( import (
"net" "net"
"net/netip" "net/netip"
"strconv"
"strings"
) )
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error { func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
if !destination.IsGlobalUnicast() { return fallbackBindIfaceToDialer(ifaceName, dialer, network, destination)
return nil
}
local := uint64(0)
if dialer.LocalAddr != nil {
_, port, err := net.SplitHostPort(dialer.LocalAddr.String())
if err == nil {
local, _ = strconv.ParseUint(port, 10, 16)
}
}
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))
if err != nil {
return err
}
dialer.LocalAddr = addr
return nil
} }
func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) { func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, network, address string) (string, error) {
_, port, err := net.SplitHostPort(address) return fallbackBindIfaceToListenConfig(ifaceName, lc, network, address)
if err != nil {
port = "0"
}
local, _ := strconv.ParseUint(port, 10, 16)
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))
if err != nil {
return "", err
}
return addr.String(), nil
} }
func ParseNetwork(network string, addr netip.Addr) string { func ParseNetwork(network string, addr netip.Addr) string {
// fix bindIfaceToListenConfig() force bind to an ipv4 address return fallbackParseNetwork(network, addr)
if !strings.HasSuffix(network, "4") &&
!strings.HasSuffix(network, "6") &&
addr.Unmap().Is6() {
network += "6"
}
return network
} }

View file

@ -74,7 +74,11 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
lc := &net.ListenConfig{} lc := &net.ListenConfig{}
if cfg.interfaceName != "" { if cfg.interfaceName != "" {
addr, err := bindIfaceToListenConfig(cfg.interfaceName, lc, network, address) bind := bindIfaceToListenConfig
if cfg.fallbackBind {
bind = fallbackBindIfaceToListenConfig
}
addr, err := bind(cfg.interfaceName, lc, network, address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,7 +129,11 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
dialer := netDialer.(*net.Dialer) dialer := netDialer.(*net.Dialer)
if opt.interfaceName != "" { if opt.interfaceName != "" {
if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil { bind := bindIfaceToDialer
if opt.fallbackBind {
bind = fallbackBindIfaceToDialer
}
if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
return nil, err return nil, err
} }
} }

View file

@ -20,6 +20,7 @@ type NetDialer interface {
type option struct { type option struct {
interfaceName string interfaceName string
fallbackBind bool
addrReuse bool addrReuse bool
routingMark int routingMark int
network int network int
@ -38,6 +39,12 @@ func WithInterface(name string) Option {
} }
} }
func WithFallbackBind(fallback bool) Option {
return func(opt *option) {
opt.fallbackBind = fallback
}
}
func WithAddrReuse(reuse bool) Option { func WithAddrReuse(reuse bool) Option {
return func(opt *option) { return func(opt *option) {
opt.addrReuse = reuse opt.addrReuse = reuse

View file

@ -280,7 +280,7 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
DstPort: uint16(uintPort), DstPort: uint16(uintPort),
} }
if proxyAdapter == nil { if proxyAdapter == nil {
return dialer.NewDialer(opts...).ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
} }
if !proxyAdapter.SupportUDP() { if !proxyAdapter.SupportUDP() {