mirror of
https://github.com/safing/portmaster
synced 2025-09-01 10:09:11 +00:00
Switch to powershell for getting gateways and nameservers on Windows
This commit is contained in:
parent
5518e952b2
commit
d16810c0e9
1 changed files with 168 additions and 79 deletions
|
@ -3,93 +3,182 @@ package netenv
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/utils/osdetail"
|
||||
)
|
||||
|
||||
const (
|
||||
nameserversRecheck = 2 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
nameservers = make([]Nameserver, 0)
|
||||
nameserversLock sync.Mutex
|
||||
nameserversExpires = time.Now()
|
||||
)
|
||||
|
||||
// Nameservers returns the currently active nameservers.
|
||||
func Nameservers() []Nameserver {
|
||||
// locking
|
||||
nameserversLock.Lock()
|
||||
defer nameserversLock.Unlock()
|
||||
// cache
|
||||
if nameserversExpires.After(time.Now()) {
|
||||
return nameservers
|
||||
}
|
||||
// update cache expiry when finished
|
||||
defer func() {
|
||||
nameserversExpires = time.Now().Add(nameserversRecheck)
|
||||
}()
|
||||
|
||||
// reset
|
||||
nameservers = make([]Nameserver, 0)
|
||||
|
||||
// This is a preliminary workaround until we have more time for proper interface using iphlpapi.dll
|
||||
// TODO: make nice implementation
|
||||
|
||||
var output = make(chan []byte, 1)
|
||||
module.StartWorker("get assigned nameservers", func(ctx context.Context) error {
|
||||
cmd := exec.CommandContext(ctx, "nslookup", "localhost")
|
||||
data, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Debugf("netenv: failed to get assigned nameserves: %s", err)
|
||||
output <- nil
|
||||
} else {
|
||||
output <- data
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case data := <-output:
|
||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||
scanner.Split(bufio.ScanLines)
|
||||
for scanner.Scan() {
|
||||
// check if we found the correct line
|
||||
if !strings.HasPrefix(scanner.Text(), "Address:") {
|
||||
continue
|
||||
}
|
||||
// split into fields, check if we have enough fields
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
// parse nameserver, return if valid IP found
|
||||
ns := net.ParseIP(fields[1])
|
||||
if ns != nil {
|
||||
nameservers = append(nameservers, Nameserver{
|
||||
IP: ns,
|
||||
})
|
||||
return nameservers
|
||||
}
|
||||
}
|
||||
log.Debug("netenv: could not find assigned nameserver")
|
||||
return nameservers
|
||||
|
||||
case <-time.After(5 * time.Second):
|
||||
log.Debug("netenv: timed out while getting assigned nameserves")
|
||||
}
|
||||
|
||||
return nameservers
|
||||
}
|
||||
|
||||
// Gateways returns the currently active gateways.
|
||||
func Gateways() []net.IP {
|
||||
return nil
|
||||
defaultIf := getDefaultInterface()
|
||||
if defaultIf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect gateways.
|
||||
var gw []net.IP
|
||||
if defaultIf.IPv4DefaultGateway != nil {
|
||||
gw = append(gw, defaultIf.IPv4DefaultGateway)
|
||||
}
|
||||
if defaultIf.IPv6DefaultGateway != nil {
|
||||
gw = append(gw, defaultIf.IPv6DefaultGateway)
|
||||
}
|
||||
|
||||
return gw
|
||||
}
|
||||
|
||||
// Nameservers returns the currently active nameservers.
|
||||
func Nameservers() []Nameserver {
|
||||
defaultIf := getDefaultInterface()
|
||||
if defaultIf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compile search list.
|
||||
var search []string
|
||||
if defaultIf.DNSServerConfig != nil {
|
||||
if defaultIf.DNSServerConfig.Suffix != "" {
|
||||
search = append(search, defaultIf.DNSServerConfig.Suffix)
|
||||
}
|
||||
if len(defaultIf.DNSServerConfig.SuffixSearchList) > 0 {
|
||||
search = append(search, defaultIf.DNSServerConfig.SuffixSearchList...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compile nameservers.
|
||||
var ns []Nameserver
|
||||
for _, nsIP := range defaultIf.DNSServer {
|
||||
ns = append(ns, Nameserver{
|
||||
IP: nsIP,
|
||||
Search: search,
|
||||
})
|
||||
}
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
const (
|
||||
defaultInterfaceRecheck = 2 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
defaultInterface *defaultNetInterface
|
||||
defaultInterfaceLock sync.Mutex
|
||||
defaultInterfaceNetworkChangedFlag = GetNetworkChangedFlag()
|
||||
)
|
||||
|
||||
type defaultNetInterface struct {
|
||||
InterfaceIndex string
|
||||
IPv6Address net.IP
|
||||
IPv4Address net.IP
|
||||
IPv6DefaultGateway net.IP
|
||||
IPv4DefaultGateway net.IP
|
||||
DNSServer []net.IP
|
||||
DNSServerConfig *dnsServerConfig
|
||||
}
|
||||
|
||||
type dnsServerConfig struct {
|
||||
Suffix string
|
||||
SuffixSearchList []string
|
||||
}
|
||||
|
||||
func getDefaultInterface() *defaultNetInterface {
|
||||
defaultInterfaceLock.Lock()
|
||||
defer defaultInterfaceLock.Unlock()
|
||||
// Check if the network changed, if not, return cache.
|
||||
if !defaultInterfaceNetworkChangedFlag.IsSet() {
|
||||
return defaultInterface
|
||||
}
|
||||
defaultInterfaceNetworkChangedFlag.Refresh()
|
||||
|
||||
// Get interface data from Windows.
|
||||
interfaceData, err := osdetail.RunPowershellCmd("Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Select-Object -First 1 | Get-NetIPConfiguration | Format-List")
|
||||
if err != nil {
|
||||
log.Warningf("netenv: failed to get interface data: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: It would be great to get this as json. Powershell can do this,
|
||||
// but it just spits out lots of weird data instead of the same strings
|
||||
// seen in the list.
|
||||
newIf := &defaultNetInterface{}
|
||||
|
||||
// Scan data for needed fields.
|
||||
scanner := bufio.NewScanner(bytes.NewBufferString(interfaceData))
|
||||
scanner.Split(bufio.ScanLines)
|
||||
var segmentKey, segmentValue, previousKey string
|
||||
for scanner.Scan() {
|
||||
segments := strings.SplitN(scanner.Text(), " : ", 2)
|
||||
|
||||
// Check what the line gives us.
|
||||
switch len(segments) {
|
||||
case 2:
|
||||
// This is a new key and value.
|
||||
segmentKey = strings.TrimSpace(segments[0])
|
||||
segmentValue = strings.TrimSpace(segments[1])
|
||||
previousKey = segmentKey
|
||||
case 1:
|
||||
// This is another value for the previous key.
|
||||
segmentKey = previousKey
|
||||
segmentValue = strings.TrimSpace(segments[0])
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore empty lines.
|
||||
if segmentValue == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse and assign value to struct.
|
||||
switch segmentKey {
|
||||
case "InterfaceIndex":
|
||||
newIf.InterfaceIndex = segmentValue
|
||||
case "IPv6Address":
|
||||
newIf.IPv6Address = net.ParseIP(segmentValue)
|
||||
case "IPv4Address":
|
||||
newIf.IPv4Address = net.ParseIP(segmentValue)
|
||||
case "IPv6DefaultGateway":
|
||||
newIf.IPv6DefaultGateway = net.ParseIP(segmentValue)
|
||||
case "IPv4DefaultGateway":
|
||||
newIf.IPv4DefaultGateway = net.ParseIP(segmentValue)
|
||||
case "DNSServer":
|
||||
newIP := net.ParseIP(segmentValue)
|
||||
if newIP != nil {
|
||||
newIf.DNSServer = append(newIf.DNSServer, newIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get Search Scopes for this interface.
|
||||
if newIf.InterfaceIndex != "" {
|
||||
dnsConfigData, err := osdetail.RunPowershellCmd(fmt.Sprintf(
|
||||
"Get-DnsClient -InterfaceIndex %s | ConvertTo-Json -Depth 1",
|
||||
newIf.InterfaceIndex,
|
||||
))
|
||||
if err != nil {
|
||||
log.Warningf("netenv: failed to get dns server config data: %s", err)
|
||||
} else {
|
||||
// Parse data into struct.
|
||||
dnsConfig := &dnsServerConfig{}
|
||||
err := json.Unmarshal([]byte(dnsConfigData), dnsConfig)
|
||||
if err != nil {
|
||||
log.Warningf("netenv: failed to get dns server config data: %s", err)
|
||||
} else {
|
||||
newIf.DNSServerConfig = dnsConfig
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Warning("netenv: could not get dns server config data, because default interface index is missing")
|
||||
}
|
||||
|
||||
// Assign new value to cache and return.
|
||||
defaultInterface = newIf
|
||||
return defaultInterface
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue