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.

152 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. cmn "github.com/tendermint/tmlibs/common"
  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(cmn.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 := cmn.RandFloat64()
  99. if r <= fc.config.ProbDropRW {
  100. return true
  101. } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
  102. // XXX: can't this fail because machine precision?
  103. // XXX: do we need an error?
  104. fc.Close() // nolint: errcheck, gas
  105. return true
  106. } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep {
  107. time.Sleep(fc.randomDuration())
  108. }
  109. case config.FuzzModeDelay:
  110. // sleep a bit
  111. time.Sleep(fc.randomDuration())
  112. }
  113. return false
  114. }
  115. func (fc *FuzzedConnection) shouldFuzz() bool {
  116. if fc.active {
  117. return true
  118. }
  119. fc.mtx.Lock()
  120. defer fc.mtx.Unlock()
  121. select {
  122. case <-fc.start:
  123. fc.active = true
  124. return true
  125. default:
  126. return false
  127. }
  128. }