Add support for HTTP packets

This commit is contained in:
maride 2020-01-08 11:48:53 +01:00
parent 56b493283e
commit cd01dc7664
4 changed files with 180 additions and 0 deletions

52
protocol/http/http.go Normal file
View 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))
}

View 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)
}
}
}
}
}

View 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)
}
}
}

View File

@ -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{},
} }
) )