"Fossies" - the Fresh Open Source Software Archive

Member "AdGuardHome-0.104.3/internal/dhcpd/network_utils.go" (19 Nov 2020, 7461 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "network_utils.go": 0.104.1_vs_0.104.3.

    1 package dhcpd
    2 
    3 import (
    4     "errors"
    5     "fmt"
    6     "io/ioutil"
    7     "net"
    8     "os/exec"
    9     "regexp"
   10     "runtime"
   11     "strings"
   12 
   13     "github.com/AdguardTeam/AdGuardHome/internal/util"
   14 
   15     "github.com/AdguardTeam/golibs/file"
   16 
   17     "github.com/AdguardTeam/golibs/log"
   18 )
   19 
   20 // HasStaticIP check if the network interface has a static IP configured
   21 //
   22 // Supports: Raspbian.
   23 func HasStaticIP(ifaceName string) (bool, error) {
   24     if runtime.GOOS == "linux" {
   25         body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
   26         if err != nil {
   27             return false, err
   28         }
   29 
   30         return hasStaticIPDhcpcdConf(string(body), ifaceName), nil
   31     }
   32 
   33     if runtime.GOOS == "darwin" {
   34         return hasStaticIPDarwin(ifaceName)
   35     }
   36 
   37     return false, fmt.Errorf("cannot check if IP is static: not supported on %s", runtime.GOOS)
   38 }
   39 
   40 // SetStaticIP sets a static IP for the network interface.
   41 func SetStaticIP(ifaceName string) error {
   42     if runtime.GOOS == "linux" {
   43         return setStaticIPDhcpdConf(ifaceName)
   44     }
   45 
   46     if runtime.GOOS == "darwin" {
   47         return setStaticIPDarwin(ifaceName)
   48     }
   49 
   50     return fmt.Errorf("cannot set static IP on %s", runtime.GOOS)
   51 }
   52 
   53 // for dhcpcd.conf
   54 func hasStaticIPDhcpcdConf(dhcpConf, ifaceName string) bool {
   55     lines := strings.Split(dhcpConf, "\n")
   56     nameLine := fmt.Sprintf("interface %s", ifaceName)
   57     withinInterfaceCtx := false
   58 
   59     for _, line := range lines {
   60         line = strings.TrimSpace(line)
   61 
   62         if withinInterfaceCtx && len(line) == 0 {
   63             // an empty line resets our state
   64             withinInterfaceCtx = false
   65         }
   66 
   67         if len(line) == 0 || line[0] == '#' {
   68             continue
   69         }
   70         line = strings.TrimSpace(line)
   71 
   72         if !withinInterfaceCtx {
   73             if line == nameLine {
   74                 // we found our interface
   75                 withinInterfaceCtx = true
   76             }
   77         } else {
   78             if strings.HasPrefix(line, "interface ") {
   79                 // we found another interface - reset our state
   80                 withinInterfaceCtx = false
   81                 continue
   82             }
   83             if strings.HasPrefix(line, "static ip_address=") {
   84                 return true
   85             }
   86         }
   87     }
   88     return false
   89 }
   90 
   91 // Get gateway IP address
   92 func getGatewayIP(ifaceName string) string {
   93     cmd := exec.Command("ip", "route", "show", "dev", ifaceName)
   94     log.Tracef("executing %s %v", cmd.Path, cmd.Args)
   95     d, err := cmd.Output()
   96     if err != nil || cmd.ProcessState.ExitCode() != 0 {
   97         return ""
   98     }
   99 
  100     fields := strings.Fields(string(d))
  101     if len(fields) < 3 || fields[0] != "default" {
  102         return ""
  103     }
  104 
  105     ip := net.ParseIP(fields[2])
  106     if ip == nil {
  107         return ""
  108     }
  109 
  110     return fields[2]
  111 }
  112 
  113 // setStaticIPDhcpdConf - updates /etc/dhcpd.conf and sets the current IP address to be static
  114 func setStaticIPDhcpdConf(ifaceName string) error {
  115     ip := util.GetSubnet(ifaceName)
  116     if len(ip) == 0 {
  117         return errors.New("can't get IP address")
  118     }
  119 
  120     ip4, _, err := net.ParseCIDR(ip)
  121     if err != nil {
  122         return err
  123     }
  124     gatewayIP := getGatewayIP(ifaceName)
  125     add := updateStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String())
  126 
  127     body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
  128     if err != nil {
  129         return err
  130     }
  131 
  132     body = append(body, []byte(add)...)
  133     err = file.SafeWrite("/etc/dhcpcd.conf", body)
  134     if err != nil {
  135         return err
  136     }
  137 
  138     return nil
  139 }
  140 
  141 // updates dhcpd.conf content -- sets static IP address there
  142 // for dhcpcd.conf
  143 func updateStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, dnsIP string) string {
  144     var body []byte
  145 
  146     add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n",
  147         ifaceName, ip)
  148     body = append(body, []byte(add)...)
  149 
  150     if len(gatewayIP) != 0 {
  151         add = fmt.Sprintf("static routers=%s\n",
  152             gatewayIP)
  153         body = append(body, []byte(add)...)
  154     }
  155 
  156     add = fmt.Sprintf("static domain_name_servers=%s\n\n",
  157         dnsIP)
  158     body = append(body, []byte(add)...)
  159 
  160     return string(body)
  161 }
  162 
  163 // Check if network interface has a static IP configured
  164 // Supports: MacOS.
  165 func hasStaticIPDarwin(ifaceName string) (bool, error) {
  166     portInfo, err := getCurrentHardwarePortInfo(ifaceName)
  167     if err != nil {
  168         return false, err
  169     }
  170 
  171     return portInfo.static, nil
  172 }
  173 
  174 // setStaticIPDarwin - uses networksetup util to set the current IP address to be static
  175 // Additionally it configures the current DNS servers as well
  176 func setStaticIPDarwin(ifaceName string) error {
  177     portInfo, err := getCurrentHardwarePortInfo(ifaceName)
  178     if err != nil {
  179         return err
  180     }
  181 
  182     if portInfo.static {
  183         return errors.New("IP address is already static")
  184     }
  185 
  186     dnsAddrs, err := getEtcResolvConfServers()
  187     if err != nil {
  188         return err
  189     }
  190 
  191     args := make([]string, 0)
  192     args = append(args, "-setdnsservers", portInfo.name)
  193     args = append(args, dnsAddrs...)
  194 
  195     // Setting DNS servers is necessary when configuring a static IP
  196     code, _, err := util.RunCommand("networksetup", args...)
  197     if err != nil {
  198         return err
  199     }
  200     if code != 0 {
  201         return fmt.Errorf("failed to set DNS servers, code=%d", code)
  202     }
  203 
  204     // Actually configures hardware port to have static IP
  205     code, _, err = util.RunCommand("networksetup", "-setmanual",
  206         portInfo.name, portInfo.ip, portInfo.subnet, portInfo.gatewayIP)
  207     if err != nil {
  208         return err
  209     }
  210     if code != 0 {
  211         return fmt.Errorf("failed to set DNS servers, code=%d", code)
  212     }
  213 
  214     return nil
  215 }
  216 
  217 // getCurrentHardwarePortInfo gets information the specified network interface
  218 func getCurrentHardwarePortInfo(ifaceName string) (hardwarePortInfo, error) {
  219     // First of all we should find hardware port name
  220     m := getNetworkSetupHardwareReports()
  221     hardwarePort, ok := m[ifaceName]
  222     if !ok {
  223         return hardwarePortInfo{}, fmt.Errorf("could not find hardware port for %s", ifaceName)
  224     }
  225 
  226     return getHardwarePortInfo(hardwarePort)
  227 }
  228 
  229 // getNetworkSetupHardwareReports parses the output of the `networksetup -listallhardwareports` command
  230 // it returns a map where the key is the interface name, and the value is the "hardware port"
  231 // returns nil if it fails to parse the output
  232 func getNetworkSetupHardwareReports() map[string]string {
  233     _, out, err := util.RunCommand("networksetup", "-listallhardwareports")
  234     if err != nil {
  235         return nil
  236     }
  237 
  238     re, err := regexp.Compile("Hardware Port: (.*?)\nDevice: (.*?)\n")
  239     if err != nil {
  240         return nil
  241     }
  242 
  243     m := make(map[string]string)
  244 
  245     matches := re.FindAllStringSubmatch(out, -1)
  246     for i := range matches {
  247         port := matches[i][1]
  248         device := matches[i][2]
  249         m[device] = port
  250     }
  251 
  252     return m
  253 }
  254 
  255 // hardwarePortInfo - information obtained using MacOS networksetup
  256 // about the current state of the internet connection
  257 type hardwarePortInfo struct {
  258     name      string
  259     ip        string
  260     subnet    string
  261     gatewayIP string
  262     static    bool
  263 }
  264 
  265 func getHardwarePortInfo(hardwarePort string) (hardwarePortInfo, error) {
  266     h := hardwarePortInfo{}
  267 
  268     _, out, err := util.RunCommand("networksetup", "-getinfo", hardwarePort)
  269     if err != nil {
  270         return h, err
  271     }
  272 
  273     re := regexp.MustCompile("IP address: (.*?)\nSubnet mask: (.*?)\nRouter: (.*?)\n")
  274 
  275     match := re.FindStringSubmatch(out)
  276     if len(match) == 0 {
  277         return h, errors.New("could not find hardware port info")
  278     }
  279 
  280     h.name = hardwarePort
  281     h.ip = match[1]
  282     h.subnet = match[2]
  283     h.gatewayIP = match[3]
  284 
  285     if strings.Index(out, "Manual Configuration") == 0 {
  286         h.static = true
  287     }
  288 
  289     return h, nil
  290 }
  291 
  292 // Gets a list of nameservers currently configured in the /etc/resolv.conf
  293 func getEtcResolvConfServers() ([]string, error) {
  294     body, err := ioutil.ReadFile("/etc/resolv.conf")
  295     if err != nil {
  296         return nil, err
  297     }
  298 
  299     re := regexp.MustCompile("nameserver ([a-zA-Z0-9.:]+)")
  300 
  301     matches := re.FindAllStringSubmatch(string(body), -1)
  302     if len(matches) == 0 {
  303         return nil, errors.New("found no DNS servers in /etc/resolv.conf")
  304     }
  305 
  306     addrs := make([]string, 0)
  307     for i := range matches {
  308         addrs = append(addrs, matches[i][1])
  309     }
  310 
  311     return addrs, nil
  312 }