Skip to content

Commit 4ce7970

Browse files
fjlobscuren
authored andcommitted
[release/1.3.4] p2p/discover: fix Windows-specific issue for larger-than-buffer packets
On Windows, UDPConn.ReadFrom returns an error for packets larger than the receive buffer. The error is not marked temporary, causing our loop to exit when the first oversized packet arrived. The fix is to treat this particular error as temporary. Fixes: #1579, #2087 Updates: #2082 Conflicts: p2p/discover/udp_test.go
1 parent 0c17be9 commit 4ce7970

File tree

4 files changed

+123
-7
lines changed

4 files changed

+123
-7
lines changed

p2p/discover/udp.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,11 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte,
455455
return packet, nil
456456
}
457457

458-
type tempError interface {
459-
Temporary() bool
458+
func isTemporaryError(err error) bool {
459+
tempErr, ok := err.(interface {
460+
Temporary() bool
461+
})
462+
return ok && tempErr.Temporary() || isPacketTooBig(err)
460463
}
461464

462465
// readLoop runs in its own goroutine. it handles incoming UDP packets.
@@ -468,7 +471,7 @@ func (t *udp) readLoop() {
468471
buf := make([]byte, 1280)
469472
for {
470473
nbytes, from, err := t.conn.ReadFromUDP(buf)
471-
if tempErr, ok := err.(tempError); ok && tempErr.Temporary() {
474+
if isTemporaryError(err) {
472475
// Ignore temporary read errors.
473476
glog.V(logger.Debug).Infof("Temporary read error: %v", err)
474477
continue

p2p/discover/udp_notwindows.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 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+
//+build !windows
18+
19+
package discover
20+
21+
// reports whether err indicates that a UDP packet didn't
22+
// fit the receive buffer. There is no such error on
23+
// non-Windows platforms.
24+
func isPacketTooBig(err error) bool {
25+
return false
26+
}

p2p/discover/udp_test.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ import (
2424
"errors"
2525
"fmt"
2626
"io"
27-
logpkg "log"
2827
"math/rand"
2928
"net"
30-
"os"
3129
"path/filepath"
3230
"reflect"
3331
"runtime"
@@ -38,12 +36,61 @@ import (
3836
"github.com/davecgh/go-spew/spew"
3937
"github.com/ethereum/go-ethereum/common"
4038
"github.com/ethereum/go-ethereum/crypto"
41-
"github.com/ethereum/go-ethereum/logger"
4239
"github.com/ethereum/go-ethereum/rlp"
4340
)
4441

4542
func init() {
46-
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
43+
spew.Config.DisableMethods = true
44+
}
45+
46+
// This test checks that isPacketTooBig correctly identifies
47+
// errors that result from receiving a UDP packet larger
48+
// than the supplied receive buffer.
49+
func TestIsPacketTooBig(t *testing.T) {
50+
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
51+
if err != nil {
52+
t.Fatal(err)
53+
}
54+
defer listener.Close()
55+
sender, err := net.Dial("udp", listener.LocalAddr().String())
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
defer sender.Close()
60+
61+
sendN := 1800
62+
recvN := 300
63+
for i := 0; i < 20; i++ {
64+
go func() {
65+
buf := make([]byte, sendN)
66+
for i := range buf {
67+
buf[i] = byte(i)
68+
}
69+
sender.Write(buf)
70+
}()
71+
72+
buf := make([]byte, recvN)
73+
listener.SetDeadline(time.Now().Add(1 * time.Second))
74+
n, _, err := listener.ReadFrom(buf)
75+
if err != nil {
76+
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
77+
continue
78+
}
79+
if !isPacketTooBig(err) {
80+
t.Fatal("unexpected read error:", spew.Sdump(err))
81+
}
82+
continue
83+
}
84+
if n != recvN {
85+
t.Fatalf("short read: %d, want %d", n, recvN)
86+
}
87+
for i := range buf {
88+
if buf[i] != byte(i) {
89+
t.Fatalf("error in pattern")
90+
break
91+
}
92+
}
93+
}
4794
}
4895

4996
// shared test variables

p2p/discover/udp_windows.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2016 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+
//+build windows
18+
19+
package discover
20+
21+
import (
22+
"net"
23+
"os"
24+
"syscall"
25+
)
26+
27+
const _WSAEMSGSIZE = syscall.Errno(10040)
28+
29+
// reports whether err indicates that a UDP packet didn't
30+
// fit the receive buffer. On Windows, WSARecvFrom returns
31+
// code WSAEMSGSIZE and no data if this happens.
32+
func isPacketTooBig(err error) bool {
33+
if opErr, ok := err.(*net.OpError); ok {
34+
if scErr, ok := opErr.Err.(*os.SyscallError); ok {
35+
return scErr.Err == _WSAEMSGSIZE
36+
}
37+
return opErr.Err == _WSAEMSGSIZE
38+
}
39+
return false
40+
}

0 commit comments

Comments
 (0)