diff --git a/examples/stackbasic/main.go b/examples/stackbasic/main.go index 01ea1f2..a299568 100644 --- a/examples/stackbasic/main.go +++ b/examples/stackbasic/main.go @@ -110,7 +110,7 @@ func doHTTP(conn *internet.TCPConn, hdr *httpraw.Header) error { return nil // No data yet. } fmt.Println("state is established; check request and send response") - _, err := hdr.ReadFromLimited(conn, hdr.Free()) + _, err := hdr.ReadFromLimited(conn, hdr.BufferFree()) if err != nil { return err } diff --git a/http/httpraw/header.go b/http/httpraw/header.go index d9f0af9..74d072c 100644 --- a/http/httpraw/header.go +++ b/http/httpraw/header.go @@ -119,7 +119,7 @@ func (h *Header) ReadFromLimited(r io.Reader, maxBytesToRead int) (int, error) { } else if h.flags.hasAny(flagMangledBuffer) { return 0, errMangledBuffer } - free := h.Free() + free := h.BufferFree() if free < maxBytesToRead { if h.flags.hasAny(flagNoBufferGrow) { return 0, errSmallBuffer @@ -145,7 +145,7 @@ func (h *Header) ReadFromBytes(b []byte) (int, error) { if len(b) == 0 { return 0, errSmallBuffer } - free := h.Free() + free := h.BufferFree() if free < len(b) { if h.flags.hasAny(flagNoBufferGrow) { return 0, errSmallBuffer @@ -156,13 +156,23 @@ func (h *Header) ReadFromBytes(b []byte) (int, error) { return len(b), nil } -// Free returns amount of bytes free in underlying buffer. -func (h *Header) Free() int { +// BufferParsed returns the amount of bytes parsed during a call to Parse* methods. +// If the Parse* method completed without error then BufferParsed returns the header's length including the final "\r\n\r\n" text. +// BufferParsed returns 0 if the buffer is invalid/mangled or if no header data has been parsed succesfully. +func (h *Header) BufferParsed() int { + if h.flags.hasAny(flagMangledBuffer | flagOOMReached) { + return 0 + } + return h.hbuf.off +} + +// BufferFree returns amount of bytes free in underlying buffer. +func (h *Header) BufferFree() int { return h.hbuf.free() } -// Capacity returns the total capacity of the underlying buffer. -func (h *Header) Capacity() int { +// BufferCapacity returns the total capacity of the underlying buffer. +func (h *Header) BufferCapacity() int { return cap(h.hbuf.buf) } diff --git a/internal/pcap/capture.go b/internal/pcap/capture.go index d053bdc..617483f 100644 --- a/internal/pcap/capture.go +++ b/internal/pcap/capture.go @@ -181,7 +181,7 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([ } func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) { - const protocol = "HTTP" + const httpProtocol = "HTTP" if bitOffset%8 != 0 { return nil, errors.New("HTTP must be parsed at byte boundary") } @@ -190,16 +190,32 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ( httpData := pkt[bitOffset/8:] pc.hdr.Reset(httpData) err := pc.hdr.Parse(asResponse) - if err == nil { - dst = append(dst, remainingFrameInfo(protocol, FieldClassText, bitOffset, len(pkt))) - return dst, nil + if err != nil { + pc.hdr.Reset(httpData) + err = pc.hdr.Parse(asRequest) // try as request. } - pc.hdr.Reset(httpData) - err = pc.hdr.Parse(asRequest) - if err == nil { - dst = append(dst, remainingFrameInfo(protocol, FieldClassText, bitOffset, len(pkt))) - return dst, nil + if err != nil { + return dst, err } + hdrLen := pc.hdr.BufferParsed() + body, _ := pc.hdr.Body() + dst = append(dst, Frame{ + Protocol: httpProtocol, + PacketBitOffset: bitOffset, + Fields: []FrameField{ + { + Name: "HTTP Header", + Class: FieldClassText, + FrameBitOffset: 0, + BitLength: hdrLen * octet, + }, + { + Class: FieldClassPayload, + FrameBitOffset: hdrLen * octet, + BitLength: len(body) * octet, + }, + }, + }) return dst, err } diff --git a/internal/pcap/capture_test.go b/internal/pcap/capture_test.go index 37f2fa9..6d99a22 100644 --- a/internal/pcap/capture_test.go +++ b/internal/pcap/capture_test.go @@ -15,6 +15,7 @@ import ( func TestCap(t *testing.T) { const mtu = 1500 + const httpBody = "{200,ok}" var buf [mtu]byte var gen ltesto.PacketGen rng := rand.New(rand.NewSource(1)) @@ -29,6 +30,8 @@ func TestCap(t *testing.T) { var hdr httpraw.Header hdr.SetStatus("200", "OK") hdr.Set("Cookie", "ABC=123") + pkt, _ = hdr.AppendResponse(pkt) + pkt = append(pkt, httpBody...) var pbreak PacketBreakdown frames, err := pbreak.CaptureEthernet(nil, pkt, 0) if err != nil { @@ -55,11 +58,19 @@ 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 + } + v, _ := frame.AppendField(nil, idx, pkt) + return v + } efrm, _ := ethernet.NewFrame(pkt) pefrm := frames[0] pifrm := frames[1] ptfrm := frames[2] - // phfrm := frames[3] + phfrm := frames[3] gotEproto := ethernet.Type(getClass(pefrm, FieldClassProto)) if gotEproto != efrm.EtherTypeOrSize() { t.Errorf("want %s ethernet type, got %s", efrm.EtherTypeOrSize().String(), gotEproto.String()) @@ -95,4 +106,8 @@ func TestCap(t *testing.T) { if gotHeaderLen != uint64(wantHeaderLen) { t.Errorf("want %d TCP header length, got %d", wantHeaderLen, gotHeaderLen) } + gotBody := getClassData(phfrm, FieldClassPayload) + if string(gotBody) != httpBody { + t.Errorf("want %q HTTP body, got %q", httpBody, gotBody) + } }