1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
package p2p
import (
"math/rand"
"net"
"sync"
"time"
cfg "github.com/tendermint/go-config"
)
//--------------------------------------------------------
// delay reads/writes
// randomly drop reads/writes
// randomly drop connections
const (
FuzzModeDrop = "drop"
FuzzModeDelay = "delay"
)
func FuzzConn(config cfg.Config, conn net.Conn) net.Conn {
return &FuzzedConnection{
conn: conn,
start: time.After(time.Second * 10), // so we have time to do peer handshakes and get set up
params: config,
}
}
type FuzzedConnection struct {
conn net.Conn
mtx sync.Mutex
fuzz bool // we don't start fuzzing right away
start <-chan time.Time
// fuzz params
params cfg.Config
}
func (fc *FuzzedConnection) randomDuration() time.Duration {
return time.Millisecond * time.Duration(rand.Int()%fc.MaxDelayMilliseconds())
}
func (fc *FuzzedConnection) Active() bool {
return fc.params.GetBool(configFuzzActive)
}
func (fc *FuzzedConnection) Mode() string {
return fc.params.GetString(configFuzzMode)
}
func (fc *FuzzedConnection) ProbDropRW() float64 {
return fc.params.GetFloat64(configFuzzProbDropRW)
}
func (fc *FuzzedConnection) ProbDropConn() float64 {
return fc.params.GetFloat64(configFuzzProbDropConn)
}
func (fc *FuzzedConnection) ProbSleep() float64 {
return fc.params.GetFloat64(configFuzzProbSleep)
}
func (fc *FuzzedConnection) MaxDelayMilliseconds() int {
return fc.params.GetInt(configFuzzMaxDelayMilliseconds)
}
// implements the fuzz (delay, kill conn)
// and returns whether or not the read/write should be ignored
func (fc *FuzzedConnection) Fuzz() bool {
if !fc.shouldFuzz() {
return false
}
switch fc.Mode() {
case FuzzModeDrop:
// randomly drop the r/w, drop the conn, or sleep
r := rand.Float64()
if r <= fc.ProbDropRW() {
return true
} else if r < fc.ProbDropRW()+fc.ProbDropConn() {
// XXX: can't this fail because machine precision?
// XXX: do we need an error?
fc.Close()
return true
} else if r < fc.ProbDropRW()+fc.ProbDropConn()+fc.ProbSleep() {
time.Sleep(fc.randomDuration())
}
case FuzzModeDelay:
// sleep a bit
time.Sleep(fc.randomDuration())
}
return false
}
// we don't fuzz until start chan fires
func (fc *FuzzedConnection) shouldFuzz() bool {
if !fc.Active() {
return false
}
fc.mtx.Lock()
defer fc.mtx.Unlock()
if fc.fuzz {
return true
}
select {
case <-fc.start:
fc.fuzz = true
default:
}
return false
}
func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
if fc.Fuzz() {
return 0, nil
}
return fc.conn.Read(data)
}
func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
if fc.Fuzz() {
return 0, nil
}
return fc.conn.Write(data)
}
// Implements net.Conn
func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
return fc.conn.SetReadDeadline(t)
}
func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
return fc.conn.SetWriteDeadline(t)
}
|