From b2b9a9aa81bc44234230ef6450300d5bed754145 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Mon, 29 Jan 2018 13:35:14 +0200 Subject: [PATCH] p2p: dont send DiscReason to disconnected peer when using net.Pipe --- p2p/rlpx.go | 10 ++++++-- p2p/rlpx_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/p2p/rlpx.go b/p2p/rlpx.go index 24037ecc13..e65a0b6047 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -108,8 +108,14 @@ func (t *rlpx) close(err error) { // Tell the remote end why we're disconnecting if possible. if t.rw != nil { if r, ok := err.(DiscReason); ok && r != DiscNetworkError { - t.fd.SetWriteDeadline(time.Now().Add(discWriteTimeout)) - SendItems(t.rw, discMsg, r) + // rlpx tries to send DiscReason to disconnected peer + // if the connection is net.Pipe (in-memory simulation) + // it hangs forever, since net.Pipe does not implement + // a write deadline. Because of this only try to send + // the disconnect reason message if there is no error. + if err := t.fd.SetWriteDeadline(time.Now().Add(discWriteTimeout)); err == nil { + SendItems(t.rw, discMsg, r) + } } } t.fd.Close() diff --git a/p2p/rlpx_test.go b/p2p/rlpx_test.go index f4cefa650b..8a378db122 100644 --- a/p2p/rlpx_test.go +++ b/p2p/rlpx_test.go @@ -156,14 +156,18 @@ func TestProtocolHandshake(t *testing.T) { node1 = &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey), IP: net.IP{5, 6, 7, 8}, TCP: 44} hs1 = &protoHandshake{Version: 3, ID: node1.ID, Caps: []Cap{{"c", 1}, {"d", 3}}} - fd0, fd1 = net.Pipe() - wg sync.WaitGroup + wg sync.WaitGroup ) + fd0, fd1, err := tcpPipe() + if err != nil { + t.Fatal(err) + } + wg.Add(2) go func() { defer wg.Done() - defer fd1.Close() + defer fd0.Close() rlpx := newRLPX(fd0) remid, err := rlpx.doEncHandshake(prv0, node1) if err != nil { @@ -597,3 +601,58 @@ func TestHandshakeForwardCompatibility(t *testing.T) { t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash) } } + +// tcpPipe creates an in process full duplex pipe based on a localhost TCP socket +func tcpPipe() (net.Conn, net.Conn, error) { + type result struct { + conn net.Conn + err error + } + + cl := make(chan result) + cd := make(chan result) + + start := make(chan net.Addr) + + go func(res chan result, start chan net.Addr) { + // resolve + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + res <- result{err: err} + return + } + // listen + l, err := net.ListenTCP("tcp", addr) + if err != nil { + res <- result{err: err} + return + } + start <- l.Addr() + c, err := l.AcceptTCP() + if err != nil { + res <- result{err: err} + return + } + res <- result{conn: c} + }(cl, start) + + go func(res chan result, start chan net.Addr) { + addr := <-start + c, err := net.DialTCP("tcp", nil, addr.(*net.TCPAddr)) + if err != nil { + res <- result{err: err} + return + } + res <- result{conn: c} + }(cd, start) + + a := <-cl + if a.err != nil { + return nil, nil, a.err + } + b := <-cd + if b.err != nil { + return nil, nil, b.err + } + return a.conn, b.conn, nil +}