mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +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 .
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -554,6 +555,39 @@ func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int)
|
||||
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) {
|
||||
const httpProtocol = "HTTP"
|
||||
if bitOffset%8 != 0 {
|
||||
@@ -573,6 +607,7 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
||||
}
|
||||
hdrLen := pc.hdr.BufferParsed()
|
||||
body, _ := pc.hdr.Body()
|
||||
bodyClass := httpBodyClass(pc.hdr.Get("Content-Type"), body)
|
||||
dst = append(dst, Frame{
|
||||
Protocol: httpProtocol,
|
||||
PacketBitOffset: bitOffset,
|
||||
@@ -584,7 +619,8 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
||||
BitLength: hdrLen * octet,
|
||||
},
|
||||
{
|
||||
Class: FieldClassPayload,
|
||||
Name: "HTTP Body",
|
||||
Class: bodyClass,
|
||||
FrameBitOffset: hdrLen * octet,
|
||||
BitLength: len(body) * octet,
|
||||
},
|
||||
|
||||
@@ -85,13 +85,14 @@ func TestCap(t *testing.T) {
|
||||
}
|
||||
return math.MaxUint64
|
||||
}
|
||||
getClassData := func(frame Frame, class FieldClass) []byte {
|
||||
idx, err := frame.FieldByClass(class)
|
||||
if err != nil {
|
||||
return nil
|
||||
getNameData := func(frame Frame, name string) []byte {
|
||||
for i := range frame.Fields {
|
||||
if frame.Fields[i].Name == name {
|
||||
v, _ := frame.AppendField(nil, i, pkt)
|
||||
return v
|
||||
}
|
||||
}
|
||||
v, _ := frame.AppendField(nil, idx, pkt)
|
||||
return v
|
||||
return nil
|
||||
}
|
||||
efrm, _ := ethernet.NewFrame(pkt)
|
||||
pefrm := frames[0]
|
||||
@@ -143,11 +144,11 @@ func TestCap(t *testing.T) {
|
||||
if gotHeaderLen != uint64(wantHeaderLen) {
|
||||
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) {
|
||||
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 {
|
||||
t.Errorf("want %q HTTP body, got %q", httpBody, gotBody)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user