github.com/MerlinKodo/sing-tun@v0.1.15/internal/winipcfg/luid.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package winipcfg 7 8 import ( 9 "errors" 10 "net/netip" 11 "strings" 12 13 "golang.org/x/sys/windows" 14 ) 15 16 // LUID represents a network interface. 17 type LUID uint64 18 19 // IPInterface method retrieves IP information for the specified interface on the local computer. 20 func (luid LUID) IPInterface(family AddressFamily) (*MibIPInterfaceRow, error) { 21 row := &MibIPInterfaceRow{} 22 row.Init() 23 row.InterfaceLUID = luid 24 row.Family = family 25 err := row.get() 26 if err != nil { 27 return nil, err 28 } 29 return row, nil 30 } 31 32 // Interface method retrieves information for the specified adapter on the local computer. 33 // https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2 34 func (luid LUID) Interface() (*MibIfRow2, error) { 35 row := &MibIfRow2{} 36 row.InterfaceLUID = luid 37 err := row.get() 38 if err != nil { 39 return nil, err 40 } 41 return row, nil 42 } 43 44 // GUID method converts a locally unique identifier (LUID) for a network interface to a globally unique identifier (GUID) for the interface. 45 // https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceluidtoguid 46 func (luid LUID) GUID() (*windows.GUID, error) { 47 guid := &windows.GUID{} 48 err := convertInterfaceLUIDToGUID(&luid, guid) 49 if err != nil { 50 return nil, err 51 } 52 return guid, nil 53 } 54 55 // LUIDFromGUID function converts a globally unique identifier (GUID) for a network interface to the locally unique identifier (LUID) for the interface. 56 // https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceguidtoluid 57 func LUIDFromGUID(guid *windows.GUID) (LUID, error) { 58 var luid LUID 59 err := convertInterfaceGUIDToLUID(guid, &luid) 60 if err != nil { 61 return 0, err 62 } 63 return luid, nil 64 } 65 66 // LUIDFromIndex function converts a local index for a network interface to the locally unique identifier (LUID) for the interface. 67 // https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceindextoluid 68 func LUIDFromIndex(index uint32) (LUID, error) { 69 var luid LUID 70 err := convertInterfaceIndexToLUID(index, &luid) 71 if err != nil { 72 return 0, err 73 } 74 return luid, nil 75 } 76 77 // IPAddress method returns MibUnicastIPAddressRow struct that matches to provided 'ip' argument. Corresponds to GetUnicastIpAddressEntry 78 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry) 79 func (luid LUID) IPAddress(addr netip.Addr) (*MibUnicastIPAddressRow, error) { 80 row := &MibUnicastIPAddressRow{InterfaceLUID: luid} 81 82 err := row.Address.SetAddr(addr) 83 if err != nil { 84 return nil, err 85 } 86 87 err = row.get() 88 if err != nil { 89 return nil, err 90 } 91 92 return row, nil 93 } 94 95 // AddIPAddress method adds new unicast IP address to the interface. Corresponds to CreateUnicastIpAddressEntry function 96 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry). 97 func (luid LUID) AddIPAddress(address netip.Prefix) error { 98 row := &MibUnicastIPAddressRow{} 99 row.Init() 100 row.InterfaceLUID = luid 101 row.DadState = DadStatePreferred 102 row.ValidLifetime = 0xffffffff 103 row.PreferredLifetime = 0xffffffff 104 err := row.Address.SetAddr(address.Addr()) 105 if err != nil { 106 return err 107 } 108 row.OnLinkPrefixLength = uint8(address.Bits()) 109 return row.Create() 110 } 111 112 // AddIPAddresses method adds multiple new unicast IP addresses to the interface. Corresponds to CreateUnicastIpAddressEntry function 113 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry). 114 func (luid LUID) AddIPAddresses(addresses []netip.Prefix) error { 115 for i := range addresses { 116 err := luid.AddIPAddress(addresses[i]) 117 if err != nil { 118 return err 119 } 120 } 121 return nil 122 } 123 124 // SetIPAddresses method sets new unicast IP addresses to the interface. 125 func (luid LUID) SetIPAddresses(addresses []netip.Prefix) error { 126 err := luid.FlushIPAddresses(windows.AF_UNSPEC) 127 if err != nil { 128 return err 129 } 130 return luid.AddIPAddresses(addresses) 131 } 132 133 // SetIPAddressesForFamily method sets new unicast IP addresses for a specific family to the interface. 134 func (luid LUID) SetIPAddressesForFamily(family AddressFamily, addresses []netip.Prefix) error { 135 err := luid.FlushIPAddresses(family) 136 if err != nil { 137 return err 138 } 139 for i := range addresses { 140 if !addresses[i].Addr().Is4() && family == windows.AF_INET { 141 continue 142 } else if !addresses[i].Addr().Is6() && family == windows.AF_INET6 { 143 continue 144 } 145 err := luid.AddIPAddress(addresses[i]) 146 if err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 153 // DeleteIPAddress method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function 154 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry). 155 func (luid LUID) DeleteIPAddress(address netip.Prefix) error { 156 row := &MibUnicastIPAddressRow{} 157 row.Init() 158 row.InterfaceLUID = luid 159 err := row.Address.SetAddr(address.Addr()) 160 if err != nil { 161 return err 162 } 163 // Note: OnLinkPrefixLength member is ignored by DeleteUnicastIpAddressEntry(). 164 row.OnLinkPrefixLength = uint8(address.Bits()) 165 return row.Delete() 166 } 167 168 // FlushIPAddresses method deletes all interface's unicast IP addresses. 169 func (luid LUID) FlushIPAddresses(family AddressFamily) error { 170 var tab *mibUnicastIPAddressTable 171 err := getUnicastIPAddressTable(family, &tab) 172 if err != nil { 173 return err 174 } 175 t := tab.get() 176 for i := range t { 177 if t[i].InterfaceLUID == luid { 178 t[i].Delete() 179 } 180 } 181 tab.free() 182 return nil 183 } 184 185 // Route method returns route determined with the input arguments. Corresponds to GetIpForwardEntry2 function 186 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2). 187 // NOTE: If the corresponding route isn't found, the method will return error. 188 func (luid LUID) Route(destination netip.Prefix, nextHop netip.Addr) (*MibIPforwardRow2, error) { 189 row := &MibIPforwardRow2{} 190 row.Init() 191 row.InterfaceLUID = luid 192 row.ValidLifetime = 0xffffffff 193 row.PreferredLifetime = 0xffffffff 194 err := row.DestinationPrefix.SetPrefix(destination) 195 if err != nil { 196 return nil, err 197 } 198 err = row.NextHop.SetAddr(nextHop) 199 if err != nil { 200 return nil, err 201 } 202 203 err = row.get() 204 if err != nil { 205 return nil, err 206 } 207 return row, nil 208 } 209 210 // AddRoute method adds a route to the interface. Corresponds to CreateIpForwardEntry2 function, with added splitDefault feature. 211 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2) 212 func (luid LUID) AddRoute(destination netip.Prefix, nextHop netip.Addr, metric uint32) error { 213 row := &MibIPforwardRow2{} 214 row.Init() 215 row.InterfaceLUID = luid 216 err := row.DestinationPrefix.SetPrefix(destination) 217 if err != nil { 218 return err 219 } 220 err = row.NextHop.SetAddr(nextHop) 221 if err != nil { 222 return err 223 } 224 row.Metric = metric 225 return row.Create() 226 } 227 228 // AddRoutes method adds multiple routes to the interface. 229 func (luid LUID) AddRoutes(routesData []*RouteData) error { 230 for _, rd := range routesData { 231 err := luid.AddRoute(rd.Destination, rd.NextHop, rd.Metric) 232 if err != nil { 233 return err 234 } 235 } 236 return nil 237 } 238 239 // SetRoutes method sets (flush than add) multiple routes to the interface. 240 func (luid LUID) SetRoutes(routesData []*RouteData) error { 241 err := luid.FlushRoutes(windows.AF_UNSPEC) 242 if err != nil { 243 return err 244 } 245 return luid.AddRoutes(routesData) 246 } 247 248 // SetRoutesForFamily method sets (flush than add) multiple routes for a specific family to the interface. 249 func (luid LUID) SetRoutesForFamily(family AddressFamily, routesData []*RouteData) error { 250 err := luid.FlushRoutes(family) 251 if err != nil { 252 return err 253 } 254 for _, rd := range routesData { 255 if !rd.Destination.Addr().Is4() && family == windows.AF_INET { 256 continue 257 } else if !rd.Destination.Addr().Is6() && family == windows.AF_INET6 { 258 continue 259 } 260 err := luid.AddRoute(rd.Destination, rd.NextHop, rd.Metric) 261 if err != nil { 262 return err 263 } 264 } 265 return nil 266 } 267 268 // DeleteRoute method deletes a route that matches the criteria. Corresponds to DeleteIpForwardEntry2 function 269 // (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2). 270 func (luid LUID) DeleteRoute(destination netip.Prefix, nextHop netip.Addr) error { 271 row := &MibIPforwardRow2{} 272 row.Init() 273 row.InterfaceLUID = luid 274 err := row.DestinationPrefix.SetPrefix(destination) 275 if err != nil { 276 return err 277 } 278 err = row.NextHop.SetAddr(nextHop) 279 if err != nil { 280 return err 281 } 282 err = row.get() 283 if err != nil { 284 return err 285 } 286 return row.Delete() 287 } 288 289 // FlushRoutes method deletes all interface's routes. 290 // It continues on failures, and returns the last error afterwards. 291 func (luid LUID) FlushRoutes(family AddressFamily) error { 292 var tab *mibIPforwardTable2 293 err := getIPForwardTable2(family, &tab) 294 if err != nil { 295 return err 296 } 297 t := tab.get() 298 for i := range t { 299 if t[i].InterfaceLUID == luid { 300 err2 := t[i].Delete() 301 if err2 != nil { 302 err = err2 303 } 304 } 305 } 306 tab.free() 307 return err 308 } 309 310 // DNS method returns all DNS server addresses associated with the adapter. 311 func (luid LUID) DNS() ([]netip.Addr, error) { 312 addresses, err := GetAdaptersAddresses(windows.AF_UNSPEC, GAAFlagDefault) 313 if err != nil { 314 return nil, err 315 } 316 r := make([]netip.Addr, 0, len(addresses)) 317 for _, addr := range addresses { 318 if addr.LUID == luid { 319 for dns := addr.FirstDNSServerAddress; dns != nil; dns = dns.Next { 320 if ip := dns.Address.IP(); ip != nil { 321 if a, ok := netip.AddrFromSlice(ip); ok { 322 r = append(r, a) 323 } 324 } else { 325 return nil, windows.ERROR_INVALID_PARAMETER 326 } 327 } 328 } 329 } 330 return r, nil 331 } 332 333 // SetDNS method clears previous and associates new DNS servers and search domains with the adapter for a specific family. 334 func (luid LUID) SetDNS(family AddressFamily, servers []netip.Addr, domains []string) error { 335 if family != windows.AF_INET && family != windows.AF_INET6 { 336 return windows.ERROR_PROTOCOL_UNREACHABLE 337 } 338 339 var filteredServers []string 340 for _, server := range servers { 341 if (server.Is4() && family == windows.AF_INET) || (server.Is6() && family == windows.AF_INET6) { 342 filteredServers = append(filteredServers, server.String()) 343 } 344 } 345 servers16, err := windows.UTF16PtrFromString(strings.Join(filteredServers, ",")) 346 if err != nil { 347 return err 348 } 349 domains16, err := windows.UTF16PtrFromString(strings.Join(domains, ",")) 350 if err != nil { 351 return err 352 } 353 guid, err := luid.GUID() 354 if err != nil { 355 return err 356 } 357 dnsInterfaceSettings := &DnsInterfaceSettings{ 358 Version: DnsInterfaceSettingsVersion1, 359 Flags: DnsInterfaceSettingsFlagNameserver | DnsInterfaceSettingsFlagSearchList, 360 NameServer: servers16, 361 SearchList: domains16, 362 } 363 if family == windows.AF_INET6 { 364 dnsInterfaceSettings.Flags |= DnsInterfaceSettingsFlagIPv6 365 } 366 // For >= Windows 10 1809 367 err = SetInterfaceDnsSettings(*guid, dnsInterfaceSettings) 368 if err == nil || !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) { 369 return err 370 } 371 372 // For < Windows 10 1809 373 err = luid.fallbackSetDNSForFamily(family, servers) 374 if err != nil { 375 return err 376 } 377 if len(domains) > 0 { 378 return luid.fallbackSetDNSDomain(domains[0]) 379 } else { 380 return luid.fallbackSetDNSDomain("") 381 } 382 } 383 384 // FlushDNS method clears all DNS servers associated with the adapter. 385 func (luid LUID) FlushDNS(family AddressFamily) error { 386 return luid.SetDNS(family, nil, nil) 387 } 388 389 func (luid LUID) DisableDNSRegistration() error { 390 guid, err := luid.GUID() 391 if err != nil { 392 return err 393 } 394 395 dnsInterfaceSettings := &DnsInterfaceSettings{ 396 Version: DnsInterfaceSettingsVersion1, 397 Flags: DnsInterfaceSettingsFlagRegistrationEnabled, 398 RegistrationEnabled: 0, 399 } 400 401 // For >= Windows 10 1809 402 err = SetInterfaceDnsSettings(*guid, dnsInterfaceSettings) 403 if err == nil || !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) { 404 return err 405 } 406 407 // For < Windows 10 1809 408 return luid.fallbackDisableDNSRegistration() 409 }