/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2026 WireGuard LLC. All Rights Reserved.
* Phobos
*/
package wintun
import (
"errors"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/dllloader"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
const (
RingCapacityMin = 0x20000
RingCapacityMax = 0x4000000
PacketSizeMax = 0xffff
)
var (
modwintun = dllloader.New("wintun.dll", nil)
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
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")
)
var ErrNoMorePackets = errors.New("wintun: no more packets are available")
type Adapter struct {
handle uintptr
}
type Session struct {
handle uintptr
}
func CreateAdapter(name, tunnelType string, requestedGUID *windows.GUID) (*Adapter, error) {
name16, err := windows.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
tunnelType16, err := windows.UTF16PtrFromString(tunnelType)
if err != nil {
return nil, err
}
handle, _, lastError := syscall.SyscallN(procWintunCreateAdapter.Addr(),
uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
if handle == 0 {
return nil, lastError
}
return &Adapter{handle: handle}, nil
}
func (a *Adapter) Close() error {
if a.handle == 0 {
return nil
}
_, _, lastError := syscall.SyscallN(procWintunCloseAdapter.Addr(), a.handle)
a.handle = 0
if lastError != windows.ERROR_SUCCESS {
return lastError
}
return nil
}
func Uninstall() error {
result, _, lastError := syscall.SyscallN(procWintunDeleteDriver.Addr())
if result == 0 {
return lastError
}
return nil
}
func RunningVersion() (uint32, error) {
version, _, lastError := syscall.SyscallN(procWintunGetRunningDriverVersion.Addr())
if version == 0 {
return 0, lastError
}
return uint32(version), nil
}
func (a *Adapter) LUID() winipcfg.LUID {
var luid uint64
syscall.SyscallN(procWintunGetAdapterLUID.Addr(), a.handle, uintptr(unsafe.Pointer(&luid)))
return winipcfg.LUID(luid)
}
func (a *Adapter) StartSession(capacity uint32) (*Session, error) {
handle, _, lastError := syscall.SyscallN(procWintunStartSession.Addr(), a.handle, uintptr(capacity))
if handle == 0 {
return nil, lastError
}
return &Session{handle: handle}, nil
}
func (s *Session) End() {
if s.handle == 0 {
return
}
syscall.SyscallN(procWintunEndSession.Addr(), s.handle)
s.handle = 0
}
func (s *Session) ReadWaitEvent() windows.Handle {
handle, _, _ := syscall.SyscallN(procWintunGetReadWaitEvent.Addr(), s.handle)
return windows.Handle(handle)
}
func (s *Session) ReceivePacket() ([]byte, error) {
var size uint32
packet, _, lastError := syscall.SyscallN(procWintunReceivePacket.Addr(), s.handle, uintptr(unsafe.Pointer(&size)))
if packet == 0 {
if lastError == windows.ERROR_NO_MORE_ITEMS {
return nil, ErrNoMorePackets
}
return nil, lastError
}
return unsafe.Slice((*byte)(unsafe.Pointer(packet)), size), nil
}
func (s *Session) ReleaseReceivePacket(packet []byte) {
syscall.SyscallN(procWintunReleaseReceivePacket.Addr(), s.handle, uintptr(unsafe.Pointer(unsafe.SliceData(packet))))
runtime.KeepAlive(packet)
}
func (s *Session) AllocateSendPacket(size int) ([]byte, error) {
packet, _, lastError := syscall.SyscallN(procWintunAllocateSendPacket.Addr(), s.handle, uintptr(size))
if packet == 0 {
return nil, lastError
}
return unsafe.Slice((*byte)(unsafe.Pointer(packet)), size), nil
}
func (s *Session) SendPacket(packet []byte) {
syscall.SyscallN(procWintunSendPacket.Addr(), s.handle, uintptr(unsafe.Pointer(unsafe.SliceData(packet))))
runtime.KeepAlive(packet)
}