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.
 
 
 
 
 
 

131 lines
2.7 KiB

  1. package main
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. )
  13. // recordingConn is a net.Conn that records the traffic that passes through it.
  14. // WriteTo can be used to produce output that can be later be loaded with
  15. // ParseTestData.
  16. type recordingConn struct {
  17. net.Conn
  18. sync.Mutex
  19. flows [][]byte
  20. reading bool
  21. }
  22. func (r *recordingConn) Read(b []byte) (n int, err error) {
  23. if n, err = r.Conn.Read(b); n == 0 {
  24. return
  25. }
  26. b = b[:n]
  27. r.Lock()
  28. defer r.Unlock()
  29. if l := len(r.flows); l == 0 || !r.reading {
  30. buf := make([]byte, len(b))
  31. copy(buf, b)
  32. r.flows = append(r.flows, buf)
  33. } else {
  34. r.flows[l-1] = append(r.flows[l-1], b[:n]...)
  35. }
  36. r.reading = true
  37. return
  38. }
  39. func (r *recordingConn) Write(b []byte) (n int, err error) {
  40. if n, err = r.Conn.Write(b); n == 0 {
  41. return
  42. }
  43. b = b[:n]
  44. r.Lock()
  45. defer r.Unlock()
  46. if l := len(r.flows); l == 0 || r.reading {
  47. buf := make([]byte, len(b))
  48. copy(buf, b)
  49. r.flows = append(r.flows, buf)
  50. } else {
  51. r.flows[l-1] = append(r.flows[l-1], b[:n]...)
  52. }
  53. r.reading = false
  54. return
  55. }
  56. // WriteTo writes hex dumps to w that contains the recorded traffic.
  57. func (r *recordingConn) WriteTo(w io.Writer) {
  58. // TLS always starts with a client to server flow.
  59. clientToServer := true
  60. for i, flow := range r.flows {
  61. source, dest := "client", "server"
  62. if !clientToServer {
  63. source, dest = dest, source
  64. }
  65. fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
  66. dumper := hex.Dumper(w)
  67. dumper.Write(flow)
  68. dumper.Close()
  69. clientToServer = !clientToServer
  70. }
  71. }
  72. func parseTestData(r io.Reader) (flows [][]byte, err error) {
  73. var currentFlow []byte
  74. scanner := bufio.NewScanner(r)
  75. for scanner.Scan() {
  76. line := scanner.Text()
  77. // If the line starts with ">>> " then it marks the beginning
  78. // of a new flow.
  79. if strings.HasPrefix(line, ">>> ") {
  80. if len(currentFlow) > 0 || len(flows) > 0 {
  81. flows = append(flows, currentFlow)
  82. currentFlow = nil
  83. }
  84. continue
  85. }
  86. // Otherwise the line is a line of hex dump that looks like:
  87. // 00000170 fc f5 06 bf (...) |.....X{&?......!|
  88. // (Some bytes have been omitted from the middle section.)
  89. if i := strings.IndexByte(line, ' '); i >= 0 {
  90. line = line[i:]
  91. } else {
  92. return nil, errors.New("invalid test data")
  93. }
  94. if i := strings.IndexByte(line, '|'); i >= 0 {
  95. line = line[:i]
  96. } else {
  97. return nil, errors.New("invalid test data")
  98. }
  99. hexBytes := strings.Fields(line)
  100. for _, hexByte := range hexBytes {
  101. val, err := strconv.ParseUint(hexByte, 16, 8)
  102. if err != nil {
  103. return nil, errors.New("invalid hex byte in test data: " + err.Error())
  104. }
  105. currentFlow = append(currentFlow, byte(val))
  106. }
  107. }
  108. if len(currentFlow) > 0 {
  109. flows = append(flows, currentFlow)
  110. }
  111. return flows, nil
  112. }