"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 }