2015-09-29 23:21:04 +01:00
|
|
|
package runner
|
2014-10-11 00:23:43 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
2015-11-02 20:33:18 +00:00
|
|
|
type flowType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
readFlow flowType = iota
|
|
|
|
writeFlow
|
|
|
|
specialFlow
|
|
|
|
)
|
|
|
|
|
|
|
|
type flow struct {
|
|
|
|
flowType flowType
|
|
|
|
message string
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
2014-10-11 00:23:43 +01:00
|
|
|
// recordingConn is a net.Conn that records the traffic that passes through it.
|
|
|
|
// WriteTo can be used to produce output that can be later be loaded with
|
|
|
|
// ParseTestData.
|
|
|
|
type recordingConn struct {
|
|
|
|
net.Conn
|
|
|
|
sync.Mutex
|
2015-11-02 20:33:18 +00:00
|
|
|
flows []flow
|
|
|
|
isDatagram bool
|
|
|
|
local, peer string
|
2014-10-11 00:23:43 +01:00
|
|
|
}
|
|
|
|
|
2015-11-02 20:33:18 +00:00
|
|
|
func (r *recordingConn) appendFlow(flowType flowType, message string, data []byte) {
|
2014-10-11 00:23:43 +01:00
|
|
|
r.Lock()
|
|
|
|
defer r.Unlock()
|
|
|
|
|
2015-11-02 20:33:18 +00:00
|
|
|
if l := len(r.flows); flowType == specialFlow || r.isDatagram || l == 0 || r.flows[l-1].flowType != flowType {
|
|
|
|
buf := make([]byte, len(data))
|
|
|
|
copy(buf, data)
|
|
|
|
r.flows = append(r.flows, flow{flowType, message, buf})
|
2014-10-11 00:23:43 +01:00
|
|
|
} else {
|
2015-11-02 20:33:18 +00:00
|
|
|
r.flows[l-1].data = append(r.flows[l-1].data, data...)
|
2014-10-11 00:23:43 +01:00
|
|
|
}
|
2015-11-02 20:33:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *recordingConn) Read(b []byte) (n int, err error) {
|
|
|
|
if n, err = r.Conn.Read(b); n == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.appendFlow(readFlow, "", b[:n])
|
2014-10-11 00:23:43 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *recordingConn) Write(b []byte) (n int, err error) {
|
|
|
|
if n, err = r.Conn.Write(b); n == 0 {
|
|
|
|
return
|
|
|
|
}
|
2015-11-02 20:33:18 +00:00
|
|
|
r.appendFlow(writeFlow, "", b[:n])
|
2014-10-11 00:23:43 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-11-02 20:33:18 +00:00
|
|
|
// LogSpecial appends an entry to the record of type 'special'.
|
|
|
|
func (r *recordingConn) LogSpecial(message string, data []byte) {
|
|
|
|
r.appendFlow(specialFlow, message, data)
|
|
|
|
}
|
|
|
|
|
2014-10-11 00:23:43 +01:00
|
|
|
// WriteTo writes hex dumps to w that contains the recorded traffic.
|
|
|
|
func (r *recordingConn) WriteTo(w io.Writer) {
|
2015-11-02 20:33:18 +00:00
|
|
|
fmt.Fprintf(w, ">>> runner is %s, shim is %s\n", r.local, r.peer)
|
2014-10-11 00:23:43 +01:00
|
|
|
for i, flow := range r.flows {
|
2015-11-02 20:33:18 +00:00
|
|
|
switch flow.flowType {
|
|
|
|
case readFlow:
|
|
|
|
fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.peer, r.local)
|
|
|
|
case writeFlow:
|
|
|
|
fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.local, r.peer)
|
|
|
|
case specialFlow:
|
|
|
|
fmt.Fprintf(w, ">>> Flow %d %q\n", i+1, flow.message)
|
|
|
|
}
|
|
|
|
|
|
|
|
if flow.data != nil {
|
|
|
|
dumper := hex.Dumper(w)
|
|
|
|
dumper.Write(flow.data)
|
|
|
|
dumper.Close()
|
2014-10-11 00:23:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 04:25:48 +00:00
|
|
|
func (r *recordingConn) Transcript() []byte {
|
|
|
|
var ret []byte
|
|
|
|
for _, flow := range r.flows {
|
|
|
|
if flow.flowType != writeFlow {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ret = append(ret, flow.data...)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2014-10-11 00:23:43 +01:00
|
|
|
func parseTestData(r io.Reader) (flows [][]byte, err error) {
|
|
|
|
var currentFlow []byte
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
// If the line starts with ">>> " then it marks the beginning
|
|
|
|
// of a new flow.
|
|
|
|
if strings.HasPrefix(line, ">>> ") {
|
|
|
|
if len(currentFlow) > 0 || len(flows) > 0 {
|
|
|
|
flows = append(flows, currentFlow)
|
|
|
|
currentFlow = nil
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise the line is a line of hex dump that looks like:
|
|
|
|
// 00000170 fc f5 06 bf (...) |.....X{&?......!|
|
|
|
|
// (Some bytes have been omitted from the middle section.)
|
|
|
|
|
|
|
|
if i := strings.IndexByte(line, ' '); i >= 0 {
|
|
|
|
line = line[i:]
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("invalid test data")
|
|
|
|
}
|
|
|
|
|
|
|
|
if i := strings.IndexByte(line, '|'); i >= 0 {
|
|
|
|
line = line[:i]
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("invalid test data")
|
|
|
|
}
|
|
|
|
|
|
|
|
hexBytes := strings.Fields(line)
|
|
|
|
for _, hexByte := range hexBytes {
|
|
|
|
val, err := strconv.ParseUint(hexByte, 16, 8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New("invalid hex byte in test data: " + err.Error())
|
|
|
|
}
|
|
|
|
currentFlow = append(currentFlow, byte(val))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(currentFlow) > 0 {
|
|
|
|
flows = append(flows, currentFlow)
|
|
|
|
}
|
|
|
|
|
|
|
|
return flows, nil
|
|
|
|
}
|