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.

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