mirror of
https://github.com/soypat/lneto.git
synced 2026-08-27 09:59:04 +00:00
improve HTTP body pcap formatting
This commit is contained in:
@@ -2,6 +2,7 @@ package pcap
|
|||||||
|
|
||||||
//go:generate stringer -type=FieldClass -linecomment -output stringers.go .
|
//go:generate stringer -type=FieldClass -linecomment -output stringers.go .
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -554,6 +555,39 @@ func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int)
|
|||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// httpBodyClass returns FieldClassText if the HTTP body appears to be human-readable text
|
||||||
|
// based on the Content-Type header, falling back to byte inspection when Content-Type is absent.
|
||||||
|
func httpBodyClass(contentType, body []byte) FieldClass {
|
||||||
|
if len(contentType) > 0 {
|
||||||
|
// Strip parameters (e.g. "; charset=utf-8") for media type matching.
|
||||||
|
mediaType := contentType
|
||||||
|
if i := bytes.IndexByte(contentType, ';'); i >= 0 {
|
||||||
|
mediaType = contentType[:i]
|
||||||
|
}
|
||||||
|
mediaType = bytes.TrimSpace(mediaType)
|
||||||
|
switch {
|
||||||
|
case bytes.HasPrefix(mediaType, []byte("text/")):
|
||||||
|
return FieldClassText
|
||||||
|
case bytes.Equal(mediaType, []byte("application/json")),
|
||||||
|
bytes.Equal(mediaType, []byte("application/javascript")),
|
||||||
|
bytes.Equal(mediaType, []byte("application/xml")):
|
||||||
|
return FieldClassText
|
||||||
|
case bytes.HasSuffix(mediaType, []byte("+json")),
|
||||||
|
bytes.HasSuffix(mediaType, []byte("+xml")):
|
||||||
|
return FieldClassText
|
||||||
|
}
|
||||||
|
return FieldClassPayload
|
||||||
|
}
|
||||||
|
// No Content-Type: inspect bytes for printable ASCII.
|
||||||
|
for _, c := range body {
|
||||||
|
if c >= 32 && c <= 126 || c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return FieldClassPayload
|
||||||
|
}
|
||||||
|
return FieldClassText
|
||||||
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
const httpProtocol = "HTTP"
|
const httpProtocol = "HTTP"
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
@@ -573,6 +607,7 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
}
|
}
|
||||||
hdrLen := pc.hdr.BufferParsed()
|
hdrLen := pc.hdr.BufferParsed()
|
||||||
body, _ := pc.hdr.Body()
|
body, _ := pc.hdr.Body()
|
||||||
|
bodyClass := httpBodyClass(pc.hdr.Get("Content-Type"), body)
|
||||||
dst = append(dst, Frame{
|
dst = append(dst, Frame{
|
||||||
Protocol: httpProtocol,
|
Protocol: httpProtocol,
|
||||||
PacketBitOffset: bitOffset,
|
PacketBitOffset: bitOffset,
|
||||||
@@ -584,7 +619,8 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
BitLength: hdrLen * octet,
|
BitLength: hdrLen * octet,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Class: FieldClassPayload,
|
Name: "HTTP Body",
|
||||||
|
Class: bodyClass,
|
||||||
FrameBitOffset: hdrLen * octet,
|
FrameBitOffset: hdrLen * octet,
|
||||||
BitLength: len(body) * octet,
|
BitLength: len(body) * octet,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -85,13 +85,14 @@ func TestCap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return math.MaxUint64
|
return math.MaxUint64
|
||||||
}
|
}
|
||||||
getClassData := func(frame Frame, class FieldClass) []byte {
|
getNameData := func(frame Frame, name string) []byte {
|
||||||
idx, err := frame.FieldByClass(class)
|
for i := range frame.Fields {
|
||||||
if err != nil {
|
if frame.Fields[i].Name == name {
|
||||||
return nil
|
v, _ := frame.AppendField(nil, i, pkt)
|
||||||
|
return v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
v, _ := frame.AppendField(nil, idx, pkt)
|
return nil
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
efrm, _ := ethernet.NewFrame(pkt)
|
efrm, _ := ethernet.NewFrame(pkt)
|
||||||
pefrm := frames[0]
|
pefrm := frames[0]
|
||||||
@@ -143,11 +144,11 @@ func TestCap(t *testing.T) {
|
|||||||
if gotHeaderLen != uint64(wantHeaderLen) {
|
if gotHeaderLen != uint64(wantHeaderLen) {
|
||||||
t.Errorf("want %d TCP header length, got %d", wantHeaderLen, gotHeaderLen)
|
t.Errorf("want %d TCP header length, got %d", wantHeaderLen, gotHeaderLen)
|
||||||
}
|
}
|
||||||
gotHttpHeader := string(getClassData(phfrm, FieldClassText))
|
gotHttpHeader := string(getNameData(phfrm, "HTTP Header"))
|
||||||
if !strings.HasPrefix(gotHttpHeader, httpProtocol) {
|
if !strings.HasPrefix(gotHttpHeader, httpProtocol) {
|
||||||
t.Errorf("want HTTP header starting with %q, got %q", httpProtocol, gotHttpHeader)
|
t.Errorf("want HTTP header starting with %q, got %q", httpProtocol, gotHttpHeader)
|
||||||
}
|
}
|
||||||
gotBody := getClassData(phfrm, FieldClassPayload)
|
gotBody := getNameData(phfrm, "HTTP Body")
|
||||||
if string(gotBody) != httpBody {
|
if string(gotBody) != httpBody {
|
||||||
t.Errorf("want %q HTTP body, got %q", httpBody, gotBody)
|
t.Errorf("want %q HTTP body, got %q", httpBody, gotBody)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user