Example code attempting to use WinTUN.
| 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