mirror of
https://github.com/buger/goreplay.git
synced 2026-10-19 16:15:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc385c31f7 | ||
|
|
18dbb7e8ab | ||
|
|
89d22e685a | ||
|
|
7deac8d02d |
@@ -67,6 +67,9 @@ run-2:
|
||||
run-3:
|
||||
sudo -E go run $(SOURCE) --input-tcp :27001 --output-stdout
|
||||
|
||||
run-4:
|
||||
sudo -E go run $(SOURCE) --input-http 127.0.0.1:8081 --input-raw 127.0.0.1:8081 --output-stdout --input-raw-engine raw_socket
|
||||
|
||||
record:
|
||||
$(RUN) go run $(SOURCE) --input-dummy=0 --output-file=requests.gor --verbose --debug
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func TestHTTPClientURLPort(t *testing.T) {
|
||||
@@ -202,6 +203,10 @@ func TestHTTPClientHTTPSSend(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHTTPClientServerInstantDisconnect(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
|
||||
GETPayload := []byte("GET / HTTP/1.1\r\n\r\n")
|
||||
|
||||
+23
-199
@@ -16,9 +16,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@@ -32,6 +29,17 @@ import (
|
||||
|
||||
var _ = fmt.Println
|
||||
|
||||
type wrongCloser interface {
|
||||
Close()
|
||||
}
|
||||
type wrongCloserProxy struct {
|
||||
w wrongCloser
|
||||
}
|
||||
func (c wrongCloserProxy) Close() error {
|
||||
c.w.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listener handle traffic capture
|
||||
type Listener struct {
|
||||
mu sync.Mutex
|
||||
@@ -63,8 +71,7 @@ type Listener struct {
|
||||
trackResponse bool
|
||||
messageExpire time.Duration
|
||||
|
||||
conn net.PacketConn
|
||||
pcapHandles []*pcap.Handle
|
||||
connHandles []io.Closer
|
||||
|
||||
quit chan bool
|
||||
readyCh chan bool
|
||||
@@ -112,6 +119,10 @@ func NewListener(addr string, port string, engine int, trackResponse bool, expir
|
||||
|
||||
// Special case for testing
|
||||
if l.port != 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
engine = EngineRawSocket
|
||||
}
|
||||
|
||||
switch engine {
|
||||
case EngineRawSocket:
|
||||
go l.readRAWSocket()
|
||||
@@ -131,9 +142,7 @@ func (t *Listener) listen() {
|
||||
for {
|
||||
select {
|
||||
case <-t.quit:
|
||||
if t.conn != nil {
|
||||
t.conn.Close()
|
||||
}
|
||||
t.Close()
|
||||
return
|
||||
case data := <-t.packetsChan:
|
||||
packet := ParseTCPPacket(data[:16], data[16:])
|
||||
@@ -217,195 +226,15 @@ func (t *Listener) dispatchMessage(message *TCPMessage) {
|
||||
t.messagesChan <- message
|
||||
}
|
||||
|
||||
// DeviceNotFoundError raised if user specified wrong ip
|
||||
type DeviceNotFoundError struct {
|
||||
addr string
|
||||
}
|
||||
|
||||
func (e *DeviceNotFoundError) Error() string {
|
||||
devices, _ := pcap.FindAllDevs()
|
||||
|
||||
if len(devices) == 0 {
|
||||
return "Can't get list of network interfaces, ensure that you running Gor as root user or sudo.\nTo run as non-root users see this docs https://github.com/buger/gor/wiki/Running-as-non-root-user"
|
||||
}
|
||||
|
||||
var msg string
|
||||
msg += "Can't find interfaces with addr: " + e.addr + ". Provide available IP for intercepting traffic: \n"
|
||||
for _, device := range devices {
|
||||
msg += "Name: " + device.Name + "\n"
|
||||
if device.Description != "" {
|
||||
msg += "Description: " + device.Description + "\n"
|
||||
}
|
||||
for _, address := range device.Addresses {
|
||||
msg += "- IP address: " + address.IP.String() + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func findPcapDevices(addr string) (interfaces []pcap.Interface, err error) {
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
if (addr == "" || addr == "0.0.0.0" || addr == "[::]" || addr == "::") && len(device.Addresses) > 0 {
|
||||
interfaces = append(interfaces, device)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, address := range device.Addresses {
|
||||
if device.Name == addr || address.IP.String() == addr {
|
||||
interfaces = append(interfaces, device)
|
||||
return interfaces, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(interfaces) == 0 {
|
||||
return nil, &DeviceNotFoundError{addr}
|
||||
} else {
|
||||
return interfaces, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Listener) readPcap() {
|
||||
devices, err := findPcapDevices(t.addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bpfSupported := true
|
||||
if runtime.GOOS == "darwin" {
|
||||
bpfSupported = false
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(devices))
|
||||
|
||||
for _, d := range devices {
|
||||
go func(device pcap.Interface) {
|
||||
handle, err := pcap.OpenLive(device.Name, 65536, true, t.messageExpire)
|
||||
if err != nil {
|
||||
log.Println("Pcap Error while opening device", device.Name, err)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
t.mu.Lock()
|
||||
t.pcapHandles = append(t.pcapHandles, handle)
|
||||
|
||||
if bpfSupported {
|
||||
var bpf string
|
||||
|
||||
if t.trackResponse {
|
||||
bpf = "tcp port " + strconv.Itoa(int(t.port))
|
||||
} else {
|
||||
bpf = "tcp dst port " + strconv.Itoa(int(t.port))
|
||||
}
|
||||
if err := handle.SetBPFFilter(bpf); err != nil {
|
||||
log.Println("BPF filter error:", err, "Device:", device.Name)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
t.mu.Unlock()
|
||||
|
||||
linkType := handle.LinkType()
|
||||
source := gopacket.NewPacketSource(handle, linkType)
|
||||
source.Lazy = true
|
||||
source.NoCopy = true
|
||||
|
||||
wg.Done()
|
||||
|
||||
var data, srcIP []byte
|
||||
|
||||
for {
|
||||
packet, err := source.NextPacket()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if linkType == layers.LinkTypeEthernet {
|
||||
// Skip ethernet layer, 14 bytes
|
||||
data = packet.Data()[14:]
|
||||
} else if linkType == layers.LinkTypeNull || linkType == layers.LinkTypeLoop {
|
||||
data = packet.Data()[4:]
|
||||
}
|
||||
|
||||
version := uint8(data[0]) >> 4
|
||||
|
||||
if version == 4 {
|
||||
ihl := uint8(data[0]) & 0x0F
|
||||
|
||||
// Truncated IP info
|
||||
if len(data) < int(ihl*4) {
|
||||
continue
|
||||
}
|
||||
|
||||
srcIP = data[12:16]
|
||||
data = data[ihl*4:]
|
||||
} else {
|
||||
// Truncated IP info
|
||||
if len(data) < 40 {
|
||||
continue
|
||||
}
|
||||
|
||||
srcIP = data[8:24]
|
||||
|
||||
data = data[40:]
|
||||
}
|
||||
|
||||
// Truncated TCP info
|
||||
if len(data) < 13 {
|
||||
continue
|
||||
}
|
||||
|
||||
dataOffset := (data[12] & 0xF0) >> 4
|
||||
|
||||
// We need only packets with data inside
|
||||
// Check that the buffer is larger than the size of the TCP header
|
||||
if len(data) > int(dataOffset*4) {
|
||||
if !bpfSupported {
|
||||
destPort := binary.BigEndian.Uint16(data[2:4])
|
||||
srcPort := binary.BigEndian.Uint16(data[0:2])
|
||||
|
||||
// log.Println(t.port, destPort, srcPort, packet)
|
||||
|
||||
if !(destPort == t.port || (t.trackResponse && srcPort == t.port)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
newBuf := make([]byte, len(data)+16)
|
||||
copy(newBuf[:16], srcIP)
|
||||
copy(newBuf[16:], data)
|
||||
|
||||
t.packetsChan <- newBuf
|
||||
}
|
||||
}
|
||||
}(d)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
t.readyCh <- true
|
||||
}
|
||||
|
||||
func (t *Listener) readRAWSocket() {
|
||||
conn, e := net.ListenPacket("ip4:tcp", t.addr)
|
||||
t.conn = conn
|
||||
conn, e := net.ListenPacket("ip:tcp", t.addr)
|
||||
t.connHandles = append(t.connHandles, conn)
|
||||
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
defer t.conn.Close()
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 64*1024) // 64kb
|
||||
|
||||
@@ -413,7 +242,7 @@ func (t *Listener) readRAWSocket() {
|
||||
|
||||
for {
|
||||
// Note: ReadFrom receive messages without IP header
|
||||
n, addr, err := t.conn.ReadFrom(buf)
|
||||
n, addr, err := conn.ReadFrom(buf)
|
||||
|
||||
if err != nil {
|
||||
if strings.HasSuffix(err.Error(), "closed network connection") {
|
||||
@@ -425,7 +254,7 @@ func (t *Listener) readRAWSocket() {
|
||||
|
||||
if n > 0 {
|
||||
if t.isValidPacket(buf[:n]) {
|
||||
newBuf := make([]byte, n+4)
|
||||
newBuf := make([]byte, n+16)
|
||||
copy(newBuf[16:], buf[:n])
|
||||
copy(newBuf[:16], []byte(addr.(*net.IPAddr).IP))
|
||||
|
||||
@@ -618,12 +447,7 @@ func (t *Listener) Receiver() chan *TCPMessage {
|
||||
}
|
||||
|
||||
func (t *Listener) Close() {
|
||||
close(t.quit)
|
||||
if t.conn != nil {
|
||||
t.conn.Close()
|
||||
}
|
||||
|
||||
for _, h := range t.pcapHandles {
|
||||
for _, h := range t.connHandles {
|
||||
h.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// +build !windows
|
||||
|
||||
package rawSocket
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"log"
|
||||
"io"
|
||||
"sync"
|
||||
"strconv"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
|
||||
// DeviceNotFoundError raised if user specified wrong ip
|
||||
type DeviceNotFoundError struct {
|
||||
addr string
|
||||
}
|
||||
|
||||
func (e *DeviceNotFoundError) Error() string {
|
||||
devices, _ := pcap.FindAllDevs()
|
||||
|
||||
if len(devices) == 0 {
|
||||
return "Can't get list of network interfaces, ensure that you running Gor as root user or sudo.\nTo run as non-root users see this docs https://github.com/buger/gor/wiki/Running-as-non-root-user"
|
||||
}
|
||||
|
||||
var msg string
|
||||
msg += "Can't find interfaces with addr: " + e.addr + ". Provide available IP for intercepting traffic: \n"
|
||||
for _, device := range devices {
|
||||
msg += "Name: " + device.Name + "\n"
|
||||
if device.Description != "" {
|
||||
msg += "Description: " + device.Description + "\n"
|
||||
}
|
||||
for _, address := range device.Addresses {
|
||||
msg += "- IP address: " + address.IP.String() + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func findPcapDevices(addr string) (interfaces []pcap.Interface, err error) {
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
if (addr == "" || addr == "0.0.0.0" || addr == "[::]" || addr == "::") && len(device.Addresses) > 0 {
|
||||
interfaces = append(interfaces, device)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, address := range device.Addresses {
|
||||
if device.Name == addr || address.IP.String() == addr {
|
||||
interfaces = append(interfaces, device)
|
||||
return interfaces, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(interfaces) == 0 {
|
||||
return nil, &DeviceNotFoundError{addr}
|
||||
} else {
|
||||
return interfaces, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Listener) readPcap() {
|
||||
devices, err := findPcapDevices(t.addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bpfSupported := true
|
||||
if runtime.GOOS == "darwin" {
|
||||
bpfSupported = false
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(devices))
|
||||
|
||||
for _, d := range devices {
|
||||
go func(device pcap.Interface) {
|
||||
handle, err := pcap.OpenLive(device.Name, 65536, true, t.messageExpire)
|
||||
if err != nil {
|
||||
log.Println("Pcap Error while opening device", device.Name, err)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
t.mu.Lock()
|
||||
t.connHandles = append(t.connHandles, wrongCloserProxy{handle})
|
||||
|
||||
if bpfSupported {
|
||||
var bpf string
|
||||
|
||||
if t.trackResponse {
|
||||
bpf = "tcp port " + strconv.Itoa(int(t.port))
|
||||
} else {
|
||||
bpf = "tcp dst port " + strconv.Itoa(int(t.port))
|
||||
}
|
||||
if err := handle.SetBPFFilter(bpf); err != nil {
|
||||
log.Println("BPF filter error:", err, "Device:", device.Name)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
t.mu.Unlock()
|
||||
|
||||
linkType := handle.LinkType()
|
||||
source := gopacket.NewPacketSource(handle, linkType)
|
||||
source.Lazy = true
|
||||
source.NoCopy = true
|
||||
|
||||
wg.Done()
|
||||
|
||||
var data, srcIP []byte
|
||||
|
||||
for {
|
||||
packet, err := source.NextPacket()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if linkType == layers.LinkTypeEthernet {
|
||||
// Skip ethernet layer, 14 bytes
|
||||
data = packet.Data()[14:]
|
||||
} else if linkType == layers.LinkTypeNull || linkType == layers.LinkTypeLoop {
|
||||
data = packet.Data()[4:]
|
||||
}
|
||||
|
||||
version := uint8(data[0]) >> 4
|
||||
|
||||
if version == 4 {
|
||||
ihl := uint8(data[0]) & 0x0F
|
||||
|
||||
// Truncated IP info
|
||||
if len(data) < int(ihl*4) {
|
||||
continue
|
||||
}
|
||||
|
||||
srcIP = data[12:16]
|
||||
data = data[ihl*4:]
|
||||
} else {
|
||||
// Truncated IP info
|
||||
if len(data) < 40 {
|
||||
continue
|
||||
}
|
||||
|
||||
srcIP = data[8:24]
|
||||
|
||||
data = data[40:]
|
||||
}
|
||||
|
||||
// Truncated TCP info
|
||||
if len(data) < 13 {
|
||||
continue
|
||||
}
|
||||
|
||||
dataOffset := (data[12] & 0xF0) >> 4
|
||||
|
||||
// We need only packets with data inside
|
||||
// Check that the buffer is larger than the size of the TCP header
|
||||
if len(data) > int(dataOffset*4) {
|
||||
if !bpfSupported {
|
||||
destPort := binary.BigEndian.Uint16(data[2:4])
|
||||
srcPort := binary.BigEndian.Uint16(data[0:2])
|
||||
|
||||
// log.Println(t.port, destPort, srcPort, packet)
|
||||
|
||||
if !(destPort == t.port || (t.trackResponse && srcPort == t.port)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
newBuf := make([]byte, len(data)+16)
|
||||
copy(newBuf[:16], srcIP)
|
||||
copy(newBuf[16:], data)
|
||||
|
||||
t.packetsChan <- newBuf
|
||||
}
|
||||
}
|
||||
}(d)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
t.readyCh <- true
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build windows
|
||||
|
||||
package rawSocket
|
||||
|
||||
func (t *Listener) readPcap() {
|
||||
panic("libpcap not implemented for windows")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user