diff --git a/README.md b/README.md index 9e340a7c..690534e2 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,6 @@ Then manually create the default route and DNS server. If your device already ha Enjoy! :) #### For Windows: -go to [https://www.wintun.net](https://www.wintun.net) and download the latest release, copy the right `wintun.dll` into the system32 directory. ```yaml tun: enable: true diff --git a/listener/tun/device/tun/driver/amd64/wintun.dll b/listener/tun/device/tun/driver/amd64/wintun.dll new file mode 100755 index 00000000..aee04e77 Binary files /dev/null and b/listener/tun/device/tun/driver/amd64/wintun.dll differ diff --git a/listener/tun/device/tun/driver/arm/wintun.dll b/listener/tun/device/tun/driver/arm/wintun.dll new file mode 100755 index 00000000..0017794f Binary files /dev/null and b/listener/tun/device/tun/driver/arm/wintun.dll differ diff --git a/listener/tun/device/tun/driver/arm64/wintun.dll b/listener/tun/device/tun/driver/arm64/wintun.dll new file mode 100755 index 00000000..dc4e4aee Binary files /dev/null and b/listener/tun/device/tun/driver/arm64/wintun.dll differ diff --git a/listener/tun/device/tun/driver/dll_windows.go b/listener/tun/device/tun/driver/dll_windows.go new file mode 100644 index 00000000..dd6e1cff --- /dev/null +++ b/listener/tun/device/tun/driver/dll_windows.go @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "fmt" + "runtime" + "sync" + "sync/atomic" + "syscall" + "unsafe" + + "github.com/Dreamacro/clash/log" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/driver/memmod" +) + +//go:linkname modwintun golang.zx2c4.com/wintun.modwintun + +//go:linkname procWintunCreateAdapter golang.zx2c4.com/wintun.procWintunCreateAdapter + +//go:linkname procWintunOpenAdapter golang.zx2c4.com/wintun.procWintunOpenAdapter + +//go:linkname procWintunCloseAdapter golang.zx2c4.com/wintun.procWintunCloseAdapter + +//go:linkname procWintunDeleteDriver golang.zx2c4.com/wintun.procWintunDeleteDriver + +//go:linkname procWintunGetAdapterLUID golang.zx2c4.com/wintun.procWintunGetAdapterLUID + +//go:linkname procWintunGetRunningDriverVersion golang.zx2c4.com/wintun.procWintunGetRunningDriverVersion + +//go:linkname procWintunAllocateSendPacket golang.zx2c4.com/wintun.procWintunAllocateSendPacket + +//go:linkname procWintunEndSession golang.zx2c4.com/wintun.procWintunEndSession + +//go:linkname procWintunGetReadWaitEvent golang.zx2c4.com/wintun.procWintunGetReadWaitEvent + +//go:linkname procWintunReceivePacket golang.zx2c4.com/wintun.procWintunReceivePacket + +//go:linkname procWintunReleaseReceivePacket golang.zx2c4.com/wintun.procWintunReleaseReceivePacket + +//go:linkname procWintunSendPacket golang.zx2c4.com/wintun.procWintunSendPacket + +//go:linkname procWintunStartSession golang.zx2c4.com/wintun.procWintunStartSession + +var ( + modwintun *lazyDLL + procWintunCreateAdapter *lazyProc + procWintunOpenAdapter *lazyProc + procWintunCloseAdapter *lazyProc + procWintunDeleteDriver *lazyProc + procWintunGetAdapterLUID *lazyProc + procWintunGetRunningDriverVersion *lazyProc + procWintunAllocateSendPacket *lazyProc + procWintunEndSession *lazyProc + procWintunGetReadWaitEvent *lazyProc + procWintunReceivePacket *lazyProc + procWintunReleaseReceivePacket *lazyProc + procWintunSendPacket *lazyProc + procWintunStartSession *lazyProc +) + +type loggerLevel int + +const ( + logInfo loggerLevel = iota + logWarn + logErr +) + +func init() { + modwintun = newLazyDLL("wintun.dll", setupLogger) + procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter") + procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter") + procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter") + procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver") + procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID") + procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion") + procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket") + procWintunEndSession = modwintun.NewProc("WintunEndSession") + procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent") + procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket") + procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket") + procWintunSendPacket = modwintun.NewProc("WintunSendPacket") + procWintunStartSession = modwintun.NewProc("WintunStartSession") +} + +func InitWintun() (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("init wintun.dll error: %v", r) + } + }() + + if err = modwintun.Load(); err != nil { + return + } + + procWintunCreateAdapter.Addr() + procWintunOpenAdapter.Addr() + procWintunCloseAdapter.Addr() + procWintunDeleteDriver.Addr() + procWintunGetAdapterLUID.Addr() + procWintunGetRunningDriverVersion.Addr() + procWintunAllocateSendPacket.Addr() + procWintunEndSession.Addr() + procWintunGetReadWaitEvent.Addr() + procWintunReceivePacket.Addr() + procWintunReleaseReceivePacket.Addr() + procWintunSendPacket.Addr() + procWintunStartSession.Addr() + + return +} + +func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL { + return &lazyDLL{Name: name, onLoad: onLoad} +} + +func logMessage(level loggerLevel, _ uint64, msg *uint16) int { + switch level { + case logInfo: + log.Infoln("[TUN] %s", windows.UTF16PtrToString(msg)) + case logWarn: + log.Warnln("[TUN] %s", windows.UTF16PtrToString(msg)) + case logErr: + log.Errorln("[TUN] %s", windows.UTF16PtrToString(msg)) + default: + log.Debugln("[TUN] %s", windows.UTF16PtrToString(msg)) + } + return 0 +} + +func setupLogger(dll *lazyDLL) { + var callback uintptr + if runtime.GOARCH == "386" { + callback = windows.NewCallback(func(level loggerLevel, _, _ uint32, msg *uint16) int { + return logMessage(level, 0, msg) + }) + } else if runtime.GOARCH == "arm" { + callback = windows.NewCallback(func(level loggerLevel, _, _, _ uint32, msg *uint16) int { + return logMessage(level, 0, msg) + }) + } else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + callback = windows.NewCallback(logMessage) + } + _, _, _ = syscall.SyscallN(dll.NewProc("WintunSetLogger").Addr(), callback) +} + +func (d *lazyDLL) NewProc(name string) *lazyProc { + return &lazyProc{dll: d, Name: name} +} + +type lazyProc struct { + Name string + mu sync.Mutex + dll *lazyDLL + addr uintptr +} + +func (p *lazyProc) Find() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil { + return nil + } + p.mu.Lock() + defer p.mu.Unlock() + if p.addr != 0 { + return nil + } + + err := p.dll.Load() + if err != nil { + return fmt.Errorf("error loading DLL: %s, MODULE: %s, error: %w", p.dll.Name, p.Name, err) + } + addr, err := p.nameToAddr() + if err != nil { + return fmt.Errorf("error getting %s address: %w", p.Name, err) + } + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr)) + return nil +} + +func (p *lazyProc) Addr() uintptr { + err := p.Find() + if err != nil { + panic(err) + } + return p.addr +} + +func (p *lazyProc) Load() error { + return p.dll.Load() +} + +type lazyDLL struct { + Name string + Base windows.Handle + mu sync.Mutex + module *memmod.Module + onLoad func(d *lazyDLL) +} + +func (d *lazyDLL) Load() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { + return nil + } + d.mu.Lock() + defer d.mu.Unlock() + if d.module != nil { + return nil + } + + module, err := memmod.LoadLibrary(dllData) + if err != nil { + return fmt.Errorf("unable to load library: %w", err) + } + d.Base = windows.Handle(module.BaseAddr()) + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) + if d.onLoad != nil { + d.onLoad(d) + } + return nil +} + +func (p *lazyProc) nameToAddr() (uintptr, error) { + return p.dll.module.ProcAddressByName(p.Name) +} diff --git a/listener/tun/device/tun/driver/dll_windows_386.go b/listener/tun/device/tun/driver/dll_windows_386.go new file mode 100644 index 00000000..519d339c --- /dev/null +++ b/listener/tun/device/tun/driver/dll_windows_386.go @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + _ "embed" +) + +//go:embed x86/wintun.dll +var dllData []byte diff --git a/listener/tun/device/tun/driver/dll_windows_amd64.go b/listener/tun/device/tun/driver/dll_windows_amd64.go new file mode 100644 index 00000000..9e10221b --- /dev/null +++ b/listener/tun/device/tun/driver/dll_windows_amd64.go @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + _ "embed" +) + +//go:embed amd64/wintun.dll +var dllData []byte diff --git a/listener/tun/device/tun/driver/dll_windows_arm.go b/listener/tun/device/tun/driver/dll_windows_arm.go new file mode 100644 index 00000000..12f20661 --- /dev/null +++ b/listener/tun/device/tun/driver/dll_windows_arm.go @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + _ "embed" +) + +//go:embed arm/wintun.dll +var dllData []byte diff --git a/listener/tun/device/tun/driver/dll_windows_arm64.go b/listener/tun/device/tun/driver/dll_windows_arm64.go new file mode 100644 index 00000000..1da869e6 --- /dev/null +++ b/listener/tun/device/tun/driver/dll_windows_arm64.go @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + _ "embed" +) + +//go:embed arm64/wintun.dll +var dllData []byte diff --git a/listener/tun/device/tun/driver/package_info.go b/listener/tun/device/tun/driver/package_info.go new file mode 100644 index 00000000..0c1bf7e4 --- /dev/null +++ b/listener/tun/device/tun/driver/package_info.go @@ -0,0 +1,10 @@ +//go:build windows + +// https://git.zx2c4.com/wireguard-go/tree/tun/wintun + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +package driver diff --git a/listener/tun/device/tun/driver/x86/wintun.dll b/listener/tun/device/tun/driver/x86/wintun.dll new file mode 100755 index 00000000..2ab97dba Binary files /dev/null and b/listener/tun/device/tun/driver/x86/wintun.dll differ diff --git a/listener/tun/device/tun/tun_wireguard.go b/listener/tun/device/tun/tun_wireguard.go index 529a1054..1aafae54 100644 --- a/listener/tun/device/tun/tun_wireguard.go +++ b/listener/tun/device/tun/tun_wireguard.go @@ -3,7 +3,6 @@ package tun import ( - "errors" "fmt" "os" "runtime" @@ -43,11 +42,11 @@ func Open(name string, mtu uint32) (_ device.Device, err error) { forcedMTU = int(t.mtu) } - nt, err := tun.CreateTUN(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way + nt, err := newDevice(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way - // retry if abnormal exit on Windows at last time - if err != nil && runtime.GOOS == "windows" && errors.Is(err, os.ErrExist) { - nt, err = tun.CreateTUN(t.name, forcedMTU) + // retry if abnormal exit at last time on Windows + if err != nil && runtime.GOOS == "windows" && os.IsExist(err) { + nt, err = newDevice(t.name, forcedMTU) } if err != nil { diff --git a/listener/tun/device/tun/tun_wireguard_unix.go b/listener/tun/device/tun/tun_wireguard_unix.go index d88787fb..b6d3addf 100644 --- a/listener/tun/device/tun/tun_wireguard_unix.go +++ b/listener/tun/device/tun/tun_wireguard_unix.go @@ -2,7 +2,13 @@ package tun +import "golang.zx2c4.com/wireguard/tun" + const ( offset = 4 /* 4 bytes TUN_PI */ defaultMTU = 1500 ) + +func newDevice(name string, mtu int) (tun.Device, error) { + return tun.CreateTUN(name, mtu) +} diff --git a/listener/tun/device/tun/tun_wireguard_windows.go b/listener/tun/device/tun/tun_wireguard_windows.go index f7cf8498..1d74b35e 100644 --- a/listener/tun/device/tun/tun_wireguard_windows.go +++ b/listener/tun/device/tun/tun_wireguard_windows.go @@ -1,6 +1,8 @@ package tun import ( + "github.com/Dreamacro/clash/listener/tun/device/tun/driver" + "golang.org/x/sys/windows" "golang.zx2c4.com/wireguard/tun" ) @@ -20,3 +22,11 @@ func init() { func (t *TUN) LUID() uint64 { return t.nt.LUID() } + +func newDevice(name string, mtu int) (nt tun.Device, err error) { + if err = driver.InitWintun(); err != nil { + return + } + + return tun.CreateTUN(name, mtu) +}