Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
5.2 KB go
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2017-2026 WireGuard LLC. All Rights Reserved.
 */

package driver

import (
	"log"
	"runtime"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
	"golang.zx2c4.com/wireguard/windows/dllloader"
	"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)

type loggerLevel int

const (
	logInfo loggerLevel = iota
	logWarn
	logErr
)

const AdapterNameMax = 128

type Adapter struct {
	handle           uintptr
	lastGetGuessSize uint32
}

var (
	modwireguard                         = dllloader.New("wireguard.dll", setupLogger)
	procWireGuardCreateAdapter           = modwireguard.NewProc("WireGuardCreateAdapter")
	procWireGuardOpenAdapter             = modwireguard.NewProc("WireGuardOpenAdapter")
	procWireGuardCloseAdapter            = modwireguard.NewProc("WireGuardCloseAdapter")
	procWireGuardDeleteDriver            = modwireguard.NewProc("WireGuardDeleteDriver")
	procWireGuardGetAdapterLUID          = modwireguard.NewProc("WireGuardGetAdapterLUID")
	procWireGuardGetRunningDriverVersion = modwireguard.NewProc("WireGuardGetRunningDriverVersion")
	procWireGuardSetAdapterLogging       = modwireguard.NewProc("WireGuardSetAdapterLogging")
)

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 *dllloader.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.SyscallN(dll.NewProc("WireGuardSetLogger").Addr(), callback)
}

func closeAdapter(wireguard *Adapter) {
	syscall.SyscallN(procWireGuardCloseAdapter.Addr(), wireguard.handle)
}

// CreateAdapter creates a WireGuard adapter. name is the cosmetic name of the adapter.
// tunnelType represents the type of adapter and should be "WireGuard". 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, tunnelType string, requestedGUID *windows.GUID) (wireguard *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.SyscallN(procWireGuardCreateAdapter.Addr(), uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
	if r0 == 0 {
		err = e1
		return
	}
	wireguard = &Adapter{handle: r0}
	runtime.SetFinalizer(wireguard, closeAdapter)
	return
}

// OpenAdapter opens an existing WireGuard adapter by name.
func OpenAdapter(name string) (wireguard *Adapter, err error) {
	var name16 *uint16
	name16, err = windows.UTF16PtrFromString(name)
	if err != nil {
		return
	}
	r0, _, e1 := syscall.SyscallN(procWireGuardOpenAdapter.Addr(), uintptr(unsafe.Pointer(name16)))
	if r0 == 0 {
		err = e1
		return
	}
	wireguard = &Adapter{handle: r0}
	runtime.SetFinalizer(wireguard, closeAdapter)
	return
}

// Close closes a WireGuard adapter.
func (wireguard *Adapter) Close() (err error) {
	runtime.SetFinalizer(wireguard, nil)
	r1, _, e1 := syscall.SyscallN(procWireGuardCloseAdapter.Addr(), wireguard.handle)
	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.SyscallN(procWireGuardDeleteDriver.Addr())
	if r1 == 0 {
		err = e1
	}
	return
}

type AdapterLogState uint32

const (
	AdapterLogOff          AdapterLogState = 0
	AdapterLogOn           AdapterLogState = 1
	AdapterLogOnWithPrefix AdapterLogState = 2
)

// SetLogging enables or disables logging on the WireGuard adapter.
func (wireguard *Adapter) SetLogging(logState AdapterLogState) (err error) {
	r1, _, e1 := syscall.SyscallN(procWireGuardSetAdapterLogging.Addr(), wireguard.handle, uintptr(logState))
	if r1 == 0 {
		err = e1
	}
	return
}

// RunningVersion returns the version of the loaded driver.
func RunningVersion() (version uint32, err error) {
	r0, _, e1 := syscall.SyscallN(procWireGuardGetRunningDriverVersion.Addr())
	version = uint32(r0)
	if version == 0 {
		err = e1
	}
	return
}

// LUID returns the LUID of the adapter.
func (wireguard *Adapter) LUID() (luid winipcfg.LUID) {
	syscall.SyscallN(procWireGuardGetAdapterLUID.Addr(), wireguard.handle, uintptr(unsafe.Pointer(&luid)))
	return
}