|
@@ -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)
|
|
|
+}
|