From d8d48c53869a94d18c5ea7bcf78613e83b24bfd8 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Wed, 12 Feb 2020 15:04:25 +0300 Subject: [PATCH] *(dhcpd): refactoring, use dhcpd/network_utils where possible --- dhcpd/dhcp_http.go | 11 +- dhcpd/network_utils.go | 123 ++++++++--------- dhcpd/network_utils_darwin.go | 8 ++ {home => dhcpd}/network_utils_test.go | 2 +- home/control_install.go | 8 +- home/network_utils.go | 183 +------------------------- 6 files changed, 88 insertions(+), 247 deletions(-) create mode 100644 dhcpd/network_utils_darwin.go rename {home => dhcpd}/network_utils_test.go (98%) diff --git a/dhcpd/dhcp_http.go b/dhcpd/dhcp_http.go index 35f6a7ad..6031b9a8 100644 --- a/dhcpd/dhcp_http.go +++ b/dhcpd/dhcp_http.go @@ -93,10 +93,9 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { s.conf.ConfigModified() if newconfig.Enabled { - - staticIP, err := hasStaticIP(newconfig.InterfaceName) + staticIP, err := HasStaticIP(newconfig.InterfaceName) if !staticIP && err == nil { - err = setStaticIP(newconfig.InterfaceName) + err = SetStaticIP(newconfig.InterfaceName) if err != nil { httpError(r, w, http.StatusInternalServerError, "Failed to configure static IP: %s", err) return @@ -122,7 +121,7 @@ type netInterfaceJSON struct { func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { response := map[string]interface{}{} - ifaces, err := getValidNetInterfaces() + ifaces, err := GetValidNetInterfaces() if err != nil { httpError(r, w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) return @@ -213,14 +212,14 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque othSrv["found"] = foundVal staticIP := map[string]interface{}{} - isStaticIP, err := hasStaticIP(interfaceName) + isStaticIP, err := HasStaticIP(interfaceName) staticIPStatus := "yes" if err != nil { staticIPStatus = "error" staticIP["error"] = err.Error() } else if !isStaticIP { staticIPStatus = "no" - staticIP["ip"] = getFullIP(interfaceName) + staticIP["ip"] = GetFullIP(interfaceName) } staticIP["static"] = staticIPStatus diff --git a/dhcpd/network_utils.go b/dhcpd/network_utils.go index 85fc261d..204105ae 100644 --- a/dhcpd/network_utils.go +++ b/dhcpd/network_utils.go @@ -10,12 +10,13 @@ import ( "strings" "github.com/AdguardTeam/golibs/file" + "github.com/AdguardTeam/golibs/log" ) -// getValidNetInterfaces returns interfaces that are eligible for DNS and/or DHCP +// GetValidNetInterfaces returns interfaces that are eligible for DNS and/or DHCP // invalid interface is a ppp interface or the one that doesn't allow broadcasts -func getValidNetInterfaces() ([]net.Interface, error) { +func GetValidNetInterfaces() ([]net.Interface, error) { ifaces, err := net.Interfaces() if err != nil { return nil, fmt.Errorf("Couldn't get list of interfaces: %s", err) @@ -38,22 +39,76 @@ func getValidNetInterfaces() ([]net.Interface, error) { // Check if network interface has a static IP configured // Supports: Raspbian. -func hasStaticIP(ifaceName string) (bool, error) { - if runtime.GOOS == "windows" { - return false, errors.New("Can't detect static IP: not supported on Windows") +func HasStaticIP(ifaceName string) (bool, error) { + if runtime.GOOS == "linux" { + body, err := ioutil.ReadFile("/etc/dhcpcd.conf") + if err != nil { + return false, err + } + + return hasStaticIPDhcpcdConf(string(body), ifaceName), nil } + if runtime.GOOS == "darwin" { + return hasStaticIPDarwin(ifaceName) + } + + return false, fmt.Errorf("Cannot check if IP is static: not supported on %s", runtime.GOOS) +} + +// Get IP address with netmask +func GetFullIP(ifaceName string) string { + cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName) + log.Tracef("executing %s %v", cmd.Path, cmd.Args) + d, err := cmd.Output() + if err != nil || cmd.ProcessState.ExitCode() != 0 { + return "" + } + + fields := strings.Fields(string(d)) + if len(fields) < 4 { + return "" + } + _, _, err = net.ParseCIDR(fields[3]) + if err != nil { + return "" + } + + return fields[3] +} + +// Set a static IP for network interface +// Supports: Raspbian. +func SetStaticIP(ifaceName string) error { + ip := GetFullIP(ifaceName) + if len(ip) == 0 { + return errors.New("Can't get IP address") + } + + ip4, _, err := net.ParseCIDR(ip) + if err != nil { + return err + } + gatewayIP := getGatewayIP(ifaceName) + add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String()) + body, err := ioutil.ReadFile("/etc/dhcpcd.conf") if err != nil { - return false, err + return err } - return hasStaticIPDhcpcdConf(string(body), ifaceName), nil + body = append(body, []byte(add)...) + err = file.SafeWrite("/etc/dhcpcd.conf", body) + if err != nil { + return err + } + + return nil } // for dhcpcd.conf -func hasStaticIPDhcpcdConf(data, ifaceName string) bool { - lines := strings.Split(data, "\n") +func hasStaticIPDhcpcdConf(dhcpConf, ifaceName string) bool { + lines := strings.Split(dhcpConf, "\n") nameLine := fmt.Sprintf("interface %s", ifaceName) withinInterfaceCtx := false @@ -90,27 +145,6 @@ func hasStaticIPDhcpcdConf(data, ifaceName string) bool { return false } -// Get IP address with netmask -func getFullIP(ifaceName string) string { - cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName) - log.Tracef("executing %s %v", cmd.Path, cmd.Args) - d, err := cmd.Output() - if err != nil || cmd.ProcessState.ExitCode() != 0 { - return "" - } - - fields := strings.Fields(string(d)) - if len(fields) < 4 { - return "" - } - _, _, err = net.ParseCIDR(fields[3]) - if err != nil { - return "" - } - - return fields[3] -} - // Get gateway IP address func getGatewayIP(ifaceName string) string { cmd := exec.Command("ip", "route", "show", "dev", ifaceName) @@ -133,35 +167,6 @@ func getGatewayIP(ifaceName string) string { return fields[2] } -// Set a static IP for network interface -// Supports: Raspbian. -func setStaticIP(ifaceName string) error { - ip := getFullIP(ifaceName) - if len(ip) == 0 { - return errors.New("Can't get IP address") - } - - ip4, _, err := net.ParseCIDR(ip) - if err != nil { - return err - } - gatewayIP := getGatewayIP(ifaceName) - add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String()) - - body, err := ioutil.ReadFile("/etc/dhcpcd.conf") - if err != nil { - return err - } - - body = append(body, []byte(add)...) - err = file.SafeWrite("/etc/dhcpcd.conf", body) - if err != nil { - return err - } - - return nil -} - // for dhcpcd.conf func setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, dnsIP string) string { var body []byte diff --git a/dhcpd/network_utils_darwin.go b/dhcpd/network_utils_darwin.go new file mode 100644 index 00000000..9110b0b0 --- /dev/null +++ b/dhcpd/network_utils_darwin.go @@ -0,0 +1,8 @@ +package dhcpd + +// Check if network interface has a static IP configured +// Supports: Raspbian. +func hasStaticIPDarwin(ifaceName string) (bool, error) { + + return false, nil +} diff --git a/home/network_utils_test.go b/dhcpd/network_utils_test.go similarity index 98% rename from home/network_utils_test.go rename to dhcpd/network_utils_test.go index 6312e535..3948a224 100644 --- a/home/network_utils_test.go +++ b/dhcpd/network_utils_test.go @@ -1,4 +1,4 @@ -package home +package dhcpd import ( "testing" diff --git a/home/control_install.go b/home/control_install.go index 45cf0c7c..196b792b 100644 --- a/home/control_install.go +++ b/home/control_install.go @@ -13,6 +13,8 @@ import ( "runtime" "strconv" + "github.com/AdguardTeam/AdGuardHome/dhcpd" + "github.com/AdguardTeam/golibs/log" ) @@ -141,19 +143,19 @@ func handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) { respData.StaticIP.Error = fmt.Sprintf("Couldn't find network interface by IP %s", reqData.DNS.IP) } else if reqData.DNS.SetStaticIP { - err = setStaticIP(interfaceName) + err = dhcpd.SetStaticIP(interfaceName) staticIPStatus = "error" respData.StaticIP.Error = err.Error() } else { // check if we have a static IP - isStaticIP, err := hasStaticIP(interfaceName) + isStaticIP, err := dhcpd.HasStaticIP(interfaceName) if err != nil { staticIPStatus = "error" respData.StaticIP.Error = err.Error() } else if !isStaticIP { staticIPStatus = "no" - respData.StaticIP.IP = getFullIP(interfaceName) + respData.StaticIP.IP = dhcpd.GetFullIP(interfaceName) } } respData.StaticIP.Static = staticIPStatus diff --git a/home/network_utils.go b/home/network_utils.go index 04112001..4277c1c2 100644 --- a/home/network_utils.go +++ b/home/network_utils.go @@ -3,18 +3,15 @@ package home import ( "errors" "fmt" - "io/ioutil" "net" "os" - "os/exec" "runtime" "strconv" - "strings" "syscall" "time" - "github.com/AdguardTeam/golibs/file" - "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/AdGuardHome/dhcpd" + "github.com/joomcode/errorx" ) @@ -26,33 +23,10 @@ type netInterface struct { Flags string } -// getValidNetInterfaces returns interfaces that are eligible for DNS and/or DHCP -// invalid interface is a ppp interface or the one that doesn't allow broadcasts -func getValidNetInterfaces() ([]net.Interface, error) { - ifaces, err := net.Interfaces() - if err != nil { - return nil, fmt.Errorf("Couldn't get list of interfaces: %s", err) - } - - netIfaces := []net.Interface{} - - for i := range ifaces { - if ifaces[i].Flags&net.FlagPointToPoint != 0 { - // this interface is ppp, we're not interested in this one - continue - } - - iface := ifaces[i] - netIfaces = append(netIfaces, iface) - } - - return netIfaces, nil -} - // getValidNetInterfacesMap returns interfaces that are eligible for DNS and WEB only // we do not return link-local addresses here func getValidNetInterfacesForWeb() ([]netInterface, error) { - ifaces, err := getValidNetInterfaces() + ifaces, err := dhcpd.GetValidNetInterfaces() if err != nil { return nil, errorx.Decorate(err, "Couldn't get interfaces") } @@ -99,81 +73,6 @@ func getValidNetInterfacesForWeb() ([]netInterface, error) { return netInterfaces, nil } -// Check if network interface has a static IP configured -// Supports: Raspbian. -func hasStaticIP(ifaceName string) (bool, error) { - if runtime.GOOS == "windows" { - return false, errors.New("Can't detect static IP: not supported on Windows") - } - - body, err := ioutil.ReadFile("/etc/dhcpcd.conf") - if err != nil { - return false, err - } - - return hasStaticIPDhcpcdConf(string(body), ifaceName), nil -} - -// for dhcpcd.conf -func hasStaticIPDhcpcdConf(data, ifaceName string) bool { - lines := strings.Split(data, "\n") - nameLine := fmt.Sprintf("interface %s", ifaceName) - withinInterfaceCtx := false - - for _, line := range lines { - line = strings.TrimSpace(line) - - if withinInterfaceCtx && len(line) == 0 { - // an empty line resets our state - withinInterfaceCtx = false - } - - if len(line) == 0 || line[0] == '#' { - continue - } - line = strings.TrimSpace(line) - - if !withinInterfaceCtx { - if line == nameLine { - // we found our interface - withinInterfaceCtx = true - } - - } else { - if strings.HasPrefix(line, "interface ") { - // we found another interface - reset our state - withinInterfaceCtx = false - continue - } - if strings.HasPrefix(line, "static ip_address=") { - return true - } - } - } - return false -} - -// Get IP address with netmask -func getFullIP(ifaceName string) string { - cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName) - log.Tracef("executing %s %v", cmd.Path, cmd.Args) - d, err := cmd.Output() - if err != nil || cmd.ProcessState.ExitCode() != 0 { - return "" - } - - fields := strings.Fields(string(d)) - if len(fields) < 4 { - return "" - } - _, _, err = net.ParseCIDR(fields[3]) - if err != nil { - return "" - } - - return fields[3] -} - // Get interface name by its IP address. func getInterfaceByIP(ip string) string { ifaces, err := getValidNetInterfacesForWeb() @@ -192,85 +91,13 @@ func getInterfaceByIP(ip string) string { return "" } -// Get gateway IP address -func getGatewayIP(ifaceName string) string { - cmd := exec.Command("ip", "route", "show", "dev", ifaceName) - log.Tracef("executing %s %v", cmd.Path, cmd.Args) - d, err := cmd.Output() - if err != nil || cmd.ProcessState.ExitCode() != 0 { - return "" - } - - fields := strings.Fields(string(d)) - if len(fields) < 3 || fields[0] != "default" { - return "" - } - - ip := net.ParseIP(fields[2]) - if ip == nil { - return "" - } - - return fields[2] -} - -// Set a static IP for network interface -// Supports: Raspbian. -func setStaticIP(ifaceName string) error { - ip := getFullIP(ifaceName) - if len(ip) == 0 { - return errors.New("Can't get IP address") - } - - ip4, _, err := net.ParseCIDR(ip) - if err != nil { - return err - } - gatewayIP := getGatewayIP(ifaceName) - add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String()) - - body, err := ioutil.ReadFile("/etc/dhcpcd.conf") - if err != nil { - return err - } - - body = append(body, []byte(add)...) - err = file.SafeWrite("/etc/dhcpcd.conf", body) - if err != nil { - return err - } - - return nil -} - -// for dhcpcd.conf -func setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, dnsIP string) string { - var body []byte - - add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n", - ifaceName, ip) - body = append(body, []byte(add)...) - - if len(gatewayIP) != 0 { - add = fmt.Sprintf("static routers=%s\n", - gatewayIP) - body = append(body, []byte(add)...) - } - - add = fmt.Sprintf("static domain_name_servers=%s\n\n", - dnsIP) - body = append(body, []byte(add)...) - - return string(body) -} - // checkPortAvailable is not a cheap test to see if the port is bindable, because it's actually doing the bind momentarily func checkPortAvailable(host string, port int) error { ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port))) if err != nil { return err } - ln.Close() + _ = ln.Close() // It seems that net.Listener.Close() doesn't close file descriptors right away. // We wait for some time and hope that this fd will be closed. @@ -283,7 +110,7 @@ func checkPacketPortAvailable(host string, port int) error { if err != nil { return err } - ln.Close() + _ = ln.Close() // It seems that net.Listener.Close() doesn't close file descriptors right away. // We wait for some time and hope that this fd will be closed.