mirror of
https://github.com/fumiama/wintun.git
synced 2026-06-13 06:50:28 +08:00
Initial import from wireguard-go 380ee85
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
17
LICENSE
Normal file
17
LICENSE
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## wintun-go
|
||||||
|
|
||||||
|
This contains bindings to use [Wintun](https://www.wintun.net) from Go.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "golang.zx2c4.com/wintun"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [Documentation](https://pkg.go.dev/golang.zx2c4.com/wintun)
|
||||||
130
dll.go
Normal file
130
dll.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
||||||
|
return &lazyDLL{Name: name, onLoad: onLoad}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 %v DLL: %w", p.dll.Name, err)
|
||||||
|
}
|
||||||
|
addr, err := p.nameToAddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting %v 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
|
||||||
|
}
|
||||||
|
|
||||||
|
type lazyDLL struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
module windows.Handle
|
||||||
|
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 != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
|
||||||
|
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||||
|
)
|
||||||
|
module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load library: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 windows.GetProcAddress(p.dll.module, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns the version of the Wintun DLL.
|
||||||
|
func Version() string {
|
||||||
|
if modwintun.Load() != nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
resInfo, err := windows.FindResource(modwintun.module, windows.ResourceID(1), windows.RT_VERSION)
|
||||||
|
if err != nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
data, err := windows.LoadResourceData(modwintun.module, resInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixedInfo *windows.VS_FIXEDFILEINFO
|
||||||
|
fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
|
||||||
|
err = windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen)
|
||||||
|
if err != nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
version := fmt.Sprintf("%d.%d", (fixedInfo.FileVersionMS>>16)&0xff, (fixedInfo.FileVersionMS>>0)&0xff)
|
||||||
|
if nextNibble := (fixedInfo.FileVersionLS >> 16) & 0xff; nextNibble != 0 {
|
||||||
|
version += fmt.Sprintf(".%d", nextNibble)
|
||||||
|
}
|
||||||
|
if nextNibble := (fixedInfo.FileVersionLS >> 0) & 0xff; nextNibble != 0 {
|
||||||
|
version += fmt.Sprintf(".%d", nextNibble)
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module golang.zx2c4.com/wintun
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20211103235746-7861aae1554b
|
||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
92
session.go
Normal file
92
session.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
handle uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PacketSizeMax = 0xffff // Maximum packet size
|
||||||
|
RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB)
|
||||||
|
RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Packet with data
|
||||||
|
type Packet struct {
|
||||||
|
Next *Packet // Pointer to next packet in queue
|
||||||
|
Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE)
|
||||||
|
Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
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 (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
} else {
|
||||||
|
session = Session{r0}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) End() {
|
||||||
|
syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0)
|
||||||
|
session.handle = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReadWaitEvent() (handle windows.Handle) {
|
||||||
|
r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0)
|
||||||
|
handle = windows.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReceivePacket() (packet []byte, err error) {
|
||||||
|
var packetSize uint32
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReleaseReceivePacket(packet []byte) {
|
||||||
|
syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) SendPacket(packet []byte) {
|
||||||
|
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||||
|
}
|
||||||
152
wintun.go
Normal file
152
wintun.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type loggerLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
logInfo loggerLevel = iota
|
||||||
|
logWarn
|
||||||
|
logErr
|
||||||
|
)
|
||||||
|
|
||||||
|
const AdapterNameMax = 128
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
handle uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimestampedWriter interface {
|
||||||
|
WriteWithTimestamp(p []byte, ts int64) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logMessage(level loggerLevel, timestamp uint64, msg *uint16) int {
|
||||||
|
if tw, ok := log.Default().Writer().(TimestampedWriter); ok {
|
||||||
|
tw.WriteWithTimestamp([]byte(log.Default().Prefix()+windows.UTF16PtrToString(msg)), (int64(timestamp)-116444736000000000)*100)
|
||||||
|
} else {
|
||||||
|
log.Println(windows.UTF16PtrToString(msg))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLogger(dll *lazyDLL) {
|
||||||
|
var callback uintptr
|
||||||
|
if runtime.GOARCH == "386" {
|
||||||
|
callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int {
|
||||||
|
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
|
||||||
|
})
|
||||||
|
} else if runtime.GOARCH == "arm" {
|
||||||
|
callback = windows.NewCallback(func(level loggerLevel, _, timestampLow, timestampHigh uint32, msg *uint16) int {
|
||||||
|
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
|
||||||
|
})
|
||||||
|
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
||||||
|
callback = windows.NewCallback(logMessage)
|
||||||
|
}
|
||||||
|
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, callback, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeAdapter(wintun *Adapter) {
|
||||||
|
syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAdapter creates a Wintun adapter. name is the cosmetic name of the adapter.
|
||||||
|
// tunnelType represents the type of adapter and should be "Wintun". requestedGUID is
|
||||||
|
// the GUID of the created network adapter, which then influences NLA generation
|
||||||
|
// deterministically. If it is set to nil, the GUID is chosen by the system at random,
|
||||||
|
// and hence a new NLA entry is created for each new adapter.
|
||||||
|
func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) {
|
||||||
|
var name16 *uint16
|
||||||
|
name16, err = windows.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var tunnelType16 *uint16
|
||||||
|
tunnelType16, err = windows.UTF16PtrFromString(tunnelType)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunCreateAdapter.Addr(), 3, uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wintun = &Adapter{handle: r0}
|
||||||
|
runtime.SetFinalizer(wintun, closeAdapter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenAdapter opens an existing Wintun adapter by name.
|
||||||
|
func OpenAdapter(name string) (wintun *Adapter, err error) {
|
||||||
|
var name16 *uint16
|
||||||
|
name16, err = windows.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 1, uintptr(unsafe.Pointer(name16)), 0, 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wintun = &Adapter{handle: r0}
|
||||||
|
runtime.SetFinalizer(wintun, closeAdapter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a Wintun adapter.
|
||||||
|
func (wintun *Adapter) Close() (err error) {
|
||||||
|
runtime.SetFinalizer(wintun, nil)
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uninstall removes the driver from the system if no drivers are currently in use.
|
||||||
|
func Uninstall() (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunDeleteDriver.Addr(), 0, 0, 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunningVersion returns the version of the loaded driver.
|
||||||
|
func RunningVersion() (version uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0)
|
||||||
|
version = uint32(r0)
|
||||||
|
if version == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LUID returns the LUID of the adapter.
|
||||||
|
func (wintun *Adapter) LUID() (luid uint64) {
|
||||||
|
syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user