github.com/apernet/sing-tun@v0.2.6-0.20240323130332-b9f6511036ad/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  }