You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
4.3 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package p2p
  2. import (
  3. "math/rand"
  4. "net"
  5. "sync"
  6. "time"
  7. )
  8. const (
  9. // FuzzModeDrop is a mode in which we randomly drop reads/writes, connections or sleep
  10. FuzzModeDrop = iota
  11. // FuzzModeDelay is a mode in which we randomly sleep
  12. FuzzModeDelay
  13. )
  14. // FuzzedConnection wraps any net.Conn and depending on the mode either delays
  15. // reads/writes or randomly drops reads/writes/connections.
  16. type FuzzedConnection struct {
  17. conn net.Conn
  18. mtx sync.Mutex
  19. start <-chan time.Time
  20. active bool
  21. config *FuzzConnConfig
  22. }
  23. // FuzzConnConfig is a FuzzedConnection configuration.
  24. type FuzzConnConfig struct {
  25. Mode int
  26. MaxDelay time.Duration
  27. ProbDropRW float64
  28. ProbDropConn float64
  29. ProbSleep float64
  30. }
  31. // DefaultFuzzConnConfig returns the default config.
  32. func DefaultFuzzConnConfig() *FuzzConnConfig {
  33. return &FuzzConnConfig{
  34. Mode: FuzzModeDrop,
  35. MaxDelay: 3 * time.Second,
  36. ProbDropRW: 0.2,
  37. ProbDropConn: 0.00,
  38. ProbSleep: 0.00,
  39. }
  40. }
  41. // FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately.
  42. func FuzzConn(conn net.Conn) net.Conn {
  43. return FuzzConnFromConfig(conn, DefaultFuzzConnConfig())
  44. }
  45. // FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing
  46. // starts immediately.
  47. func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn {
  48. return &FuzzedConnection{
  49. conn: conn,
  50. start: make(<-chan time.Time),
  51. active: true,
  52. config: config,
  53. }
  54. }
  55. // FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the
  56. // duration elapses.
  57. func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn {
  58. return FuzzConnAfterFromConfig(conn, d, DefaultFuzzConnConfig())
  59. }
  60. // FuzzConnAfterFromConfig creates a new FuzzedConnection from a config.
  61. // Fuzzing starts when the duration elapses.
  62. func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn {
  63. return &FuzzedConnection{
  64. conn: conn,
  65. start: time.After(d),
  66. active: false,
  67. config: config,
  68. }
  69. }
  70. // Config returns the connection's config.
  71. func (fc *FuzzedConnection) Config() *FuzzConnConfig {
  72. return fc.config
  73. }
  74. // Read implements net.Conn.
  75. func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
  76. if fc.fuzz() {
  77. return 0, nil
  78. }
  79. return fc.conn.Read(data)
  80. }
  81. // Write implements net.Conn.
  82. func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
  83. if fc.fuzz() {
  84. return 0, nil
  85. }
  86. return fc.conn.Write(data)
  87. }
  88. // Close implements net.Conn.
  89. func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
  90. // LocalAddr implements net.Conn.
  91. func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
  92. // RemoteAddr implements net.Conn.
  93. func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
  94. // SetDeadline implements net.Conn.
  95. func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
  96. // SetReadDeadline implements net.Conn.
  97. func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
  98. return fc.conn.SetReadDeadline(t)
  99. }
  100. // SetWriteDeadline implements net.Conn.
  101. func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
  102. return fc.conn.SetWriteDeadline(t)
  103. }
  104. func (fc *FuzzedConnection) randomDuration() time.Duration {
  105. maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
  106. return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis)
  107. }
  108. // implements the fuzz (delay, kill conn)
  109. // and returns whether or not the read/write should be ignored
  110. func (fc *FuzzedConnection) fuzz() bool {
  111. if !fc.shouldFuzz() {
  112. return false
  113. }
  114. switch fc.config.Mode {
  115. case FuzzModeDrop:
  116. // randomly drop the r/w, drop the conn, or sleep
  117. r := rand.Float64()
  118. if r <= fc.config.ProbDropRW {
  119. return true
  120. } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
  121. // XXX: can't this fail because machine precision?
  122. // XXX: do we need an error?
  123. fc.Close() // nolint: errcheck
  124. return true
  125. } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep {
  126. time.Sleep(fc.randomDuration())
  127. }
  128. case FuzzModeDelay:
  129. // sleep a bit
  130. time.Sleep(fc.randomDuration())
  131. }
  132. return false
  133. }
  134. func (fc *FuzzedConnection) shouldFuzz() bool {
  135. if fc.active {
  136. return true
  137. }
  138. fc.mtx.Lock()
  139. defer fc.mtx.Unlock()
  140. select {
  141. case <-fc.start:
  142. fc.active = true
  143. return true
  144. default:
  145. return false
  146. }
  147. }