diff --git a/fuzz.go b/fuzz.go index 822002b9..aefac986 100644 --- a/fuzz.go +++ b/fuzz.go @@ -1,7 +1,6 @@ package p2p import ( - "fmt" "math/rand" "net" "sync" @@ -15,58 +14,6 @@ const ( FuzzModeDelay ) -type FuzzConnConfig struct { - Mode int - MaxDelay time.Duration - ProbDropRW float64 - ProbDropConn float64 - ProbSleep float64 -} - -func defaultFuzzConnConfig() *FuzzConnConfig { - return &FuzzConnConfig{ - Mode: FuzzModeDrop, - MaxDelay: 3 * time.Second, - ProbDropRW: 0.2, - ProbDropConn: 0.00, - ProbSleep: 0.00, - } -} - -func FuzzConn(conn net.Conn) net.Conn { - return FuzzConnFromConfig(conn, defaultFuzzConnConfig()) -} - -func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn { - return &FuzzedConnection{ - conn: conn, - start: make(<-chan time.Time), - active: true, - mode: config.Mode, - maxDelay: config.MaxDelay, - probDropRW: config.ProbDropRW, - probDropConn: config.ProbDropConn, - probSleep: config.ProbSleep, - } -} - -func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn { - return FuzzConnAfterFromConfig(conn, d, defaultFuzzConnConfig()) -} - -func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn { - return &FuzzedConnection{ - conn: conn, - start: time.After(d), - active: false, - mode: config.Mode, - maxDelay: config.MaxDelay, - probDropRW: config.ProbDropRW, - probDropConn: config.ProbDropConn, - probSleep: config.ProbSleep, - } -} - // FuzzedConnection wraps any net.Conn and depending on the mode either delays // reads/writes or randomly drops reads/writes/connections. type FuzzedConnection struct { @@ -76,43 +23,108 @@ type FuzzedConnection struct { start <-chan time.Time active bool - mode int - maxDelay time.Duration - probDropRW float64 - probDropConn float64 - probSleep float64 + config *FuzzConnConfig } -func (fc *FuzzedConnection) randomDuration() time.Duration { - maxDelayMillis := int(fc.maxDelay.Nanoseconds() / 1000) - return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis) +// FuzzConnConfig is a FuzzedConnection configuration. +type FuzzConnConfig struct { + Mode int + MaxDelay time.Duration + ProbDropRW float64 + ProbDropConn float64 + ProbSleep float64 } -func (fc *FuzzedConnection) SetMode(mode int) { - switch mode { - case FuzzModeDrop: - fc.mode = FuzzModeDrop - case FuzzModeDelay: - fc.mode = FuzzModeDelay - default: - panic(fmt.Sprintf("Unknown mode %d", mode)) +// DefaultFuzzConnConfig returns the default config. +func DefaultFuzzConnConfig() *FuzzConnConfig { + return &FuzzConnConfig{ + Mode: FuzzModeDrop, + MaxDelay: 3 * time.Second, + ProbDropRW: 0.2, + ProbDropConn: 0.00, + ProbSleep: 0.00, } } -func (fc *FuzzedConnection) SetProbDropRW(prob float64) { - fc.probDropRW = prob +// FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately. +func FuzzConn(conn net.Conn) net.Conn { + return FuzzConnFromConfig(conn, DefaultFuzzConnConfig()) } -func (fc *FuzzedConnection) SetProbDropConn(prob float64) { - fc.probDropConn = prob +// FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing +// starts immediately. +func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn { + return &FuzzedConnection{ + conn: conn, + start: make(<-chan time.Time), + active: true, + config: config, + } } -func (fc *FuzzedConnection) SetProbSleep(prob float64) { - fc.probSleep = prob +// FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the +// duration elapses. +func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn { + return FuzzConnAfterFromConfig(conn, d, DefaultFuzzConnConfig()) } -func (fc *FuzzedConnection) SetMaxDelay(d time.Duration) { - fc.maxDelay = d +// FuzzConnAfterFromConfig creates a new FuzzedConnection from a config. +// Fuzzing starts when the duration elapses. +func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn { + return &FuzzedConnection{ + conn: conn, + start: time.After(d), + active: false, + config: config, + } +} + +// Config returns the connection's config. +func (fc *FuzzedConnection) Config() *FuzzConnConfig { + return fc.config +} + +// Read implements net.Conn. +func (fc *FuzzedConnection) Read(data []byte) (n int, err error) { + if fc.fuzz() { + return 0, nil + } + return fc.conn.Read(data) +} + +// Write implements net.Conn. +func (fc *FuzzedConnection) Write(data []byte) (n int, err error) { + if fc.fuzz() { + return 0, nil + } + return fc.conn.Write(data) +} + +// Close implements net.Conn. +func (fc *FuzzedConnection) Close() error { return fc.conn.Close() } + +// LocalAddr implements net.Conn. +func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() } + +// RemoteAddr implements net.Conn. +func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() } + +// SetDeadline implements net.Conn. +func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) } + +// SetReadDeadline implements net.Conn. +func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error { + return fc.conn.SetReadDeadline(t) +} + +// SetWriteDeadline implements net.Conn. +func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error { + return fc.conn.SetWriteDeadline(t) +} + +func (fc *FuzzedConnection) randomDuration() time.Duration { + maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000) + return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis) } // implements the fuzz (delay, kill conn) @@ -122,18 +134,18 @@ func (fc *FuzzedConnection) fuzz() bool { return false } - switch fc.mode { + switch fc.config.Mode { case FuzzModeDrop: // randomly drop the r/w, drop the conn, or sleep r := rand.Float64() - if r <= fc.probDropRW { + if r <= fc.config.ProbDropRW { return true - } else if r < fc.probDropRW+fc.probDropConn { + } else if r < fc.config.ProbDropRW+fc.config.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 { + } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep { time.Sleep(fc.randomDuration()) } case FuzzModeDelay: @@ -143,7 +155,6 @@ func (fc *FuzzedConnection) fuzz() bool { return false } -// we don't fuzz until start chan fires func (fc *FuzzedConnection) shouldFuzz() bool { if fc.active { return true @@ -160,31 +171,3 @@ func (fc *FuzzedConnection) shouldFuzz() bool { return false } } - -// Read implements net.Conn -func (fc *FuzzedConnection) Read(data []byte) (n int, err error) { - if fc.fuzz() { - return 0, nil - } - return fc.conn.Read(data) -} - -// Write implements net.Conn -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) -} diff --git a/peer.go b/peer.go index 6ef31737..3d2b4720 100644 --- a/peer.go +++ b/peer.go @@ -52,7 +52,7 @@ func defaultPeerConfig() *PeerConfig { HandshakeTimeout: 20 * time.Second, DialTimeout: 3 * time.Second, MConfig: defaultMConnectionConfig(), - FuzzConfig: defaultFuzzConnConfig(), + FuzzConfig: DefaultFuzzConnConfig(), } }