"Fossies" - the Fresh Open Source Software Archive

Member "AdGuardHome-0.104.3/internal/dhcpd/check_other_dhcp.go" (19 Nov 2020, 7755 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 "check_other_dhcp.go": 0.104.1_vs_0.104.3.

    1 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
    2 
    3 package dhcpd
    4 
    5 import (
    6     "bytes"
    7     "fmt"
    8     "net"
    9     "os"
   10     "runtime"
   11     "time"
   12 
   13     "github.com/AdguardTeam/AdGuardHome/internal/dhcpd/nclient4"
   14     "github.com/AdguardTeam/golibs/log"
   15     "github.com/insomniacslk/dhcp/dhcpv4"
   16     "github.com/insomniacslk/dhcp/dhcpv6"
   17     "github.com/insomniacslk/dhcp/dhcpv6/nclient6"
   18     "github.com/insomniacslk/dhcp/iana"
   19 )
   20 
   21 // CheckIfOtherDHCPServersPresentV4 sends a DHCP request to the specified network interface,
   22 // and waits for a response for a period defined by defaultDiscoverTime
   23 func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
   24     iface, err := net.InterfaceByName(ifaceName)
   25     if err != nil {
   26         return false, fmt.Errorf("couldn't find interface by name %s: %w", ifaceName, err)
   27     }
   28 
   29     ifaceIPNet, err := ifaceIPv4Addrs(iface)
   30     if err != nil {
   31         return false, fmt.Errorf("getting ipv4 addrs for iface %s: %w", ifaceName, err)
   32     }
   33     if len(ifaceIPNet) == 0 {
   34         return false, fmt.Errorf("interface %s has no ipv4 addresses", ifaceName)
   35     }
   36 
   37     // TODO(a.garipov): Find out what this is about.  Perhaps this
   38     // information is outdated or at least incomplete.
   39     if runtime.GOOS == "darwin" {
   40         return false, fmt.Errorf("can't find DHCP server: not supported on macOS")
   41     }
   42 
   43     srcIP := ifaceIPNet[0]
   44     src := net.JoinHostPort(srcIP.String(), "68")
   45     dst := "255.255.255.255:67"
   46 
   47     hostname, _ := os.Hostname()
   48 
   49     req, err := dhcpv4.NewDiscovery(iface.HardwareAddr)
   50     if err != nil {
   51         return false, fmt.Errorf("dhcpv4.NewDiscovery: %w", err)
   52     }
   53     req.Options.Update(dhcpv4.OptClientIdentifier(iface.HardwareAddr))
   54     req.Options.Update(dhcpv4.OptHostName(hostname))
   55 
   56     // resolve 0.0.0.0:68
   57     udpAddr, err := net.ResolveUDPAddr("udp4", src)
   58     if err != nil {
   59         return false, fmt.Errorf("couldn't resolve UDP address %s: %w", src, err)
   60     }
   61 
   62     if !udpAddr.IP.To4().Equal(srcIP) {
   63         return false, fmt.Errorf("resolved UDP address is not %s: %w", src, err)
   64     }
   65 
   66     // resolve 255.255.255.255:67
   67     dstAddr, err := net.ResolveUDPAddr("udp4", dst)
   68     if err != nil {
   69         return false, fmt.Errorf("couldn't resolve UDP address %s: %w", dst, err)
   70     }
   71 
   72     // bind to 0.0.0.0:68
   73     log.Tracef("Listening to udp4 %+v", udpAddr)
   74     c, err := nclient4.NewRawUDPConn(ifaceName, 68)
   75     if err != nil {
   76         return false, fmt.Errorf("couldn't listen on :68: %w", err)
   77     }
   78     if c != nil {
   79         defer c.Close()
   80     }
   81 
   82     // send to 255.255.255.255:67
   83     _, err = c.WriteTo(req.ToBytes(), dstAddr)
   84     if err != nil {
   85         return false, fmt.Errorf("couldn't send a packet to %s: %w", dst, err)
   86     }
   87 
   88     for {
   89         ok, next, err := tryConn4(req, c, iface)
   90         if next {
   91             if err != nil {
   92                 log.Debug("dhcpv4: trying a connection: %s", err)
   93             }
   94 
   95             continue
   96         }
   97         if ok {
   98             return true, nil
   99         }
  100         if err != nil {
  101             return false, err
  102         }
  103     }
  104 }
  105 
  106 // TODO(a.garipov): Refactor further.  Inspect error handling, remove parameter
  107 // next, address the TODO, merge with tryConn6, etc.
  108 func tryConn4(req *dhcpv4.DHCPv4, c net.PacketConn, iface *net.Interface) (ok, next bool, err error) {
  109     // TODO: replicate dhclient's behavior of retrying several times with
  110     // progressively longer timeouts.
  111     log.Tracef("dhcpv4: waiting %v for an answer", defaultDiscoverTime)
  112 
  113     b := make([]byte, 1500)
  114     err = c.SetDeadline(time.Now().Add(defaultDiscoverTime))
  115     if err != nil {
  116         return false, false, fmt.Errorf("setting deadline: %w", err)
  117     }
  118 
  119     n, _, err := c.ReadFrom(b)
  120     if err != nil {
  121         if isTimeout(err) {
  122             log.Debug("dhcpv4: didn't receive dhcp response")
  123 
  124             return false, false, nil
  125         }
  126 
  127         return false, false, fmt.Errorf("receiving packet: %w", err)
  128     }
  129 
  130     log.Tracef("dhcpv4: received packet, %d bytes", n)
  131 
  132     response, err := dhcpv4.FromBytes(b[:n])
  133     if err != nil {
  134         log.Debug("dhcpv4: encoding: %s", err)
  135 
  136         return false, true, err
  137     }
  138 
  139     log.Debug("dhcpv4: received message from server: %s", response.Summary())
  140 
  141     if !(response.OpCode == dhcpv4.OpcodeBootReply &&
  142         response.HWType == iana.HWTypeEthernet &&
  143         bytes.Equal(response.ClientHWAddr, iface.HardwareAddr) &&
  144         bytes.Equal(response.TransactionID[:], req.TransactionID[:]) &&
  145         response.Options.Has(dhcpv4.OptionDHCPMessageType)) {
  146 
  147         log.Debug("dhcpv4: received message from server doesn't match our request")
  148 
  149         return false, true, nil
  150     }
  151 
  152     log.Tracef("dhcpv4: the packet is from an active dhcp server")
  153 
  154     return true, false, nil
  155 }
  156 
  157 // CheckIfOtherDHCPServersPresentV6 sends a DHCP request to the specified network interface,
  158 // and waits for a response for a period defined by defaultDiscoverTime
  159 func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
  160     iface, err := net.InterfaceByName(ifaceName)
  161     if err != nil {
  162         return false, fmt.Errorf("dhcpv6: net.InterfaceByName: %s: %w", ifaceName, err)
  163     }
  164 
  165     ifaceIPNet, err := ifaceIPv6Addrs(iface)
  166     if err != nil {
  167         return false, fmt.Errorf("getting ipv6 addrs for iface %s: %w", ifaceName, err)
  168     }
  169     if len(ifaceIPNet) == 0 {
  170         return false, fmt.Errorf("interface %s has no ipv6 addresses", ifaceName)
  171     }
  172 
  173     srcIP := ifaceIPNet[0]
  174     src := net.JoinHostPort(srcIP.String(), "546")
  175     dst := "[ff02::1:2]:547"
  176 
  177     req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
  178     if err != nil {
  179         return false, fmt.Errorf("dhcpv6: dhcpv6.NewSolicit: %w", err)
  180     }
  181 
  182     udpAddr, err := net.ResolveUDPAddr("udp6", src)
  183     if err != nil {
  184         return false, fmt.Errorf("dhcpv6: Couldn't resolve UDP address %s: %w", src, err)
  185     }
  186 
  187     if !udpAddr.IP.To16().Equal(srcIP) {
  188         return false, fmt.Errorf("dhcpv6: Resolved UDP address is not %s: %w", src, err)
  189     }
  190 
  191     dstAddr, err := net.ResolveUDPAddr("udp6", dst)
  192     if err != nil {
  193         return false, fmt.Errorf("dhcpv6: Couldn't resolve UDP address %s: %w", dst, err)
  194     }
  195 
  196     log.Debug("DHCPv6: Listening to udp6 %+v", udpAddr)
  197     c, err := nclient6.NewIPv6UDPConn(ifaceName, dhcpv6.DefaultClientPort)
  198     if err != nil {
  199         return false, fmt.Errorf("dhcpv6: Couldn't listen on :546: %w", err)
  200     }
  201     if c != nil {
  202         defer c.Close()
  203     }
  204 
  205     _, err = c.WriteTo(req.ToBytes(), dstAddr)
  206     if err != nil {
  207         return false, fmt.Errorf("dhcpv6: Couldn't send a packet to %s: %w", dst, err)
  208     }
  209 
  210     for {
  211         ok, next, err := tryConn6(req, c)
  212         if next {
  213             if err != nil {
  214                 log.Debug("dhcpv6: trying a connection: %s", err)
  215             }
  216 
  217             continue
  218         }
  219         if ok {
  220             return true, nil
  221         }
  222         if err != nil {
  223             return false, err
  224         }
  225     }
  226 }
  227 
  228 // TODO(a.garipov): See the comment on tryConn4.  Sigh…
  229 func tryConn6(req *dhcpv6.Message, c net.PacketConn) (ok, next bool, err error) {
  230     // TODO: replicate dhclient's behavior of retrying several times with
  231     // progressively longer timeouts.
  232     log.Tracef("dhcpv6: waiting %v for an answer", defaultDiscoverTime)
  233 
  234     b := make([]byte, 4096)
  235     err = c.SetDeadline(time.Now().Add(defaultDiscoverTime))
  236     if err != nil {
  237         return false, false, fmt.Errorf("setting deadline: %w", err)
  238     }
  239 
  240     n, _, err := c.ReadFrom(b)
  241     if err != nil {
  242         if isTimeout(err) {
  243             log.Debug("dhcpv6: didn't receive dhcp response")
  244 
  245             return false, false, nil
  246         }
  247 
  248         return false, false, fmt.Errorf("receiving packet: %w", err)
  249     }
  250 
  251     log.Tracef("dhcpv6: received packet, %d bytes", n)
  252 
  253     response, err := dhcpv6.FromBytes(b[:n])
  254     if err != nil {
  255         log.Debug("dhcpv6: encoding: %s", err)
  256 
  257         return false, true, err
  258     }
  259 
  260     log.Debug("dhcpv6: received message from server: %s", response.Summary())
  261 
  262     cid := req.Options.ClientID()
  263     msg, err := response.GetInnerMessage()
  264     if err != nil {
  265         log.Debug("dhcpv6: resp.GetInnerMessage(): %s", err)
  266 
  267         return false, true, err
  268     }
  269 
  270     rcid := msg.Options.ClientID()
  271     if !(response.Type() == dhcpv6.MessageTypeAdvertise &&
  272         msg.TransactionID == req.TransactionID &&
  273         rcid != nil &&
  274         cid.Equal(*rcid)) {
  275 
  276         log.Debug("dhcpv6: received message from server doesn't match our request")
  277 
  278         return false, true, nil
  279     }
  280 
  281     log.Tracef("dhcpv6: the packet is from an active dhcp server")
  282 
  283     return true, false, nil
  284 }