mirror of
https://github.com/buger/goreplay.git
synced 2024-04-21 12:32:02 +00:00
Updated docs
This commit is contained in:
@@ -40,10 +40,11 @@ func Run() {
|
||||
log.Fatal("Connection error", err)
|
||||
}
|
||||
|
||||
// Sniffing traffic from given port
|
||||
// Sniffing traffic from given address
|
||||
listener := RAWTCPListen(Settings.address, Settings.port)
|
||||
|
||||
for {
|
||||
// Receiving TCPMessage object
|
||||
m := listener.Receive()
|
||||
|
||||
if Settings.verbose {
|
||||
|
||||
@@ -6,17 +6,23 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Capture traffic from socket using RAW_SOCKET's
|
||||
// http://en.wikipedia.org/wiki/Raw_socket
|
||||
//
|
||||
// RAW_SOCKET allow you listen for traffic on any port (e.g. sniffing) because they operate on IP level.
|
||||
// Ports is TCP feature, same as flow control, reliable transmission and etc.
|
||||
// Since we can't use default TCP libraries RAWTCPLitener implements own TCP layer
|
||||
// TCP packets is parsed using tcp_packet.go, and flow control is managed by tcp_message.go
|
||||
type RAWTCPListener struct {
|
||||
messages []*TCPMessage // buffer of TCPMessages waiting to be send
|
||||
|
||||
c_packets chan *TCPPacket
|
||||
c_messages chan *TCPMessage
|
||||
c_messages chan *TCPMessage // Messages ready to be send to client
|
||||
|
||||
c_add_message chan *TCPMessage
|
||||
c_del_message chan *TCPMessage
|
||||
c_del_message chan *TCPMessage // Used for notifications about completed or expired messages
|
||||
|
||||
addr string
|
||||
port int
|
||||
addr string // IP to listen
|
||||
port int // Port to listen
|
||||
}
|
||||
|
||||
func RAWTCPListen(addr string, port int) (listener *RAWTCPListener) {
|
||||
@@ -24,14 +30,13 @@ func RAWTCPListen(addr string, port int) (listener *RAWTCPListener) {
|
||||
|
||||
listener.c_packets = make(chan *TCPPacket)
|
||||
listener.c_messages = make(chan *TCPMessage)
|
||||
listener.c_add_message = make(chan *TCPMessage)
|
||||
listener.c_del_message = make(chan *TCPMessage)
|
||||
|
||||
listener.addr = addr
|
||||
listener.port = port
|
||||
|
||||
go listener.listen()
|
||||
go listener.readTCPPackets()
|
||||
go listener.readRAWSocket()
|
||||
|
||||
return
|
||||
}
|
||||
@@ -39,21 +44,23 @@ func RAWTCPListen(addr string, port int) (listener *RAWTCPListener) {
|
||||
func (t *RAWTCPListener) listen() {
|
||||
for {
|
||||
select {
|
||||
// If message ready for deletion it means that its also complete or expired by timeout
|
||||
case message := <-t.c_del_message:
|
||||
t.deleteMessage(message)
|
||||
Debug("Deleted")
|
||||
t.c_messages <- message
|
||||
|
||||
// We need to use channgels to process each packet to avoid data races
|
||||
case packet := <-t.c_packets:
|
||||
t.processTCPPacket(packet)
|
||||
Debug("Processed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting messages that came from t.c_del_message channel
|
||||
func (t *RAWTCPListener) deleteMessage(message *TCPMessage) bool {
|
||||
var idx int = -1
|
||||
|
||||
// Searching for given message in messages buffer
|
||||
for i, m := range t.messages {
|
||||
if m.Ack == message.Ack {
|
||||
idx = i
|
||||
@@ -65,14 +72,17 @@ func (t *RAWTCPListener) deleteMessage(message *TCPMessage) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete element from array
|
||||
// Note: that this version for arrays that consist of pointers
|
||||
// https://code.google.com/p/go-wiki/wiki/SliceTricks
|
||||
copy(t.messages[idx:], t.messages[idx+1:])
|
||||
t.messages[len(t.messages)-1] = nil // or the zero value of T
|
||||
t.messages[len(t.messages)-1] = nil // Ensure that value will be garbage-collected.
|
||||
t.messages = t.messages[:len(t.messages)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *RAWTCPListener) readTCPPackets() {
|
||||
func (t *RAWTCPListener) readRAWSocket() {
|
||||
conn, e := net.ListenPacket("ip4:tcp", t.addr)
|
||||
defer conn.Close()
|
||||
|
||||
@@ -80,9 +90,10 @@ func (t *RAWTCPListener) readTCPPackets() {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
buf := make([]byte, 1500*2)
|
||||
buf := make([]byte, 4096)
|
||||
|
||||
for {
|
||||
// Note: ReadFrom receive messages without IP header
|
||||
n, _, err := conn.ReadFrom(buf)
|
||||
|
||||
if err != nil {
|
||||
@@ -90,20 +101,24 @@ func (t *RAWTCPListener) readTCPPackets() {
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
// To avoid full packet parsing every time, we manually parsing values needed for packet filtering
|
||||
// http://en.wikipedia.org/wiki/Transmission_Control_Protocol
|
||||
dest_port := binary.BigEndian.Uint16(buf[2:4])
|
||||
|
||||
// Because RAW_SOCKET can't be bound to port, we have to control it by ourself
|
||||
if int(dest_port) == t.port {
|
||||
// Check TCPPacket code for more description
|
||||
flags := binary.BigEndian.Uint16(buf[12:14]) & 0x1FF
|
||||
f_psh := (flags & TCP_PSH) != 0
|
||||
|
||||
// We need only packets with data inside
|
||||
// TCP PSH flag indicate that client should push data to buffer
|
||||
// TCP PSH flag indicate that packet have data inside
|
||||
if f_psh {
|
||||
// We should create new buffer because go slices is pointers. So buffer data shoud be immutable.
|
||||
new_buf := make([]byte, n)
|
||||
copy(new_buf, buf[:n])
|
||||
|
||||
// To avoid socket locking processing packet in new goroutine
|
||||
go func(buf []byte) {
|
||||
packet := NewTCPPacket(new_buf)
|
||||
t.c_packets <- packet
|
||||
@@ -114,10 +129,13 @@ func (t *RAWTCPListener) readTCPPackets() {
|
||||
}
|
||||
}
|
||||
|
||||
// Trying to add packet to existing message or creating new message
|
||||
//
|
||||
// For TCP message unique id is Acknowledgment number (see tcp_packet.go)
|
||||
func (t *RAWTCPListener) processTCPPacket(packet *TCPPacket) {
|
||||
var message *TCPMessage
|
||||
|
||||
// Searching for message with same Ack
|
||||
for _, msg := range t.messages {
|
||||
if msg.Ack == packet.Ack {
|
||||
message = msg
|
||||
@@ -126,12 +144,14 @@ func (t *RAWTCPListener) processTCPPacket(packet *TCPPacket) {
|
||||
}
|
||||
|
||||
if message == nil {
|
||||
// We sending c_del_message channel, so message object can communicate with Listener and notify it if message completed
|
||||
message = NewTCPMessage(packet.Ack, t.c_del_message)
|
||||
Debug("Adding message")
|
||||
|
||||
t.messages = append(t.messages, message)
|
||||
}
|
||||
|
||||
// Adding packet to message
|
||||
message.c_packets <- packet
|
||||
}
|
||||
|
||||
|
||||
+12
-9
@@ -12,12 +12,12 @@ const MSG_EXPIRE = 200 * time.Millisecond
|
||||
//
|
||||
// Each TCP Packet have 2 ids: acknowledgement - message_id, and sequence - packet_id
|
||||
// Message can be compiled from unique packets with same message_id which sorted by sequence
|
||||
// Message is received if we did't receive any packets for 200ms OR if we received packet with "fin" flag
|
||||
// Message is received if we did't receive any packets for 200ms
|
||||
type TCPMessage struct {
|
||||
Ack uint32 // Message ID
|
||||
packets []*TCPPacket
|
||||
|
||||
timer *time.Timer
|
||||
timer *time.Timer // Used for expire check
|
||||
|
||||
expired bool
|
||||
|
||||
@@ -32,21 +32,22 @@ func NewTCPMessage(Ack uint32, c_del chan *TCPMessage) (msg *TCPMessage) {
|
||||
|
||||
msg.c_packets = make(chan *TCPPacket)
|
||||
msg.c_closing = make(chan int)
|
||||
msg.c_del_message = c_del
|
||||
msg.c_del_message = c_del // used for notifying that message completed or expired
|
||||
|
||||
// Every time we receive packet we reset this timer
|
||||
msg.timer = time.AfterFunc(MSG_EXPIRE, msg.Timeout)
|
||||
|
||||
go msg.ListenPackets()
|
||||
go msg.listen()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TCPMessage) ListenPackets() {
|
||||
func (t *TCPMessage) listen() {
|
||||
for {
|
||||
select {
|
||||
case <-t.c_closing:
|
||||
close(t.c_packets)
|
||||
return
|
||||
return // Stop loop if message completed/expired
|
||||
case packet := <-t.c_packets:
|
||||
t.AddPacket(packet)
|
||||
}
|
||||
@@ -54,8 +55,8 @@ func (t *TCPMessage) ListenPackets() {
|
||||
}
|
||||
|
||||
func (t *TCPMessage) Timeout() {
|
||||
t.c_closing <- 1
|
||||
t.c_del_message <- t
|
||||
t.c_closing <- 1 // Notify to stop listen loop and close channel
|
||||
t.c_del_message <- t // Notify RAWListener that message is ready to be send to replay server
|
||||
}
|
||||
|
||||
// Sort packets in right orders and return message content
|
||||
@@ -77,7 +78,8 @@ func (t *TCPMessage) Bytes() (output []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
// Add packet to the message
|
||||
// Add packet to the message and ensure packet uniquiness
|
||||
// TCP allows that packet can be re-send multiple times
|
||||
func (t *TCPMessage) AddPacket(packet *TCPPacket) {
|
||||
if t.expired {
|
||||
Debug("Adding packet to expired message")
|
||||
@@ -99,5 +101,6 @@ func (t *TCPMessage) AddPacket(packet *TCPPacket) {
|
||||
t.packets = append(t.packets, packet)
|
||||
}
|
||||
|
||||
// Reset message timeout timer
|
||||
t.timer.Reset(MSG_EXPIRE)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ func NewRequestFactory() (factory *RequestFactory) {
|
||||
func (f *RequestFactory) sendRequest(host *ForwardHost, request *http.Request) {
|
||||
client := &http.Client{}
|
||||
|
||||
// Change HOST of original request
|
||||
URL := host.Url + request.URL.Path + "?" + request.URL.RawQuery
|
||||
|
||||
request.RequestURI = ""
|
||||
|
||||
Reference in New Issue
Block a user