mirror of
https://github.com/maride/pancap.git
synced 2024-11-22 00:44:26 +00:00
Add support for HTTP packets
This commit is contained in:
parent
56b493283e
commit
cd01dc7664
52
protocol/http/http.go
Normal file
52
protocol/http/http.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.darknebu.la/maride/pancap/common"
|
||||||
|
"git.darknebu.la/maride/pancap/output"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/tcpassembly"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Protocol struct {
|
||||||
|
initialized bool
|
||||||
|
requestFactory *httpRequestFactory
|
||||||
|
responseFactory *httpResponseFactory
|
||||||
|
requestPool *tcpassembly.StreamPool
|
||||||
|
responsePool *tcpassembly.StreamPool
|
||||||
|
requestAssembler *tcpassembly.Assembler
|
||||||
|
responseAssembler *tcpassembly.Assembler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the given packet is an HTTP packet we can process
|
||||||
|
func (p *Protocol) CanAnalyze(packet gopacket.Packet) bool {
|
||||||
|
return packet.Layer(layers.LayerTypeTCP) != nil && packet.Layer(layers.LayerTypeTLS) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyzes the given HTTP packet
|
||||||
|
func (p *Protocol) Analyze(packet gopacket.Packet) error {
|
||||||
|
// Check if we need to init
|
||||||
|
if !p.initialized {
|
||||||
|
// Initialize
|
||||||
|
p.requestFactory = &httpRequestFactory{}
|
||||||
|
p.responseFactory = &httpResponseFactory{}
|
||||||
|
p.requestPool = tcpassembly.NewStreamPool(p.requestFactory)
|
||||||
|
p.responsePool = tcpassembly.NewStreamPool(p.responseFactory)
|
||||||
|
p.requestAssembler = tcpassembly.NewAssembler(p.requestPool)
|
||||||
|
p.responseAssembler = tcpassembly.NewAssembler(p.responsePool)
|
||||||
|
p.initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to cast packet and assemble HTTP stream
|
||||||
|
tcp := packet.TransportLayer().(*layers.TCP)
|
||||||
|
p.requestAssembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
|
||||||
|
p.responseAssembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a summary after all packets are processed
|
||||||
|
func (p *Protocol) PrintSummary() {
|
||||||
|
output.PrintBlock("HTTP Requests", common.GenerateTree(requestSummaryLines))
|
||||||
|
output.PrintBlock("HTTP Responses", common.GenerateTree(responseSummaryLines))
|
||||||
|
}
|
67
protocol/http/httpRequestFactory.go
Normal file
67
protocol/http/httpRequestFactory.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/tcpassembly"
|
||||||
|
"github.com/google/gopacket/tcpassembly/tcpreader"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
requestSummaryLines []string
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpRequestFactory struct{}
|
||||||
|
|
||||||
|
type httpRequestStream struct {
|
||||||
|
net, transport gopacket.Flow
|
||||||
|
r tcpreader.ReaderStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new HTTPRequestStream for the given packet flow, and analyzes it in a separate thread
|
||||||
|
func (h *httpRequestFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
|
||||||
|
hstream := &httpRequestStream{
|
||||||
|
net: net,
|
||||||
|
transport: transport,
|
||||||
|
r: tcpreader.NewReaderStream(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start analyzer as thread and return TCP reader stream
|
||||||
|
go hstream.run()
|
||||||
|
return &hstream.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyzes the given request
|
||||||
|
func (h *httpRequestStream) run() {
|
||||||
|
iobuf := bufio.NewReader(&h.r)
|
||||||
|
|
||||||
|
for {
|
||||||
|
req, reqErr := http.ReadRequest(iobuf)
|
||||||
|
|
||||||
|
if reqErr == io.EOF {
|
||||||
|
// That's ok, we can ignore EOF errors
|
||||||
|
return
|
||||||
|
} else if reqErr != nil {
|
||||||
|
// Ignore, because it may be a response
|
||||||
|
} else {
|
||||||
|
// Try to process assembled request
|
||||||
|
tcpreader.DiscardBytesToEOF(req.Body)
|
||||||
|
req.Body.Close()
|
||||||
|
|
||||||
|
// Build summary
|
||||||
|
line := fmt.Sprintf("Request %s http://%s%s", req.Method, req.Host, req.RequestURI)
|
||||||
|
requestSummaryLines = append(requestSummaryLines, line)
|
||||||
|
|
||||||
|
// Check for file uploads
|
||||||
|
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
||||||
|
for k, v := range req.MultipartForm.File {
|
||||||
|
log.Println(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
protocol/http/httpResponseFactory.go
Normal file
59
protocol/http/httpResponseFactory.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/tcpassembly"
|
||||||
|
"github.com/google/gopacket/tcpassembly/tcpreader"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
responseSummaryLines []string
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpResponseFactory struct{}
|
||||||
|
|
||||||
|
type httpResponseStream struct {
|
||||||
|
net, transport gopacket.Flow
|
||||||
|
r tcpreader.ReaderStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new HTTPResponseStream for the given packet flow, and analyzes it in a separate thread
|
||||||
|
func (h *httpResponseFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
|
||||||
|
hstream := &httpResponseStream{
|
||||||
|
net: net,
|
||||||
|
transport: transport,
|
||||||
|
r: tcpreader.NewReaderStream(),
|
||||||
|
}
|
||||||
|
go hstream.run() // Important... we must guarantee that data from the reader stream is read.
|
||||||
|
|
||||||
|
// ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
|
||||||
|
return &hstream.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyzes the given response
|
||||||
|
func (h *httpResponseStream) run() {
|
||||||
|
iobuf := bufio.NewReader(&h.r)
|
||||||
|
|
||||||
|
for {
|
||||||
|
resp, respErr := http.ReadResponse(iobuf, nil)
|
||||||
|
|
||||||
|
if respErr == io.EOF {
|
||||||
|
// That's ok, we can ignore EOF errors
|
||||||
|
return
|
||||||
|
} else if respErr != nil {
|
||||||
|
// Ignore, because it may be a request
|
||||||
|
} else {
|
||||||
|
// Try to process assembled request
|
||||||
|
tcpreader.DiscardBytesToEOF(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
// Build summary
|
||||||
|
line := fmt.Sprintf("Response %s, Type %s, Size %d bytes", resp.Status, resp.Header.Get("Content-Type"), resp.ContentLength)
|
||||||
|
responseSummaryLines = append(responseSummaryLines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"git.darknebu.la/maride/pancap/protocol/arp"
|
"git.darknebu.la/maride/pancap/protocol/arp"
|
||||||
"git.darknebu.la/maride/pancap/protocol/dhcpv4"
|
"git.darknebu.la/maride/pancap/protocol/dhcpv4"
|
||||||
"git.darknebu.la/maride/pancap/protocol/dns"
|
"git.darknebu.la/maride/pancap/protocol/dns"
|
||||||
|
"git.darknebu.la/maride/pancap/protocol/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -11,5 +12,6 @@ var (
|
|||||||
&arp.Protocol{},
|
&arp.Protocol{},
|
||||||
&dhcpv4.Protocol{},
|
&dhcpv4.Protocol{},
|
||||||
&dns.Protocol{},
|
&dns.Protocol{},
|
||||||
|
&http.Protocol{},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user