AdGuardHome/internal/aghnet/net_darwin.go
Eugene Burkov 3a67cc2c45 Pull request: create aghnet package
Merge in DNS/adguard-home from mk-aghnet to master

Updates #2829.

Squashed commit of the following:

commit 519806c04b8d0517aacce9c31f2d06ab56631937
Merge: 92376c86 97361234
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 19:13:56 2021 +0300

    Merge branch 'master' into mk-aghnet

commit 92376c8665e529191aa482432f9d5e3e2e3afdc8
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 18:37:22 2021 +0300

    aghnet: fix linux

commit 7f36d19b0e650d4e4fc5cf9ea4b501a7f636abeb
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 18:08:30 2021 +0300

    aghnet: mv network utils from util

commit aa68c70c1146b8c32303c6e037953a41aa7d72f9
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 17:30:06 2021 +0300

    aghnet: mv ipDetector here

commit b033657f5c5ee91f869c36508a5eb15976a174a0
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 17:24:07 2021 +0300

    all: mk aghnet package, rename sysutil package
2021-03-16 19:42:15 +03:00

162 lines
3.8 KiB
Go

// +build darwin
package aghnet
import (
"errors"
"fmt"
"io/ioutil"
"regexp"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/util"
)
// hardwarePortInfo - information obtained using MacOS networksetup
// about the current state of the internet connection
type hardwarePortInfo struct {
name string
ip string
subnet string
gatewayIP string
static bool
}
func ifaceHasStaticIP(ifaceName string) (bool, error) {
portInfo, err := getCurrentHardwarePortInfo(ifaceName)
if err != nil {
return false, err
}
return portInfo.static, nil
}
// getCurrentHardwarePortInfo gets information the specified network interface.
func getCurrentHardwarePortInfo(ifaceName string) (hardwarePortInfo, error) {
// First of all we should find hardware port name
m := getNetworkSetupHardwareReports()
hardwarePort, ok := m[ifaceName]
if !ok {
return hardwarePortInfo{}, fmt.Errorf("could not find hardware port for %s", ifaceName)
}
return getHardwarePortInfo(hardwarePort)
}
// getNetworkSetupHardwareReports parses the output of the `networksetup -listallhardwareports` command
// it returns a map where the key is the interface name, and the value is the "hardware port"
// returns nil if it fails to parse the output
func getNetworkSetupHardwareReports() map[string]string {
_, out, err := util.RunCommand("networksetup", "-listallhardwareports")
if err != nil {
return nil
}
re, err := regexp.Compile("Hardware Port: (.*?)\nDevice: (.*?)\n")
if err != nil {
return nil
}
m := make(map[string]string)
matches := re.FindAllStringSubmatch(out, -1)
for i := range matches {
port := matches[i][1]
device := matches[i][2]
m[device] = port
}
return m
}
func getHardwarePortInfo(hardwarePort string) (hardwarePortInfo, error) {
h := hardwarePortInfo{}
_, out, err := util.RunCommand("networksetup", "-getinfo", hardwarePort)
if err != nil {
return h, err
}
re := regexp.MustCompile("IP address: (.*?)\nSubnet mask: (.*?)\nRouter: (.*?)\n")
match := re.FindStringSubmatch(out)
if len(match) == 0 {
return h, errors.New("could not find hardware port info")
}
h.name = hardwarePort
h.ip = match[1]
h.subnet = match[2]
h.gatewayIP = match[3]
if strings.Index(out, "Manual Configuration") == 0 {
h.static = true
}
return h, nil
}
func ifaceSetStaticIP(ifaceName string) (err error) {
portInfo, err := getCurrentHardwarePortInfo(ifaceName)
if err != nil {
return err
}
if portInfo.static {
return errors.New("IP address is already static")
}
dnsAddrs, err := getEtcResolvConfServers()
if err != nil {
return err
}
args := make([]string, 0)
args = append(args, "-setdnsservers", portInfo.name)
args = append(args, dnsAddrs...)
// Setting DNS servers is necessary when configuring a static IP
code, _, err := util.RunCommand("networksetup", args...)
if err != nil {
return err
}
if code != 0 {
return fmt.Errorf("failed to set DNS servers, code=%d", code)
}
// Actually configures hardware port to have static IP
code, _, err = util.RunCommand("networksetup", "-setmanual",
portInfo.name, portInfo.ip, portInfo.subnet, portInfo.gatewayIP)
if err != nil {
return err
}
if code != 0 {
return fmt.Errorf("failed to set DNS servers, code=%d", code)
}
return nil
}
// getEtcResolvConfServers returns a list of nameservers configured in
// /etc/resolv.conf.
func getEtcResolvConfServers() ([]string, error) {
body, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
return nil, err
}
re := regexp.MustCompile("nameserver ([a-zA-Z0-9.:]+)")
matches := re.FindAllStringSubmatch(string(body), -1)
if len(matches) == 0 {
return nil, errors.New("found no DNS servers in /etc/resolv.conf")
}
addrs := make([]string, 0)
for i := range matches {
addrs = append(addrs, matches[i][1])
}
return addrs, nil
}