AdGuardHome/util/network_utils.go
Andrey Meshkov 7a3eda02ce Fix #1069 install: check static ip
Squashed commit of the following:

commit 57466233cb
Merge: 2df5f281 867bf545
Author: Andrey Meshkov <am@adguard.com>
Date:   Thu Feb 13 18:39:15 2020 +0300

    Merge branch 'master' into 1069-install-static-ip

commit 2df5f281c4
Author: Andrey Meshkov <am@adguard.com>
Date:   Thu Feb 13 18:35:54 2020 +0300

    *: lang fix

commit b4649a6b27
Merge: c2785253 f61d5f0f
Author: Andrey Meshkov <am@adguard.com>
Date:   Thu Feb 13 16:47:30 2020 +0300

    *(home): fixed issues with setting static IP on Mac

commit c27852537d
Author: Andrey Meshkov <am@adguard.com>
Date:   Thu Feb 13 14:14:30 2020 +0300

    +(dhcpd): added static IP for MacOS

commit f61d5f0f85
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Thu Feb 13 14:13:35 2020 +0300

    + client: show confirm before setting static IP

commit 7afa16fbe7
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Thu Feb 13 13:51:52 2020 +0300

    - client: fix text

commit 019bff0851
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Thu Feb 13 13:49:16 2020 +0300

    - client: pass all params to the check_config request

commit 194bed72f5
Author: Andrey Meshkov <am@adguard.com>
Date:   Wed Feb 12 17:12:16 2020 +0300

    *: fix home_test

commit 9359f6b55f
Merge: ae299058 c5ca2a77
Author: Andrey Meshkov <am@adguard.com>
Date:   Wed Feb 12 15:54:54 2020 +0300

    Merge with master

commit ae2990582d
Author: Andrey Meshkov <am@adguard.com>
Date:   Wed Feb 12 15:53:36 2020 +0300

    *(global): refactoring - moved runtime properties to Context

commit d8d48c5386
Author: Andrey Meshkov <am@adguard.com>
Date:   Wed Feb 12 15:04:25 2020 +0300

    *(dhcpd): refactoring, use dhcpd/network_utils where possible

commit 8d039c572f
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Fri Feb 7 18:37:39 2020 +0300

    - client: fix button position

commit 26c47e59dd
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Fri Feb 7 18:08:56 2020 +0300

    - client: fix static ip description

commit cb12babc46
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Feb 7 17:08:39 2020 +0300

    *: lower log level for some commands

commit d9001ff848
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Feb 7 16:17:59 2020 +0300

    *(documentation): updated openapi

commit 1d213d53c8
Merge: 8406d7d2 80861860
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Feb 7 15:16:46 2020 +0300

    *: merge with master

commit 8406d7d288
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Fri Jan 31 16:52:22 2020 +0300

    - client: fix locales

commit fb476b0117
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Jan 31 13:29:03 2020 +0300

    linter

commit 84b5708e71
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Jan 31 13:27:53 2020 +0300

    linter

commit 143a86a28a
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Jan 31 13:26:47 2020 +0300

    linter

... and 7 more commits
2020-02-13 18:42:07 +03:00

195 lines
4.7 KiB
Go

package util
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"strconv"
"syscall"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/joomcode/errorx"
)
// NetInterface represents a list of network interfaces
type NetInterface struct {
Name string // Network interface name
MTU int // MTU
HardwareAddr string // Hardware address
Addresses []string // Array with the network interface addresses
Subnets []string // Array with CIDR addresses of this network interface
Flags string // Network interface flags (up, broadcast, etc)
}
// 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()
if err != nil {
return nil, errorx.Decorate(err, "Couldn't get interfaces")
}
if len(ifaces) == 0 {
return nil, errors.New("couldn't find any legible interface")
}
var netInterfaces []NetInterface
for _, iface := range ifaces {
addrs, e := iface.Addrs()
if e != nil {
return nil, errorx.Decorate(e, "Failed to get addresses for interface %s", iface.Name)
}
netIface := NetInterface{
Name: iface.Name,
MTU: iface.MTU,
HardwareAddr: iface.HardwareAddr.String(),
}
if iface.Flags != 0 {
netIface.Flags = iface.Flags.String()
}
// Collect network interface addresses
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
// not an IPNet, should not happen
return nil, fmt.Errorf("got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr)
}
// ignore link-local
if ipNet.IP.IsLinkLocalUnicast() {
continue
}
// ignore IPv6
if ipNet.IP.To4() == nil {
continue
}
netIface.Addresses = append(netIface.Addresses, ipNet.IP.String())
netIface.Subnets = append(netIface.Subnets, ipNet.String())
}
// Discard interfaces with no addresses
if len(netIface.Addresses) != 0 {
netInterfaces = append(netInterfaces, netIface)
}
}
return netInterfaces, nil
}
// Get interface name by its IP address.
func GetInterfaceByIP(ip string) string {
ifaces, err := GetValidNetInterfacesForWeb()
if err != nil {
return ""
}
for _, iface := range ifaces {
for _, addr := range iface.Addresses {
if ip == addr {
return iface.Name
}
}
}
return ""
}
// Get IP address with netmask for the specified interface
// Returns an empty string if it fails to find it
func GetSubnet(ifaceName string) string {
netIfaces, err := GetValidNetInterfacesForWeb()
if err != nil {
log.Error("Could not get network interfaces info: %v", err)
return ""
}
for _, netIface := range netIfaces {
if netIface.Name == ifaceName && len(netIface.Subnets) > 0 {
return netIface.Subnets[0]
}
}
return ""
}
// 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()
// 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.
time.Sleep(100 * time.Millisecond)
return nil
}
func CheckPacketPortAvailable(host string, port int) error {
ln, err := net.ListenPacket("udp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return err
}
_ = 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.
time.Sleep(100 * time.Millisecond)
return err
}
// check if error is "address already in use"
func ErrorIsAddrInUse(err error) bool {
errOpError, ok := err.(*net.OpError)
if !ok {
return false
}
errSyscallError, ok := errOpError.Err.(*os.SyscallError)
if !ok {
return false
}
errErrno, ok := errSyscallError.Err.(syscall.Errno)
if !ok {
return false
}
if runtime.GOOS == "windows" {
const WSAEADDRINUSE = 10048
return errErrno == WSAEADDRINUSE
}
return errErrno == syscall.EADDRINUSE
}