github.com/jimmyx0x/go-ethereum@v1.10.28/p2p/discover/v4_udp_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discover 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 crand "crypto/rand" 23 "encoding/binary" 24 "errors" 25 "fmt" 26 "io" 27 "math/rand" 28 "net" 29 "reflect" 30 "sync" 31 "testing" 32 "time" 33 34 "github.com/ethereum/go-ethereum/internal/testlog" 35 "github.com/ethereum/go-ethereum/log" 36 "github.com/ethereum/go-ethereum/p2p/discover/v4wire" 37 "github.com/ethereum/go-ethereum/p2p/enode" 38 "github.com/ethereum/go-ethereum/p2p/enr" 39 ) 40 41 // shared test variables 42 var ( 43 futureExp = uint64(time.Now().Add(10 * time.Hour).Unix()) 44 testTarget = v4wire.Pubkey{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} 45 testRemote = v4wire.Endpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2} 46 testLocalAnnounced = v4wire.Endpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4} 47 testLocal = v4wire.Endpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6} 48 ) 49 50 type udpTest struct { 51 t *testing.T 52 pipe *dgramPipe 53 table *Table 54 db *enode.DB 55 udp *UDPv4 56 sent [][]byte 57 localkey, remotekey *ecdsa.PrivateKey 58 remoteaddr *net.UDPAddr 59 } 60 61 func newUDPTest(t *testing.T) *udpTest { 62 test := &udpTest{ 63 t: t, 64 pipe: newpipe(), 65 localkey: newkey(), 66 remotekey: newkey(), 67 remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, 68 } 69 70 test.db, _ = enode.OpenDB("") 71 ln := enode.NewLocalNode(test.db, test.localkey) 72 test.udp, _ = ListenV4(test.pipe, ln, Config{ 73 PrivateKey: test.localkey, 74 Log: testlog.Logger(t, log.LvlTrace), 75 }) 76 test.table = test.udp.tab 77 // Wait for initial refresh so the table doesn't send unexpected findnode. 78 <-test.table.initDone 79 return test 80 } 81 82 func (test *udpTest) close() { 83 test.udp.Close() 84 test.db.Close() 85 } 86 87 // handles a packet as if it had been sent to the transport. 88 func (test *udpTest) packetIn(wantError error, data v4wire.Packet) { 89 test.t.Helper() 90 91 test.packetInFrom(wantError, test.remotekey, test.remoteaddr, data) 92 } 93 94 // handles a packet as if it had been sent to the transport by the key/endpoint. 95 func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *net.UDPAddr, data v4wire.Packet) { 96 test.t.Helper() 97 98 enc, _, err := v4wire.Encode(key, data) 99 if err != nil { 100 test.t.Errorf("%s encode error: %v", data.Name(), err) 101 } 102 test.sent = append(test.sent, enc) 103 if err = test.udp.handlePacket(addr, enc); err != wantError { 104 test.t.Errorf("error mismatch: got %q, want %q", err, wantError) 105 } 106 } 107 108 // waits for a packet to be sent by the transport. 109 // validate should have type func(X, *net.UDPAddr, []byte), where X is a packet type. 110 func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) { 111 test.t.Helper() 112 113 dgram, err := test.pipe.receive() 114 if err == errClosed { 115 return true 116 } else if err != nil { 117 test.t.Error("packet receive error:", err) 118 return false 119 } 120 p, _, hash, err := v4wire.Decode(dgram.data) 121 if err != nil { 122 test.t.Errorf("sent packet decode error: %v", err) 123 return false 124 } 125 fn := reflect.ValueOf(validate) 126 exptype := fn.Type().In(0) 127 if !reflect.TypeOf(p).AssignableTo(exptype) { 128 test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) 129 return false 130 } 131 fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(hash)}) 132 return false 133 } 134 135 func TestUDPv4_packetErrors(t *testing.T) { 136 test := newUDPTest(t) 137 defer test.close() 138 139 test.packetIn(errExpired, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4}) 140 test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: []byte{}, Expiration: futureExp}) 141 test.packetIn(errUnknownNode, &v4wire.Findnode{Expiration: futureExp}) 142 test.packetIn(errUnsolicitedReply, &v4wire.Neighbors{Expiration: futureExp}) 143 } 144 145 func TestUDPv4_pingTimeout(t *testing.T) { 146 t.Parallel() 147 test := newUDPTest(t) 148 defer test.close() 149 150 key := newkey() 151 toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} 152 node := enode.NewV4(&key.PublicKey, toaddr.IP, 0, toaddr.Port) 153 if _, err := test.udp.ping(node); err != errTimeout { 154 t.Error("expected timeout error, got", err) 155 } 156 } 157 158 type testPacket byte 159 160 func (req testPacket) Kind() byte { return byte(req) } 161 func (req testPacket) Name() string { return "" } 162 163 func TestUDPv4_responseTimeouts(t *testing.T) { 164 t.Parallel() 165 test := newUDPTest(t) 166 defer test.close() 167 168 rand.Seed(time.Now().UnixNano()) 169 randomDuration := func(max time.Duration) time.Duration { 170 return time.Duration(rand.Int63n(int64(max))) 171 } 172 173 var ( 174 nReqs = 200 175 nTimeouts = 0 // number of requests with ptype > 128 176 nilErr = make(chan error, nReqs) // for requests that get a reply 177 timeoutErr = make(chan error, nReqs) // for requests that time out 178 ) 179 for i := 0; i < nReqs; i++ { 180 // Create a matcher for a random request in udp.loop. Requests 181 // with ptype <= 128 will not get a reply and should time out. 182 // For all other requests, a reply is scheduled to arrive 183 // within the timeout window. 184 p := &replyMatcher{ 185 ptype: byte(rand.Intn(255)), 186 callback: func(v4wire.Packet) (bool, bool) { return true, true }, 187 } 188 binary.BigEndian.PutUint64(p.from[:], uint64(i)) 189 if p.ptype <= 128 { 190 p.errc = timeoutErr 191 test.udp.addReplyMatcher <- p 192 nTimeouts++ 193 } else { 194 p.errc = nilErr 195 test.udp.addReplyMatcher <- p 196 time.AfterFunc(randomDuration(60*time.Millisecond), func() { 197 if !test.udp.handleReply(p.from, p.ip, testPacket(p.ptype)) { 198 t.Logf("not matched: %v", p) 199 } 200 }) 201 } 202 time.Sleep(randomDuration(30 * time.Millisecond)) 203 } 204 205 // Check that all timeouts were delivered and that the rest got nil errors. 206 // The replies must be delivered. 207 var ( 208 recvDeadline = time.After(20 * time.Second) 209 nTimeoutsRecv, nNil = 0, 0 210 ) 211 for i := 0; i < nReqs; i++ { 212 select { 213 case err := <-timeoutErr: 214 if err != errTimeout { 215 t.Fatalf("got non-timeout error on timeoutErr %d: %v", i, err) 216 } 217 nTimeoutsRecv++ 218 case err := <-nilErr: 219 if err != nil { 220 t.Fatalf("got non-nil error on nilErr %d: %v", i, err) 221 } 222 nNil++ 223 case <-recvDeadline: 224 t.Fatalf("exceeded recv deadline") 225 } 226 } 227 if nTimeoutsRecv != nTimeouts { 228 t.Errorf("wrong number of timeout errors received: got %d, want %d", nTimeoutsRecv, nTimeouts) 229 } 230 if nNil != nReqs-nTimeouts { 231 t.Errorf("wrong number of successful replies: got %d, want %d", nNil, nReqs-nTimeouts) 232 } 233 } 234 235 func TestUDPv4_findnodeTimeout(t *testing.T) { 236 t.Parallel() 237 test := newUDPTest(t) 238 defer test.close() 239 240 toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} 241 toid := enode.ID{1, 2, 3, 4} 242 target := v4wire.Pubkey{4, 5, 6, 7} 243 result, err := test.udp.findnode(toid, toaddr, target) 244 if err != errTimeout { 245 t.Error("expected timeout error, got", err) 246 } 247 if len(result) > 0 { 248 t.Error("expected empty result, got", result) 249 } 250 } 251 252 func TestUDPv4_findnode(t *testing.T) { 253 test := newUDPTest(t) 254 defer test.close() 255 256 // put a few nodes into the table. their exact 257 // distribution shouldn't matter much, although we need to 258 // take care not to overflow any bucket. 259 nodes := &nodesByDistance{target: testTarget.ID()} 260 live := make(map[enode.ID]bool) 261 numCandidates := 2 * bucketSize 262 for i := 0; i < numCandidates; i++ { 263 key := newkey() 264 ip := net.IP{10, 13, 0, byte(i)} 265 n := wrapNode(enode.NewV4(&key.PublicKey, ip, 0, 2000)) 266 // Ensure half of table content isn't verified live yet. 267 if i > numCandidates/2 { 268 n.livenessChecks = 1 269 live[n.ID()] = true 270 } 271 nodes.push(n, numCandidates) 272 } 273 fillTable(test.table, nodes.entries) 274 275 // ensure there's a bond with the test node, 276 // findnode won't be accepted otherwise. 277 remoteID := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID() 278 test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now()) 279 280 // check that closest neighbors are returned. 281 expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true) 282 test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp}) 283 waitNeighbors := func(want []*node) { 284 test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) { 285 if len(p.Nodes) != len(want) { 286 t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize) 287 return 288 } 289 for i, n := range p.Nodes { 290 if n.ID.ID() != want[i].ID() { 291 t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, n, expected.entries[i]) 292 } 293 if !live[n.ID.ID()] { 294 t.Errorf("result includes dead node %v", n.ID.ID()) 295 } 296 } 297 }) 298 } 299 // Receive replies. 300 want := expected.entries 301 if len(want) > v4wire.MaxNeighbors { 302 waitNeighbors(want[:v4wire.MaxNeighbors]) 303 want = want[v4wire.MaxNeighbors:] 304 } 305 waitNeighbors(want) 306 } 307 308 func TestUDPv4_findnodeMultiReply(t *testing.T) { 309 test := newUDPTest(t) 310 defer test.close() 311 312 rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey) 313 test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.IP, time.Now()) 314 315 // queue a pending findnode request 316 resultc, errc := make(chan []*node, 1), make(chan error, 1) 317 go func() { 318 rid := encodePubkey(&test.remotekey.PublicKey).id() 319 ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget) 320 if err != nil && len(ns) == 0 { 321 errc <- err 322 } else { 323 resultc <- ns 324 } 325 }() 326 327 // wait for the findnode to be sent. 328 // after it is sent, the transport is waiting for a reply 329 test.waitPacketOut(func(p *v4wire.Findnode, to *net.UDPAddr, hash []byte) { 330 if p.Target != testTarget { 331 t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) 332 } 333 }) 334 335 // send the reply as two packets. 336 list := []*node{ 337 wrapNode(enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")), 338 wrapNode(enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")), 339 wrapNode(enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")), 340 wrapNode(enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")), 341 } 342 rpclist := make([]v4wire.Node, len(list)) 343 for i := range list { 344 rpclist[i] = nodeToRPC(list[i]) 345 } 346 test.packetIn(nil, &v4wire.Neighbors{Expiration: futureExp, Nodes: rpclist[:2]}) 347 test.packetIn(nil, &v4wire.Neighbors{Expiration: futureExp, Nodes: rpclist[2:]}) 348 349 // check that the sent neighbors are all returned by findnode 350 select { 351 case result := <-resultc: 352 want := append(list[:2], list[3:]...) 353 if !reflect.DeepEqual(result, want) { 354 t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, want) 355 } 356 case err := <-errc: 357 t.Errorf("findnode error: %v", err) 358 case <-time.After(5 * time.Second): 359 t.Error("findnode did not return within 5 seconds") 360 } 361 } 362 363 // This test checks that reply matching of pong verifies the ping hash. 364 func TestUDPv4_pingMatch(t *testing.T) { 365 test := newUDPTest(t) 366 defer test.close() 367 368 randToken := make([]byte, 32) 369 crand.Read(randToken) 370 371 test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) 372 test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) 373 test.waitPacketOut(func(*v4wire.Ping, *net.UDPAddr, []byte) {}) 374 test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp}) 375 } 376 377 // This test checks that reply matching of pong verifies the sender IP address. 378 func TestUDPv4_pingMatchIP(t *testing.T) { 379 test := newUDPTest(t) 380 defer test.close() 381 382 test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) 383 test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) 384 385 test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { 386 wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 1, 2}, Port: 30000} 387 test.packetInFrom(errUnsolicitedReply, test.remotekey, wrongAddr, &v4wire.Pong{ 388 ReplyTok: hash, 389 To: testLocalAnnounced, 390 Expiration: futureExp, 391 }) 392 }) 393 } 394 395 func TestUDPv4_successfulPing(t *testing.T) { 396 test := newUDPTest(t) 397 added := make(chan *node, 1) 398 test.table.nodeAddedHook = func(n *node) { added <- n } 399 defer test.close() 400 401 // The remote side sends a ping packet to initiate the exchange. 402 go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) 403 404 // The ping is replied to. 405 test.waitPacketOut(func(p *v4wire.Pong, to *net.UDPAddr, hash []byte) { 406 pinghash := test.sent[0][:32] 407 if !bytes.Equal(p.ReplyTok, pinghash) { 408 t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) 409 } 410 wantTo := v4wire.Endpoint{ 411 // The mirrored UDP address is the UDP packet sender 412 IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), 413 // The mirrored TCP port is the one from the ping packet 414 TCP: testRemote.TCP, 415 } 416 if !reflect.DeepEqual(p.To, wantTo) { 417 t.Errorf("got pong.To %v, want %v", p.To, wantTo) 418 } 419 }) 420 421 // Remote is unknown, the table pings back. 422 test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { 423 if !reflect.DeepEqual(p.From, test.udp.ourEndpoint()) { 424 t.Errorf("got ping.From %#v, want %#v", p.From, test.udp.ourEndpoint()) 425 } 426 wantTo := v4wire.Endpoint{ 427 // The mirrored UDP address is the UDP packet sender. 428 IP: test.remoteaddr.IP, 429 UDP: uint16(test.remoteaddr.Port), 430 TCP: 0, 431 } 432 if !reflect.DeepEqual(p.To, wantTo) { 433 t.Errorf("got ping.To %v, want %v", p.To, wantTo) 434 } 435 test.packetIn(nil, &v4wire.Pong{ReplyTok: hash, Expiration: futureExp}) 436 }) 437 438 // The node should be added to the table shortly after getting the 439 // pong packet. 440 select { 441 case n := <-added: 442 rid := encodePubkey(&test.remotekey.PublicKey).id() 443 if n.ID() != rid { 444 t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid) 445 } 446 if !n.IP().Equal(test.remoteaddr.IP) { 447 t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP) 448 } 449 if n.UDP() != test.remoteaddr.Port { 450 t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port) 451 } 452 if n.TCP() != int(testRemote.TCP) { 453 t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP) 454 } 455 case <-time.After(2 * time.Second): 456 t.Errorf("node was not added within 2 seconds") 457 } 458 } 459 460 // This test checks that EIP-868 requests work. 461 func TestUDPv4_EIP868(t *testing.T) { 462 test := newUDPTest(t) 463 defer test.close() 464 465 test.udp.localNode.Set(enr.WithEntry("foo", "bar")) 466 wantNode := test.udp.localNode.Node() 467 468 // ENR requests aren't allowed before endpoint proof. 469 test.packetIn(errUnknownNode, &v4wire.ENRRequest{Expiration: futureExp}) 470 471 // Perform endpoint proof and check for sequence number in packet tail. 472 test.packetIn(nil, &v4wire.Ping{Expiration: futureExp}) 473 test.waitPacketOut(func(p *v4wire.Pong, addr *net.UDPAddr, hash []byte) { 474 if p.ENRSeq != wantNode.Seq() { 475 t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq()) 476 } 477 }) 478 test.waitPacketOut(func(p *v4wire.Ping, addr *net.UDPAddr, hash []byte) { 479 if p.ENRSeq != wantNode.Seq() { 480 t.Errorf("wrong sequence number in ping: %d, want %d", p.ENRSeq, wantNode.Seq()) 481 } 482 test.packetIn(nil, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash}) 483 }) 484 485 // Request should work now. 486 test.packetIn(nil, &v4wire.ENRRequest{Expiration: futureExp}) 487 test.waitPacketOut(func(p *v4wire.ENRResponse, addr *net.UDPAddr, hash []byte) { 488 n, err := enode.New(enode.ValidSchemes, &p.Record) 489 if err != nil { 490 t.Fatalf("invalid record: %v", err) 491 } 492 if !reflect.DeepEqual(n, wantNode) { 493 t.Fatalf("wrong node in ENRResponse: %v", n) 494 } 495 }) 496 } 497 498 // This test verifies that a small network of nodes can boot up into a healthy state. 499 func TestUDPv4_smallNetConvergence(t *testing.T) { 500 t.Parallel() 501 502 // Start the network. 503 nodes := make([]*UDPv4, 4) 504 for i := range nodes { 505 var cfg Config 506 if i > 0 { 507 bn := nodes[0].Self() 508 cfg.Bootnodes = []*enode.Node{bn} 509 } 510 nodes[i] = startLocalhostV4(t, cfg) 511 defer nodes[i].Close() 512 } 513 514 // Run through the iterator on all nodes until 515 // they have all found each other. 516 status := make(chan error, len(nodes)) 517 for i := range nodes { 518 node := nodes[i] 519 go func() { 520 found := make(map[enode.ID]bool, len(nodes)) 521 it := node.RandomNodes() 522 for it.Next() { 523 found[it.Node().ID()] = true 524 if len(found) == len(nodes) { 525 status <- nil 526 return 527 } 528 } 529 status <- fmt.Errorf("node %s didn't find all nodes", node.Self().ID().TerminalString()) 530 }() 531 } 532 533 // Wait for all status reports. 534 timeout := time.NewTimer(30 * time.Second) 535 defer timeout.Stop() 536 for received := 0; received < len(nodes); { 537 select { 538 case <-timeout.C: 539 for _, node := range nodes { 540 node.Close() 541 } 542 case err := <-status: 543 received++ 544 if err != nil { 545 t.Error("ERROR:", err) 546 return 547 } 548 } 549 } 550 } 551 552 func startLocalhostV4(t *testing.T, cfg Config) *UDPv4 { 553 t.Helper() 554 555 cfg.PrivateKey = newkey() 556 db, _ := enode.OpenDB("") 557 ln := enode.NewLocalNode(db, cfg.PrivateKey) 558 559 // Prefix logs with node ID. 560 lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString()) 561 lfmt := log.TerminalFormat(false) 562 cfg.Log = testlog.Logger(t, log.LvlTrace) 563 cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error { 564 t.Logf("%s %s", lprefix, lfmt.Format(r)) 565 return nil 566 })) 567 568 // Listen. 569 socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}}) 570 if err != nil { 571 t.Fatal(err) 572 } 573 realaddr := socket.LocalAddr().(*net.UDPAddr) 574 ln.SetStaticIP(realaddr.IP) 575 ln.SetFallbackUDP(realaddr.Port) 576 udp, err := ListenV4(socket, ln, cfg) 577 if err != nil { 578 t.Fatal(err) 579 } 580 return udp 581 } 582 583 // dgramPipe is a fake UDP socket. It queues all sent datagrams. 584 type dgramPipe struct { 585 mu *sync.Mutex 586 cond *sync.Cond 587 closing chan struct{} 588 closed bool 589 queue []dgram 590 } 591 592 type dgram struct { 593 to net.UDPAddr 594 data []byte 595 } 596 597 func newpipe() *dgramPipe { 598 mu := new(sync.Mutex) 599 return &dgramPipe{ 600 closing: make(chan struct{}), 601 cond: &sync.Cond{L: mu}, 602 mu: mu, 603 } 604 } 605 606 // WriteToUDP queues a datagram. 607 func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { 608 msg := make([]byte, len(b)) 609 copy(msg, b) 610 c.mu.Lock() 611 defer c.mu.Unlock() 612 if c.closed { 613 return 0, errors.New("closed") 614 } 615 c.queue = append(c.queue, dgram{*to, b}) 616 c.cond.Signal() 617 return len(b), nil 618 } 619 620 // ReadFromUDP just hangs until the pipe is closed. 621 func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { 622 <-c.closing 623 return 0, nil, io.EOF 624 } 625 626 func (c *dgramPipe) Close() error { 627 c.mu.Lock() 628 defer c.mu.Unlock() 629 if !c.closed { 630 close(c.closing) 631 c.closed = true 632 } 633 c.cond.Broadcast() 634 return nil 635 } 636 637 func (c *dgramPipe) LocalAddr() net.Addr { 638 return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)} 639 } 640 641 func (c *dgramPipe) receive() (dgram, error) { 642 c.mu.Lock() 643 defer c.mu.Unlock() 644 645 var timedOut bool 646 timer := time.AfterFunc(3*time.Second, func() { 647 c.mu.Lock() 648 timedOut = true 649 c.mu.Unlock() 650 c.cond.Broadcast() 651 }) 652 defer timer.Stop() 653 654 for len(c.queue) == 0 && !c.closed && !timedOut { 655 c.cond.Wait() 656 } 657 if c.closed { 658 return dgram{}, errClosed 659 } 660 if timedOut { 661 return dgram{}, errTimeout 662 } 663 p := c.queue[0] 664 copy(c.queue, c.queue[1:]) 665 c.queue = c.queue[:len(c.queue)-1] 666 return p, nil 667 }