Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
//go:build windows
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WindowsNetworkDiscoverer discovers network configuration using netsh commands.
|
||||
// It does not require elevated privileges or PowerShell.
|
||||
type WindowsNetworkDiscoverer struct{}
|
||||
|
||||
var ipv4Re = regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
|
||||
var dnsLineRe = regexp.MustCompile(`^\s*(?:\S.*:\s+)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*$`)
|
||||
|
||||
// Discover returns the gateway IP and DNS servers for the default IPv4 route.
|
||||
func (d *WindowsNetworkDiscoverer) Discover() (NetworkInfo, error) {
|
||||
info := NetworkInfo{}
|
||||
|
||||
// Step 1: Find default route — extract interface index and gateway IP.
|
||||
ifIdx, gateway, err := windowsDefaultRoute()
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("reading default IPv4 route: %w", err)
|
||||
}
|
||||
info.Gateway = gateway
|
||||
|
||||
// Step 2: Map interface index to name.
|
||||
ifName, err := windowsInterfaceName(ifIdx)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("resolving interface name for index %d: %w", ifIdx, err)
|
||||
}
|
||||
info.Interface = ifName
|
||||
|
||||
// Step 3: Enumerate DNS servers for that interface.
|
||||
servers, err := windowsDNSServers(ifName)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("reading DNS servers for interface %q: %w", ifName, err)
|
||||
}
|
||||
info.DNSServers = servers
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// windowsDefaultRoute runs "netsh interface ipv4 show route" and returns the
|
||||
// interface index and gateway IP for the best (lowest-metric) 0.0.0.0/0 route.
|
||||
func windowsDefaultRoute() (ifIdx int, gateway string, err error) {
|
||||
out, err := exec.Command("netsh", "interface", "ipv4", "show", "route").Output()
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("netsh interface ipv4 show route: %w", err)
|
||||
}
|
||||
|
||||
bestMetric := -1
|
||||
bestIdx := 0
|
||||
bestGateway := ""
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(out)))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.Contains(line, "0.0.0.0/0") {
|
||||
continue
|
||||
}
|
||||
// Format: <Publish> <Type> <Metric> <Prefix> <Idx> <Gateway or Iface Name...>
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 6 {
|
||||
continue
|
||||
}
|
||||
metric, err := strconv.Atoi(fields[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
idx, err := strconv.Atoi(fields[4])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Gateway field is field 5+ — join and extract first IPv4.
|
||||
gatewayField := strings.Join(fields[5:], " ")
|
||||
m := ipv4Re.FindString(gatewayField)
|
||||
if m == "" {
|
||||
continue
|
||||
}
|
||||
if bestMetric < 0 || metric < bestMetric {
|
||||
bestMetric = metric
|
||||
bestIdx = idx
|
||||
bestGateway = m
|
||||
}
|
||||
}
|
||||
|
||||
if bestIdx == 0 {
|
||||
return 0, "", fmt.Errorf("no default IPv4 route found")
|
||||
}
|
||||
return bestIdx, bestGateway, nil
|
||||
}
|
||||
|
||||
// windowsInterfaceName runs "netsh interface ipv4 show interfaces" and maps
|
||||
// the given interface index to the interface name.
|
||||
func windowsInterfaceName(idx int) (string, error) {
|
||||
out, err := exec.Command("netsh", "interface", "ipv4", "show", "interfaces").Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("netsh interface ipv4 show interfaces: %w", err)
|
||||
}
|
||||
|
||||
idxStr := strconv.Itoa(idx)
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(out)))
|
||||
// Skip two header lines.
|
||||
for i := 0; i < 2; i++ {
|
||||
scanner.Scan()
|
||||
}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 5 {
|
||||
continue
|
||||
}
|
||||
if fields[0] != idxStr {
|
||||
continue
|
||||
}
|
||||
// Name is fields[4:] joined — may contain spaces.
|
||||
return strings.Join(fields[4:], " "), nil
|
||||
}
|
||||
return "", fmt.Errorf("interface index %d not found", idx)
|
||||
}
|
||||
|
||||
// windowsDNSServers runs "netsh interface ipv4 show dnsservers name=<ifName>"
|
||||
// and returns all IPv4 DNS server addresses in order.
|
||||
func windowsDNSServers(ifName string) ([]string, error) {
|
||||
out, err := exec.Command("netsh", "interface", "ipv4", "show", "dnsservers",
|
||||
"name="+ifName).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("netsh interface ipv4 show dnsservers: %w", err)
|
||||
}
|
||||
|
||||
var servers []string
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(out)))
|
||||
for scanner.Scan() {
|
||||
m := dnsLineRe.FindStringSubmatch(scanner.Text())
|
||||
if len(m) < 2 {
|
||||
continue
|
||||
}
|
||||
ip := m[1]
|
||||
if net.ParseIP(ip) != nil {
|
||||
servers = append(servers, ip)
|
||||
}
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
Reference in New Issue
Block a user