"Fossies" - the Fresh Open Source Software Archive 
Member "AdGuardHome-0.104.3/internal/dhcpd/nclient4/client_test.go" (19 Nov 2020, 9872 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 "client_test.go":
0.104.1_vs_0.104.3.
1 // Copyright 2018 the u-root Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // +build linux
6 // github.com/hugelgupf/socketpair is Linux-only
7 // +build go1.12
8
9 package nclient4
10
11 import (
12 "bytes"
13 "context"
14 "fmt"
15 "net"
16 "sync"
17 "testing"
18 "time"
19
20 "github.com/AdguardTeam/AdGuardHome/internal/testutil"
21 "github.com/hugelgupf/socketpair"
22 "github.com/insomniacslk/dhcp/dhcpv4"
23 "github.com/insomniacslk/dhcp/dhcpv4/server4"
24 )
25
26 func TestMain(m *testing.M) {
27 testutil.DiscardLogOutput(m)
28 }
29
30 type handler struct {
31 mu sync.Mutex
32 received []*dhcpv4.DHCPv4
33
34 // Each received packet can have more than one response (in theory,
35 // from different servers sending different Advertise, for example).
36 responses [][]*dhcpv4.DHCPv4
37 }
38
39 func (h *handler) handle(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
40 h.mu.Lock()
41 defer h.mu.Unlock()
42
43 h.received = append(h.received, m)
44
45 if len(h.responses) > 0 {
46 for _, resp := range h.responses[0] {
47 _, _ = conn.WriteTo(resp.ToBytes(), peer)
48 }
49 h.responses = h.responses[1:]
50 }
51 }
52
53 func serveAndClient(ctx context.Context, responses [][]*dhcpv4.DHCPv4, opts ...ClientOpt) (*Client, net.PacketConn) {
54 // Fake PacketConn connection.
55 clientRawConn, serverRawConn, err := socketpair.PacketSocketPair()
56 if err != nil {
57 panic(err)
58 }
59
60 clientConn := NewBroadcastUDPConn(clientRawConn, &net.UDPAddr{Port: ClientPort})
61 serverConn := NewBroadcastUDPConn(serverRawConn, &net.UDPAddr{Port: ServerPort})
62
63 o := []ClientOpt{WithRetry(1), WithTimeout(2 * time.Second)}
64 o = append(o, opts...)
65 mc, err := NewWithConn(clientConn, net.HardwareAddr{0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, o...)
66 if err != nil {
67 panic(err)
68 }
69
70 h := &handler{responses: responses}
71 s, err := server4.NewServer("", nil, h.handle, server4.WithConn(serverConn))
72 if err != nil {
73 panic(err)
74 }
75 go func() {
76 _ = s.Serve()
77 }()
78
79 return mc, serverConn
80 }
81
82 func ComparePacket(got *dhcpv4.DHCPv4, want *dhcpv4.DHCPv4) error {
83 if got == nil && got == want {
84 return nil
85 }
86 if (want == nil || got == nil) && (got != want) {
87 return fmt.Errorf("packet got %v, want %v", got, want)
88 }
89 if !bytes.Equal(got.ToBytes(), want.ToBytes()) {
90 return fmt.Errorf("packet got %v, want %v", got, want)
91 }
92 return nil
93 }
94
95 func pktsExpected(got []*dhcpv4.DHCPv4, want []*dhcpv4.DHCPv4) error {
96 if len(got) != len(want) {
97 return fmt.Errorf("got %d packets, want %d packets", len(got), len(want))
98 }
99
100 for i := range got {
101 if err := ComparePacket(got[i], want[i]); err != nil {
102 return err
103 }
104 }
105 return nil
106 }
107
108 func newPacketWeirdHWAddr(op dhcpv4.OpcodeType, xid dhcpv4.TransactionID) *dhcpv4.DHCPv4 {
109 p, err := dhcpv4.New()
110 if err != nil {
111 panic(fmt.Sprintf("newpacket: %v", err))
112 }
113 p.OpCode = op
114 p.TransactionID = xid
115 p.ClientHWAddr = net.HardwareAddr{0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 1, 2, 3, 4, 5, 6}
116 return p
117 }
118
119 func newPacket(op dhcpv4.OpcodeType, xid dhcpv4.TransactionID) *dhcpv4.DHCPv4 {
120 p, err := dhcpv4.New()
121 if err != nil {
122 panic(fmt.Sprintf("newpacket: %v", err))
123 }
124 p.OpCode = op
125 p.TransactionID = xid
126 p.ClientHWAddr = net.HardwareAddr{0xa, 0xb, 0xc, 0xd, 0xe, 0xf}
127 return p
128 }
129
130 func withBufferCap(n int) ClientOpt {
131 return func(c *Client) (err error) {
132 c.bufferCap = n
133 return
134 }
135 }
136
137 func TestSendAndRead(t *testing.T) {
138 for _, tt := range []struct {
139 desc string
140 send *dhcpv4.DHCPv4
141 server []*dhcpv4.DHCPv4
142
143 // If want is nil, we assume server[0] contains what is wanted.
144 want *dhcpv4.DHCPv4
145 wantErr error
146 }{
147 {
148 desc: "two response packets",
149 send: newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
150 server: []*dhcpv4.DHCPv4{
151 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
152 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
153 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
154 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
155 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
156 },
157 want: newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
158 },
159 {
160 desc: "one response packet",
161 send: newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
162 server: []*dhcpv4.DHCPv4{
163 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
164 },
165 want: newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
166 },
167 {
168 desc: "one response packet, one invalid XID, one invalid opcode, one invalid hwaddr",
169 send: newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
170 server: []*dhcpv4.DHCPv4{
171 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x77, 0x33, 0x33, 0x33}),
172 newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
173 newPacketWeirdHWAddr(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
174 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
175 },
176 want: newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
177 },
178 {
179 desc: "discard wrong XID",
180 send: newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
181 server: []*dhcpv4.DHCPv4{
182 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0, 0, 0, 0}),
183 },
184 want: nil, // Explicitly empty.
185 wantErr: ErrNoResponse,
186 },
187 {
188 desc: "no response, timeout",
189 send: newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
190 wantErr: ErrNoResponse,
191 },
192 } {
193 t.Run(tt.desc, func(t *testing.T) {
194 // Both server and client only get 2 seconds.
195 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
196 defer cancel()
197
198 mc, _ := serveAndClient(ctx, [][]*dhcpv4.DHCPv4{tt.server},
199 // Use an unbuffered channel to make sure we
200 // have no deadlocks.
201 withBufferCap(0))
202 defer mc.Close()
203
204 rcvd, err := mc.SendAndRead(context.Background(), DefaultServers, tt.send, nil)
205 if err != tt.wantErr {
206 t.Error(err)
207 }
208
209 if err := ComparePacket(rcvd, tt.want); err != nil {
210 t.Errorf("got unexpected packets: %v", err)
211 }
212 })
213 }
214 }
215
216 func TestParallelSendAndRead(t *testing.T) {
217 pkt := newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33})
218
219 // Both the server and client only get 2 seconds.
220 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
221 defer cancel()
222
223 mc, _ := serveAndClient(ctx, [][]*dhcpv4.DHCPv4{},
224 WithTimeout(10*time.Second),
225 // Use an unbuffered channel to make sure nothing blocks.
226 withBufferCap(0))
227 defer mc.Close()
228
229 var wg sync.WaitGroup
230
231 wg.Add(1)
232 go func() {
233 defer wg.Done()
234 if _, err := mc.SendAndRead(context.Background(), DefaultServers, pkt, nil); err != ErrNoResponse {
235 t.Errorf("SendAndRead(%v) = %v, want %v", pkt, err, ErrNoResponse)
236 }
237 }()
238
239 wg.Add(1)
240 go func() {
241 defer wg.Done()
242
243 time.Sleep(4 * time.Second)
244
245 if err := mc.Close(); err != nil {
246 t.Errorf("closing failed: %v", err)
247 }
248 }()
249
250 wg.Wait()
251 }
252
253 func TestReuseXID(t *testing.T) {
254 pkt := newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33})
255
256 // Both the server and client only get 2 seconds.
257 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
258 defer cancel()
259
260 mc, _ := serveAndClient(ctx, [][]*dhcpv4.DHCPv4{})
261 defer mc.Close()
262
263 if _, err := mc.SendAndRead(context.Background(), DefaultServers, pkt, nil); err != ErrNoResponse {
264 t.Errorf("SendAndRead(%v) = %v, want %v", pkt, err, ErrNoResponse)
265 }
266
267 if _, err := mc.SendAndRead(context.Background(), DefaultServers, pkt, nil); err != ErrNoResponse {
268 t.Errorf("SendAndRead(%v) = %v, want %v", pkt, err, ErrNoResponse)
269 }
270 }
271
272 func TestSimpleSendAndReadDiscardGarbage(t *testing.T) {
273 pkt := newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33})
274
275 responses := newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33})
276
277 // Both the server and client only get 2 seconds.
278 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
279 defer cancel()
280
281 mc, udpConn := serveAndClient(ctx, [][]*dhcpv4.DHCPv4{{responses}})
282 defer mc.Close()
283
284 // Too short for valid DHCPv4 packet.
285 _, _ = udpConn.WriteTo([]byte{0x01}, nil)
286 _, _ = udpConn.WriteTo([]byte{0x01, 0x2}, nil)
287
288 rcvd, err := mc.SendAndRead(ctx, DefaultServers, pkt, nil)
289 if err != nil {
290 t.Errorf("SendAndRead(%v) = %v, want nil", pkt, err)
291 }
292
293 if err := ComparePacket(rcvd, responses); err != nil {
294 t.Errorf("got unexpected packets: %v", err)
295 }
296 }
297
298 func TestMultipleSendAndRead(t *testing.T) {
299 for _, tt := range []struct {
300 desc string
301 send []*dhcpv4.DHCPv4
302 server [][]*dhcpv4.DHCPv4
303 wantErr []error
304 }{
305 {
306 desc: "two requests, two responses",
307 send: []*dhcpv4.DHCPv4{
308 newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x33, 0x33, 0x33, 0x33}),
309 newPacket(dhcpv4.OpcodeBootRequest, [4]byte{0x44, 0x44, 0x44, 0x44}),
310 },
311 server: [][]*dhcpv4.DHCPv4{
312 []*dhcpv4.DHCPv4{ // Response for first packet.
313 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x33, 0x33, 0x33, 0x33}),
314 },
315 []*dhcpv4.DHCPv4{ // Response for second packet.
316 newPacket(dhcpv4.OpcodeBootReply, [4]byte{0x44, 0x44, 0x44, 0x44}),
317 },
318 },
319 wantErr: []error{
320 nil,
321 nil,
322 },
323 },
324 } {
325 // Both server and client only get 2 seconds.
326 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
327 defer cancel()
328
329 mc, _ := serveAndClient(ctx, tt.server)
330 defer mc.Close()
331
332 for i, send := range tt.send {
333 ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
334 defer cancel()
335 rcvd, err := mc.SendAndRead(ctx, DefaultServers, send, nil)
336
337 if wantErr := tt.wantErr[i]; err != wantErr {
338 t.Errorf("SendAndReadOne(%v): got %v, want %v", send, err, wantErr)
339 }
340 if err := pktsExpected([]*dhcpv4.DHCPv4{rcvd}, tt.server[i]); err != nil {
341 t.Errorf("got unexpected packets: %v", err)
342 }
343 }
344 }
345 }