-
-
Save jwriteclub/4f0fa081cb4eb39620702d7f7e212afd to your computer and use it in GitHub Desktop.
Example code attempting to use WinTUN.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"golang.org/x/sys/windows" | |
"golang.zx2c4.com/winipcfg" | |
"golang.zx2c4.com/wireguard/tun" | |
"golang.zx2c4.com/wireguard/windows/services" | |
"net" | |
"os" | |
"sync" | |
"syscall" | |
"time" | |
"unsafe" | |
) | |
func main() { | |
checkForAdminGroup() | |
checkForWow64() | |
t, err := tun.CreateTUN("My WinTUN Test") | |
if err != nil { | |
fmt.Printf("Unable to create tunnel interface: %s", err.Error()) | |
os.Exit(1) | |
} | |
nt := t.(*tun.NativeTun) | |
time.Sleep(time.Second * 2) // The interface sometimes isn't found if this is called too quickly after creating the Tun | |
// TODO: Find a better way of searching for the interface than just sleeping. Retry loop? | |
// Get the interface | |
iface, err := winipcfg.InterfaceFromLUID(nt.LUID()) | |
if err != nil { | |
fmt.Printf("Unable to get interface: %s\n", err.Error()) | |
os.Exit(1) | |
} | |
// Add an IP Address (172.16.16.1/24) and a default route to 172.16.16.0/24 through 172.16.16.1 | |
network := &net.IPNet{IP: net.IPv4(0xAC, 0x10, 0x10, 0x01), Mask: net.IPv4Mask(0xFF, 0xFF, 0xFF, 0x00)} | |
fmt.Printf("Setting address %s\n", network.String()) | |
err = iface.SetAddresses([]*net.IPNet{network}) | |
if err != nil { | |
fmt.Printf("Unable to set an address on %s: %s\n", iface.AdapterName, err.Error()) | |
os.Exit(1) | |
} | |
fmt.Printf("Adding route\n") | |
err = iface.AddRoute(&winipcfg.RouteData{Destination: net.IPNet{IP: net.IPv4(0xAC, 0x10, 0x10, 0x00), Mask: net.IPv4Mask(0xFF, 0xFF, 0xFF, 0x00)}, NextHop: net.IPv4(0xAC, 0x10, 0x10, 0x01), Metric: 1}) | |
if err != nil { | |
fmt.Printf("Unable to add route on %s: %s\n", iface.AdapterName, err.Error()) | |
os.Exit(1) | |
} | |
// Display the routes, just for giggles | |
rts, err := iface.GetRoutes(windows.AF_INET) | |
if err != nil { | |
fmt.Printf("Unable to load existing routes: %s\n", err.Error()) | |
os.Exit(1) | |
} else { | |
for i := range rts { | |
fmt.Printf(" > Route: %#v\n", rts[i]) | |
} | |
} | |
wg := &sync.WaitGroup{} | |
wg.Add(1) | |
go func() { | |
fmt.Printf("Starting read loop\n") | |
defer fmt.Printf("Closing read loop\n") | |
defer wg.Done() | |
for { | |
bt := make([]byte, 15000) | |
read, err := nt.Read(bt, 0) | |
if err != nil { | |
fmt.Printf("read error: %s\n", err.Error()) | |
os.Exit(1) | |
} | |
fmt.Printf("Read %d bytes\n", read) | |
} | |
}() | |
wg.Wait() | |
} | |
func checkForAdminGroup() { | |
// This is not a security check, but rather a user-confusion one. | |
processToken, err := windows.OpenCurrentProcessToken() | |
if err != nil { | |
panic(fmt.Sprintf("Unable to open current process token: ", err)) | |
} | |
defer processToken.Close() | |
if !services.TokenIsMemberOfBuiltInAdministrator(processToken) { | |
panic(fmt.Sprintf("Process may only be used by users who are a member of the Builtin Administrators group.")) | |
} else { | |
fmt.Printf("Process appears to be a member of the Builtin Administrators group\n") | |
} | |
} | |
var ( | |
modkernel32 = windows.NewLazySystemDLL("kernel32.dll") | |
procIsWow64Process = modkernel32.NewProc("IsWow64Process") | |
) | |
func checkForWow64() { | |
var b bool | |
p, _ := windows.GetCurrentProcess() | |
err := isWow64Process(p, &b) | |
if err != nil { | |
panic(fmt.Sprintf("Unable to determine whether the process is running under WOW64: ", err)) | |
} | |
if b { | |
panic(fmt.Sprintf("You must use the 64-bit version of process on this computer.")) | |
} else { | |
fmt.Printf("You appear to be running a 64-bit version of Windows") | |
} | |
} | |
func isWow64Process(handle windows.Handle, isWow64 *bool) (err error) { | |
r1, _, e1 := syscall.Syscall(procIsWow64Process.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(isWow64)), 0) | |
if r1 == 0 { | |
if e1 != 0 { | |
err = errnoErr(e1) | |
} else { | |
err = syscall.EINVAL | |
} | |
} | |
return | |
} | |
const ( | |
errnoERROR_IO_PENDING = 997 | |
) | |
var ( | |
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) | |
) | |
func errnoErr(e syscall.Errno) error { | |
switch e { | |
case 0: | |
return nil | |
case errnoERROR_IO_PENDING: | |
return errERROR_IO_PENDING | |
} | |
// TODO: add more here, after collecting data on the common | |
// error values see on Windows. (perhaps when running | |
// all.bat?) | |
return e | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment