gateway icon indicating copy to clipboard operation
gateway copied to clipboard

Win32 API version

Open AudriusButkevicius opened this issue 10 years ago • 7 comments
trafficstars

Just sharing my implementation incase you feel to incorporate it someday (IPv4 only though). Feel free to copy it and modify it in any shape or form, you can ignore the license completely.

https://github.com/calmh/syncthing/commit/6a6ec722cfcc01cd0ce44896aaa07bd77c7b4466

I have no clue how to check which one is the default one though :dancer: Stackoverflow suggests: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365924(v=vs.85).aspx

AudriusButkevicius avatar Apr 26 '15 12:04 AudriusButkevicius

Thanks! Very thoughtful of you to offer.

jackpal avatar Apr 29 '15 22:04 jackpal

package main

import (
	"fmt"
	"log"
	"net"
	"unsafe"

	"golang.org/x/sys/windows"
)

type (
	DWORD               uint32
	ULONG               uint32
	NET_IFINDEX         ULONG
	IF_INDEX            NET_IFINDEX
	NL_ROUTE_PROTOCOL   int32
	MIB_IPFORWARD_PROTO NL_ROUTE_PROTOCOL
	MIB_IPFORWARD_TYPE  int32
)

type MIB_IPFORWARDROW struct {
	DwForwardDest      DWORD
	DwForwardMask      DWORD
	DwForwardPolicy    DWORD
	DwForwardNextHop   DWORD
	DwForwardIfIndex   IF_INDEX
	ForwardType        MIB_IPFORWARD_TYPE
	ForwardProto       MIB_IPFORWARD_PROTO
	DwForwardAge       DWORD
	DwForwardNextHopAS DWORD
	DwForwardMetric1   DWORD
	DwForwardMetric2   DWORD
	DwForwardMetric3   DWORD
	DwForwardMetric4   DWORD
	DwForwardMetric5   DWORD
}

func ipDword(ip net.IP) (d DWORD) {
	ip = ip.To4()
	d |= DWORD(ip[0]) << 0
	d |= DWORD(ip[1]) << 8
	d |= DWORD(ip[2]) << 16
	d |= DWORD(ip[3]) << 24
	return
}

func dwordIP(d DWORD) (ip net.IP) {
	ip = make(net.IP, net.IPv4len)
	ip[0] = byte(d & 0xff)
	ip[1] = byte((d >> 8) & 0xff)
	ip[2] = byte((d >> 16) & 0xff)
	ip[3] = byte((d >> 24) & 0xff)
	return
}

func init() {
	log.SetFlags(log.Lshortfile)
}

func main() {

	dll, err := windows.LoadDLL("Iphlpapi.dll")
	if err != nil {
		log.Fatal(err)
	}
	defer dll.Release()

	getBestRoute, err := dll.FindProc("GetBestRoute")
	if err != nil {
		log.Fatal(err)
	}

	var row MIB_IPFORWARDROW

	// 8.8.8.8 is Google's public DNS, also there is 8.8.4.4

	_, _, err = getBestRoute.Call(
		uintptr(ipDword(net.ParseIP("8.8.8.8"))),
		uintptr(ipDword(net.ParseIP("0.0.0.0"))),
		uintptr(unsafe.Pointer(&row)),
	)
	if err != nil && err != windows.Errno(0) {
		log.Fatal(err)
	}

	for _, val := range []struct {
		Name  string
		Value DWORD
	}{
		{"DwForwardDest", row.DwForwardDest},
		{"DwForwardMask", row.DwForwardMask},
		{"DwForwardPolicy", row.DwForwardPolicy},
		{"DwForwardNextHop", row.DwForwardNextHop},
		{"DwForwardAge", row.DwForwardAge},
		{"DwForwardNextHopAS", row.DwForwardNextHopAS},
		{"DwForwardMetric1", row.DwForwardMetric1},
		{"DwForwardMetric2", row.DwForwardMetric2},
		{"DwForwardMetric3", row.DwForwardMetric3},
		{"DwForwardMetric4", row.DwForwardMetric4},
		{"DwForwardMetric5", row.DwForwardMetric5},
	} {
		fmt.Printf("%20s %s\n", val.Name+":", dwordIP(val.Value).String())
	}

}
	DwForwardDest: 0.0.0.0
	      DwForwardMask: 0.0.0.0
	    DwForwardPolicy: 0.0.0.0
	   DwForwardNextHop: 192.168.1.1
	       DwForwardAge: 79.42.1.0
	 DwForwardNextHopAS: 0.0.0.0
	   DwForwardMetric1: 50.0.0.0
	   DwForwardMetric2: 255.255.255.255
	   DwForwardMetric3: 255.255.255.255
	   DwForwardMetric4: 255.255.255.255
	   DwForwardMetric5: 255.255.255.255

The DwForwardNextHop is the gateway.

logrusorgru avatar May 07 '19 12:05 logrusorgru

I wonder if searching for a route to 8.8.8.8 will work for people running in networks that try to prevent connecting to public DNS. I tried finding the best route to 0.0.0.0 and it seemed to give the same result as 8.8.8.8

Another issue is that I don't think this handles IPv6

jackpal avatar May 15 '19 06:05 jackpal

IPv6

GetBestRoute2 handles IPv6, but it appeared in Vista.

XP + IPv6

Probably, GetBestInterfaceEx can be used with a workaround or as a combination with the

calmh/syncthing@6a6ec72

Currently I have no idea how to associate windows network interface with adapter. As I know interface is network level abstraction, and adapter is hardware level abstraction. The calmh/syncthing@6a6ec72 obtains information for adapters. The GetBestInterfaceEx obtains interface index. Hm.... May be it is the same. May be not.

logrusorgru avatar May 15 '19 09:05 logrusorgru

Go is sort of not supported on XP anyway, so not sure it should matter.

AudriusButkevicius avatar May 15 '19 11:05 AudriusButkevicius

Аnyway, it turned out that the GetAdaptersInfo works only for IPv4.

logrusorgru avatar May 15 '19 12:05 logrusorgru

If the user has a complicated network they could have multiple gateways, each of which handles some subset of IP addresses. That's what makes it problematic to search for the best router for a given well-known IP address. On the other hand, I'm not sure we can do any better. (The current API for this code returns just one IP address after all.)

jackpal avatar Jun 13 '19 02:06 jackpal