github.com/labulakalia/water@v0.0.5-0.20231118024244-f351ca6784b6/syscalls_windows.go (about) 1 package water 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "net" 8 "sync" 9 "syscall" 10 "unsafe" 11 12 "golang.org/x/sys/windows/registry" 13 ) 14 15 // To use it with windows, you need a tap driver installed on windows. 16 // https://github.com/OpenVPN/tap-windows6 17 // or just install OpenVPN 18 // https://github.com/OpenVPN/openvpn 19 20 const ( 21 // tapDriverKey is the location of the TAP driver key. 22 tapDriverKey = `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}` 23 // netConfigKey is the location of the TAP adapter's network config. 24 netConfigKey = `SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}` 25 ) 26 27 var ( 28 errIfceNameNotFound = errors.New("Failed to find the name of interface") 29 // Device Control Codes 30 tap_win_ioctl_get_mac = tap_control_code(1, 0) 31 tap_win_ioctl_get_version = tap_control_code(2, 0) 32 tap_win_ioctl_get_mtu = tap_control_code(3, 0) 33 tap_win_ioctl_get_info = tap_control_code(4, 0) 34 tap_ioctl_config_point_to_point = tap_control_code(5, 0) 35 tap_ioctl_set_media_status = tap_control_code(6, 0) 36 tap_win_ioctl_config_dhcp_masq = tap_control_code(7, 0) 37 tap_win_ioctl_get_log_line = tap_control_code(8, 0) 38 tap_win_ioctl_config_dhcp_set_opt = tap_control_code(9, 0) 39 tap_ioctl_config_tun = tap_control_code(10, 0) 40 // w32 api 41 file_device_unknown = uint32(0x00000022) 42 nCreateEvent, 43 nResetEvent, 44 nGetOverlappedResult uintptr 45 ) 46 47 func init() { 48 k32, err := syscall.LoadLibrary("kernel32.dll") 49 if err != nil { 50 panic("LoadLibrary " + err.Error()) 51 } 52 defer syscall.FreeLibrary(k32) 53 54 nCreateEvent = getProcAddr(k32, "CreateEventW") 55 nResetEvent = getProcAddr(k32, "ResetEvent") 56 nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult") 57 } 58 59 func getProcAddr(lib syscall.Handle, name string) uintptr { 60 addr, err := syscall.GetProcAddress(lib, name) 61 if err != nil { 62 panic(name + " " + err.Error()) 63 } 64 return addr 65 } 66 67 func resetEvent(h syscall.Handle) error { 68 r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0) 69 if r == 0 { 70 return err 71 } 72 return nil 73 } 74 75 func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) { 76 var n int 77 r, _, err := syscall.Syscall6(nGetOverlappedResult, 4, 78 uintptr(h), 79 uintptr(unsafe.Pointer(overlapped)), 80 uintptr(unsafe.Pointer(&n)), 1, 0, 0) 81 if r == 0 { 82 return n, err 83 } 84 85 return n, nil 86 } 87 88 func newOverlapped() (*syscall.Overlapped, error) { 89 var overlapped syscall.Overlapped 90 r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0) 91 if r == 0 { 92 return nil, err 93 } 94 overlapped.HEvent = syscall.Handle(r) 95 return &overlapped, nil 96 } 97 98 type wfile struct { 99 fd syscall.Handle 100 rl sync.Mutex 101 wl sync.Mutex 102 ro *syscall.Overlapped 103 wo *syscall.Overlapped 104 } 105 106 func (f *wfile) Close() error { 107 return syscall.Close(f.fd) 108 } 109 110 func (f *wfile) Write(b []byte) (int, error) { 111 f.wl.Lock() 112 defer f.wl.Unlock() 113 114 if err := resetEvent(f.wo.HEvent); err != nil { 115 return 0, err 116 } 117 var n uint32 118 err := syscall.WriteFile(f.fd, b, &n, f.wo) 119 if err != nil && err != syscall.ERROR_IO_PENDING { 120 return int(n), err 121 } 122 return getOverlappedResult(f.fd, f.wo) 123 } 124 125 func (f *wfile) Read(b []byte) (int, error) { 126 f.rl.Lock() 127 defer f.rl.Unlock() 128 129 if err := resetEvent(f.ro.HEvent); err != nil { 130 return 0, err 131 } 132 var done uint32 133 err := syscall.ReadFile(f.fd, b, &done, f.ro) 134 if err != nil && err != syscall.ERROR_IO_PENDING { 135 return int(done), err 136 } 137 return getOverlappedResult(f.fd, f.ro) 138 } 139 140 func ctl_code(device_type, function, method, access uint32) uint32 { 141 return (device_type << 16) | (access << 14) | (function << 2) | method 142 } 143 144 func tap_control_code(request, method uint32) uint32 { 145 return ctl_code(file_device_unknown, request, method, 0) 146 } 147 148 // getdeviceid finds out a TAP device from registry, it *may* requires privileged right to prevent some weird issue. 149 func getdeviceid(componentID string, interfaceName string) (deviceid string, err error) { 150 k, err := registry.OpenKey(registry.LOCAL_MACHINE, tapDriverKey, registry.READ) 151 if err != nil { 152 return "", fmt.Errorf("Failed to open the adapter registry, TAP driver may be not installed, %v", err) 153 } 154 defer k.Close() 155 // read all subkeys, it should not return an err here 156 keys, err := k.ReadSubKeyNames(-1) 157 if err != nil { 158 return "", err 159 } 160 // find the one matched ComponentId 161 for _, v := range keys { 162 key, err := registry.OpenKey(registry.LOCAL_MACHINE, tapDriverKey+"\\"+v, registry.READ) 163 if err != nil { 164 continue 165 } 166 val, _, err := key.GetStringValue("ComponentId") 167 if err != nil { 168 key.Close() 169 continue 170 } 171 if val == componentID { 172 val, _, err = key.GetStringValue("NetCfgInstanceId") 173 if err != nil { 174 key.Close() 175 continue 176 } 177 if len(interfaceName) > 0 { 178 key2 := fmt.Sprintf("%s\\%s\\Connection", netConfigKey, val) 179 k2, err := registry.OpenKey(registry.LOCAL_MACHINE, key2, registry.READ) 180 if err != nil { 181 continue 182 } 183 defer k2.Close() 184 val, _, err := k2.GetStringValue("Name") 185 if err != nil || val != interfaceName { 186 continue 187 } 188 } 189 key.Close() 190 return val, nil 191 } 192 key.Close() 193 } 194 if len(interfaceName) > 0 { 195 return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId '%s' and InterfaceName '%s', TAP driver may be not installed or you may have specified an interface name that doesn't exist", componentID, interfaceName) 196 } 197 198 return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId '%s', TAP driver may be not installed", componentID) 199 } 200 201 // setStatus is used to bring up or bring down the interface 202 func setStatus(fd syscall.Handle, status bool) error { 203 var bytesReturned uint32 204 rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) 205 code := []byte{0x00, 0x00, 0x00, 0x00} 206 if status { 207 code[0] = 0x01 208 } 209 return syscall.DeviceIoControl(fd, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) 210 } 211 212 // setTUN is used to configure the IP address in the underlying driver when using TUN 213 func setTUN(fd syscall.Handle, network string) error { 214 var bytesReturned uint32 215 rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) 216 217 localIP, remoteNet, err := net.ParseCIDR(network) 218 if err != nil { 219 return fmt.Errorf("Failed to parse network CIDR in config, %v", err) 220 } 221 if localIP.To4() == nil { 222 return fmt.Errorf("Provided network(%s) is not a valid IPv4 address", network) 223 } 224 code2 := make([]byte, 0, 12) 225 code2 = append(code2, localIP.To4()[:4]...) 226 code2 = append(code2, remoteNet.IP.To4()[:4]...) 227 code2 = append(code2, remoteNet.Mask[:4]...) 228 if len(code2) != 12 { 229 return fmt.Errorf("Provided network(%s) is not valid", network) 230 } 231 if err := syscall.DeviceIoControl(fd, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil { 232 return err 233 } 234 return nil 235 } 236 237 // openDev find and open an interface. 238 func openDev(config Config) (ifce *Interface, err error) { 239 if config.DeviceType == TUN { 240 return openTunDev(config) 241 } 242 // find the device in registry. 243 deviceid, err := getdeviceid(config.PlatformSpecificParams.ComponentID, config.PlatformSpecificParams.Name) 244 if err != nil { 245 return nil, err 246 } 247 path := "\\\\.\\Global\\" + deviceid + ".tap" 248 pathp, err := syscall.UTF16PtrFromString(path) 249 if err != nil { 250 return nil, err 251 } 252 // type Handle uintptr 253 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|syscall.FILE_FLAG_OVERLAPPED, 0) 254 // if err hanppens, close the interface. 255 defer func() { 256 if err != nil { 257 syscall.Close(file) 258 } 259 if err := recover(); err != nil { 260 syscall.Close(file) 261 } 262 }() 263 if err != nil { 264 return nil, err 265 } 266 var bytesReturned uint32 267 268 // find the mac address of tap device, use this to find the name of interface 269 mac := make([]byte, 6) 270 err = syscall.DeviceIoControl(file, tap_win_ioctl_get_mac, &mac[0], uint32(len(mac)), &mac[0], uint32(len(mac)), &bytesReturned, nil) 271 if err != nil { 272 return nil, err 273 } 274 275 // fd := os.NewFile(uintptr(file), path) 276 ro, err := newOverlapped() 277 if err != nil { 278 return 279 } 280 wo, err := newOverlapped() 281 if err != nil { 282 return 283 } 284 fd := &wfile{fd: file, ro: ro, wo: wo} 285 ifce = &Interface{isTAP: (config.DeviceType == TAP), ReadWriteCloser: fd} 286 287 // bring up device. 288 if err := setStatus(file, true); err != nil { 289 return nil, err 290 } 291 292 //TUN 293 if config.DeviceType == TUN { 294 if err := setTUN(file, config.PlatformSpecificParams.Network); err != nil { 295 return nil, err 296 } 297 } 298 299 // find the name of tap interface(u need it to set the ip or other command) 300 ifces, err := net.Interfaces() 301 if err != nil { 302 return 303 } 304 305 for _, v := range ifces { 306 if len(v.HardwareAddr) < 6 { 307 continue 308 } 309 if bytes.Equal(v.HardwareAddr[:6], mac[:6]) { 310 ifce.name = v.Name 311 return 312 } 313 } 314 315 return nil, errIfceNameNotFound 316 }