Skip to content

Commit 1c850e1

Browse files
Timeout all Identify stream reads (#1032)
* fix: read timeouts on Identify protocols * fixed tests * review and go fmt
1 parent 577e752 commit 1c850e1

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

p2p/protocol/identify/id.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ const LibP2PVersion = "ipfs/0.1.0"
4646
// Deprecated: Set this with the UserAgent option.
4747
var ClientVersion = "github.com/libp2p/go-libp2p"
4848

49+
// StreamReadTimeout is the read timeout on all incoming Identify family streams.
50+
var StreamReadTimeout = 60 * time.Second
51+
4952
var (
5053
legacyIDSize = 2 * 1024 // 2k Bytes
5154
signedIDSize = 8 * 1024 // 8K
@@ -369,7 +372,8 @@ func (ids *IDService) identifyConn(c network.Conn, signal chan struct{}) {
369372
s.Reset()
370373
return
371374
}
372-
ids.handleIdentifyResponse(s)
375+
376+
err = ids.handleIdentifyResponse(s)
373377
}
374378

375379
func (ids *IDService) sendIdentifyResp(s network.Stream) {
@@ -408,7 +412,9 @@ func (ids *IDService) sendIdentifyResp(s network.Stream) {
408412
log.Debugf("%s sent message to %s %s", ID, c.RemotePeer(), c.RemoteMultiaddr())
409413
}
410414

411-
func (ids *IDService) handleIdentifyResponse(s network.Stream) {
415+
func (ids *IDService) handleIdentifyResponse(s network.Stream) error {
416+
_ = s.SetReadDeadline(time.Now().Add(StreamReadTimeout))
417+
412418
c := s.Conn()
413419

414420
r := protoio.NewDelimitedReader(s, signedIDSize)
@@ -417,14 +423,16 @@ func (ids *IDService) handleIdentifyResponse(s network.Stream) {
417423
if err := readAllIDMessages(r, mes); err != nil {
418424
log.Warning("error reading identify message: ", err)
419425
s.Reset()
420-
return
426+
return err
421427
}
422428

423429
defer s.Close()
424430

425431
log.Debugf("%s received message from %s %s", s.Protocol(), c.RemotePeer(), c.RemoteMultiaddr())
426432

427433
ids.consumeMessage(mes, c)
434+
435+
return nil
428436
}
429437

430438
func readAllIDMessages(r protoio.Reader, finalMsg proto.Message) error {

p2p/protocol/identify/id_delta.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/libp2p/go-libp2p-core/network"
66
"github.com/libp2p/go-libp2p-core/peer"
77
"github.com/libp2p/go-libp2p-core/protocol"
8+
"time"
89

910
pb "github.com/libp2p/go-libp2p/p2p/protocol/identify/pb"
1011

@@ -15,6 +16,8 @@ const IDDelta = "/p2p/id/delta/1.0.0"
1516

1617
// deltaHandler handles incoming delta updates from peers.
1718
func (ids *IDService) deltaHandler(s network.Stream) {
19+
_ = s.SetReadDeadline(time.Now().Add(StreamReadTimeout))
20+
1821
c := s.Conn()
1922

2023
r := protoio.NewDelimitedReader(s, 2048)

p2p/protocol/identify/id_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,3 +978,83 @@ func TestLargePushMessage(t *testing.T) {
978978
}, 5*time.Second, 500*time.Millisecond)
979979
testHasCertifiedAddrs(t, h2, h1p, h1.Addrs())
980980
}
981+
982+
func TestIdentifyResponseReadTimeout(t *testing.T) {
983+
ctx := context.Background()
984+
timeout := identify.StreamReadTimeout
985+
identify.StreamReadTimeout = 100 * time.Millisecond
986+
defer func() {
987+
identify.StreamReadTimeout = timeout
988+
}()
989+
990+
ctx, cancel := context.WithCancel(context.Background())
991+
defer cancel()
992+
993+
h1 := blhost.NewBlankHost(swarmt.GenSwarm(t, ctx))
994+
h2 := blhost.NewBlankHost(swarmt.GenSwarm(t, ctx))
995+
defer h1.Close()
996+
defer h2.Close()
997+
998+
h2p := h2.ID()
999+
ids1 := identify.NewIDService(h1)
1000+
ids2 := identify.NewIDService(h2)
1001+
defer ids1.Close()
1002+
defer ids2.Close()
1003+
// remote stream handler will just hang and not send back an identify response
1004+
h2.SetStreamHandler(identify.ID, func(s network.Stream) {
1005+
time.Sleep(100 * time.Second)
1006+
})
1007+
1008+
sub, err := ids1.Host.EventBus().Subscribe(new(event.EvtPeerIdentificationFailed), eventbus.BufSize(16))
1009+
require.NoError(t, err)
1010+
1011+
h2pi := h2.Peerstore().PeerInfo(h2p)
1012+
require.NoError(t, h1.Connect(ctx, h2pi))
1013+
1014+
select {
1015+
case ev := <-sub.Out():
1016+
fev := ev.(event.EvtPeerIdentificationFailed)
1017+
require.EqualError(t, fev.Reason, "i/o deadline reached")
1018+
case <-time.After(5 * time.Second):
1019+
t.Fatal("did not receive identify failure event")
1020+
}
1021+
}
1022+
1023+
func TestIncomingIDStreamsTimeout(t *testing.T) {
1024+
ctx := context.Background()
1025+
timeout := identify.StreamReadTimeout
1026+
identify.StreamReadTimeout = 100 * time.Millisecond
1027+
defer func() {
1028+
identify.StreamReadTimeout = timeout
1029+
}()
1030+
1031+
ctx, cancel := context.WithCancel(context.Background())
1032+
defer cancel()
1033+
1034+
protocols := []protocol.ID{identify.IDPush, identify.IDDelta}
1035+
1036+
for _, p := range protocols {
1037+
h1 := blhost.NewBlankHost(swarmt.GenSwarm(t, ctx))
1038+
h2 := blhost.NewBlankHost(swarmt.GenSwarm(t, ctx))
1039+
defer h1.Close()
1040+
defer h2.Close()
1041+
1042+
ids1 := identify.NewIDService(h1)
1043+
ids2 := identify.NewIDService(h2)
1044+
defer ids1.Close()
1045+
defer ids2.Close()
1046+
1047+
h2p := h2.ID()
1048+
h2pi := h2.Peerstore().PeerInfo(h2p)
1049+
require.NoError(t, h1.Connect(ctx, h2pi))
1050+
1051+
_, err := h1.NewStream(ctx, h2p, p)
1052+
require.NoError(t, err)
1053+
1054+
// remote peer should eventually reset stream
1055+
require.Eventually(t, func() bool {
1056+
c := h2.Network().ConnsToPeer(h1.ID())[0]
1057+
return len(c.GetStreams()) == 0
1058+
}, 1*time.Second, 200*time.Millisecond)
1059+
}
1060+
}

0 commit comments

Comments
 (0)