Skip to content

Commit

Permalink
Merge pull request #34 from angrycub/f-win-ps-route
Browse files Browse the repository at this point in the history
Add PowerShell impl. of GetDefaultInterfaceName
  • Loading branch information
angrycub committed Jun 9, 2023
2 parents 1bfef14 + cc581bb commit 21bd712
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
21 changes: 17 additions & 4 deletions ifaddrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ var (
// Centralize all regexps and regexp.Copy() where necessary.
signRE *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`)
whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`)
ifNameRE *regexp.Regexp = regexp.MustCompile(`^(?:Ethernet|Wireless LAN) adapter ([^:]+):`)
ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
// These regular expressions enable the deprecated parseDefaultIfNameWindows
// and should be removed when those functions are.
ifNameRE *regexp.Regexp = regexp.MustCompile(`^(?:Ethernet|Wireless LAN) adapter ([^:]+):`)
ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
)

// IfAddrs is a slice of IfAddr
Expand Down Expand Up @@ -1214,15 +1216,14 @@ func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
// Android.
func parseDefaultIfNameFromIPCmdAndroid(routeOut string) (string, error) {
parsedLines := parseIfNameFromIPCmd(routeOut)
if (len(parsedLines) > 0) {
if len(parsedLines) > 0 {
ifName := strings.TrimSpace(parsedLines[0][4])
return ifName, nil
}

return "", errors.New("No default interface found")
}


// parseIfNameFromIPCmd parses interfaces from ip(8) for
// Linux.
func parseIfNameFromIPCmd(routeOut string) [][]string {
Expand All @@ -1241,6 +1242,10 @@ func parseIfNameFromIPCmd(routeOut string) [][]string {

// parseDefaultIfNameWindows parses the default interface from `netstat -rn` and
// `ipconfig` on Windows.
//
// This has been deprecated in favor of a Powershell-based solution because of
// issues with localized Windows versions, but is currently retained for backward
// compatibility
func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
defaultIPAddr, err := parseDefaultIPAddrWindowsRoute(routeOut)
if err != nil {
Expand All @@ -1262,6 +1267,10 @@ func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
// IPv6 connected host, submit an issue on github.com/hashicorp/go-sockaddr with
// the output from `netstat -rn`, `ipconfig`, and version of Windows to see IPv6
// support added.
//
// This has been deprecated in favor of a Powershell-based solution because of
// issues with localized Windows versions, but is currently retained for backward
// compatibility.
func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n")
re := whitespaceRE.Copy()
Expand All @@ -1282,6 +1291,10 @@ func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {

// parseDefaultIfNameWindowsIPConfig parses the output of `ipconfig` to find the
// interface name forwarding traffic to the default gateway.
//
// This has been deprecated in favor of a Powershell-based solution because of
// issues with localized Windows versions, but is currently retained for backward
// compatibility
func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n")
ifNameRe := ifNameRE.Copy()
Expand Down
25 changes: 25 additions & 0 deletions route_info_test_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package sockaddr

import "testing"

func Test_parseWindowsDefaultIfName_new_vs_old(t *testing.T) {
if !hasPowershell() {
t.Skip("this test requires powershell.")
return
}
ri, err := NewRouteInfo()
if err != nil {
t.Fatalf("bad: %v", err)
}
psVer, err1 := ri.GetDefaultInterfaceName()
legacyVer, err2 := ri.GetDefaultInterfaceNameLegacy()
if err1 != nil {
t.Errorf("err != nil for GetDefaultInterfaceName - %v", err1)
}
if err2 != nil {
t.Errorf("err != nil for GetDefaultInterfaceNameLegacy - %v", err2)
}
if psVer != legacyVer {
t.Errorf("got %s; want %s", psVer, legacyVer)
}
}
30 changes: 29 additions & 1 deletion route_info_windows.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package sockaddr

import "os/exec"
import (
"os/exec"
"strings"
)

var cmds map[string][]string = map[string][]string{
"defaultInterface": {"powershell", "Get-NetRoute -DestinationPrefix '0.0.0.0/0' | select -ExpandProperty InterfaceAlias"},
// These commands enable GetDefaultInterfaceNameLegacy and should be removed
// when it is.
"netstat": {"netstat", "-rn"},
"ipconfig": {"ipconfig"},
}
Expand All @@ -22,6 +28,23 @@ func NewRouteInfo() (routeInfo, error) {
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
if !hasPowershell() {
// No powershell, fallback to legacy method
return ri.GetDefaultInterfaceNameLegacy()
}

ifNameOut, err := exec.Command(cmds["defaultInterface"][0], cmds["defaultInterface"][1:]...).Output()
if err != nil {
return "", err
}

ifName := strings.TrimSpace(string(ifNameOut[:]))
return ifName, nil
}

// GetDefaultInterfaceNameLegacy provides legacy behavior for GetDefaultInterfaceName
// on Windows machines without powershell.
func (ri routeInfo) GetDefaultInterfaceNameLegacy() (string, error) {
ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output()
if err != nil {
return "", err
Expand All @@ -39,3 +62,8 @@ func (ri routeInfo) GetDefaultInterfaceName() (string, error) {

return ifName, nil
}

func hasPowershell() bool {
_, err := exec.LookPath("powershell")
return (err != nil)
}

0 comments on commit 21bd712

Please sign in to comment.