From c871518ca0789ff3397ef4dcfc4fbcbc640e1440 Mon Sep 17 00:00:00 2001 From: lucus Date: Sun, 25 Dec 2016 17:35:25 +0900 Subject: [PATCH] Add windows support. To use it on windows, you need a tap driver, or just install OpenVPN. --- syscalls_other.go | 2 +- syscalls_windows.go | 171 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 syscalls_windows.go diff --git a/syscalls_other.go b/syscalls_other.go index 96d1d63..ab3b9e5 100644 --- a/syscalls_other.go +++ b/syscalls_other.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin +// +build !linux,!darwin,!windows package water diff --git a/syscalls_windows.go b/syscalls_windows.go new file mode 100644 index 0000000..140ab88 --- /dev/null +++ b/syscalls_windows.go @@ -0,0 +1,171 @@ +// +build windows + +// To use it with windows, you need a tap driver installed on windows. +// https://github.com/OpenVPN/tap-windows6 +// or just install OpenVPN +// https://github.com/OpenVPN/openvpn +package water + +import ( + "errors" + "net" + "os" + "syscall" + + "golang.org/x/sys/windows/registry" +) + +var ( + IfceNameNotFound = errors.New("Failed to find the name of interface.") + TapDeviceNotFound = errors.New("Failed to find the tap device in registry.") + // Device Control Codes + tap_win_ioctl_get_mac = tap_control_code(1, 0) + tap_win_ioctl_get_version = tap_control_code(2, 0) + tap_win_ioctl_get_mtu = tap_control_code(3, 0) + tap_win_ioctl_get_info = tap_control_code(4, 0) + tap_ioctl_config_point_to_point = tap_control_code(5, 0) + tap_ioctl_set_media_status = tap_control_code(6, 0) + tap_win_ioctl_config_dhcp_masq = tap_control_code(7, 0) + tap_win_ioctl_get_log_line = tap_control_code(8, 0) + tap_win_ioctl_config_dhcp_set_opt = tap_control_code(9, 0) + tap_ioctl_config_tun = tap_control_code(10, 0) + // w32 api + file_device_unknown = uint32(0x00000022) +) + +func ctl_code(device_type, function, method, access uint32) uint32 { + return (device_type << 16) | (access << 14) | (function << 2) | method +} + +func tap_control_code(request, method uint32) uint32 { + return ctl_code(file_device_unknown, request, method, 0) +} + +// getdeviceid finds out a TAP device from registry, it requires privileged right. +func getdeviceid() (string, error) { + // TAP driver key location + regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}` + k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.ALL_ACCESS) + if err != nil { + return "", err + } + defer k.Close() + // read all subkeys + keys, err := k.ReadSubKeyNames(-1) + if err != nil { + return "", err + } + // find the one with ComponentId == "tap0901" + for _, v := range keys { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey+"\\"+v, registry.ALL_ACCESS) + if err != nil { + continue + } + val, _, err := key.GetStringValue("ComponentId") + if err != nil { + goto next + } + if val == "tap0901" { + val, _, err = key.GetStringValue("NetCfgInstanceId") + if err != nil { + goto next + } + key.Close() + return val, nil + } + next: + key.Close() + } + return "", TapDeviceNotFound +} + +// openDev find and open an interface. +func openDev(isTAP bool) (ifce *Interface, err error) { + // ifName won't work + // find the device in registrym you need privileged right. + deviceid, err := getdeviceid() + if err != nil { + return nil, err + } + path := "\\\\.\\Global\\" + deviceid + ".tap" + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return nil, err + } + // type Handle uintptr + file, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_SYSTEM, 0) + // if err hanppens, close the interface. + defer func() { + if err != nil { + syscall.Close(file) + } + if err := recover(); err != nil { + syscall.Close(file) + } + }() + if err != nil { + return nil, err + } + var bytesReturned uint32 + rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) + + //TUN + if !isTAP { + code2 := []byte{0x0a, 0x03, 0x00, 0x01, 0x0a, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00} + err = syscall.DeviceIoControl(file, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) + if err != nil { + return + } + } + + // find the mac address of tap device, use this to find the name of interface + mac := make([]byte, 6) + err = syscall.DeviceIoControl(file, tap_win_ioctl_get_mac, &mac[0], uint32(len(mac)), &mac[0], uint32(len(mac)), &bytesReturned, nil) + if err != nil { + return nil, err + } + + fd := os.NewFile(uintptr(file), path) + ifce = &Interface{isTAP: isTAP, ReadWriteCloser: fd} + + // bring up device. + code := []byte{0x01, 0x00, 0x00, 0x00} + err = syscall.DeviceIoControl(file, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) + if err != nil { + return + } + // find the name of tap interface(u need it to set the ip or other command) + hwaddr_equal := func(a net.HardwareAddr, b []byte) bool { + for i := 0; i < 6; i++ { + if a[i] != b[i] { + return false + } + } + return true + } + + ifces, err := net.Interfaces() + if err != nil { + return + } + + for _, v := range ifces { + if hwaddr_equal(v.HardwareAddr[:6], mac[:6]) { + ifce.name = v.Name + return + } + } + + err = IfceNameNotFound + return +} + +func newTAP(ifName string) (ifce *Interface, err error) { + // ifName won't work + return openDev(true) +} + +func newTUN(ifName string) (ifce *Interface, err error) { + // ifName won't work + return openDev(false) +}