Add hex2pcap

This commit is contained in:
2026-10-05 20:07:31 +02:00
parent 477e6c0ee7
commit 5b5cf211d0
19 changed files with 1694 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
package web
import (
"bytes"
"encoding/binary"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"pcap-web-tools/internal/pcap"
)
func TestBuildPacketConcatenatesWithoutFix(t *testing.T) {
res, err := buildPacket(hex2pcapForm{L2: "aa bb", L3: "cc", L7: "dd:ee"})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(res.Packet, []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee}) {
t.Errorf("packet %x", res.Packet)
}
if res.LinkType != pcap.LinkTypeEthernet || res.Fixed {
t.Errorf("link type %d fixed %v", res.LinkType, res.Fixed)
}
}
func TestBuildPacketLinkType(t *testing.T) {
tests := []struct {
form hex2pcapForm
want uint32
}{
{hex2pcapForm{L3: "45"}, pcap.LinkTypeRaw},
{hex2pcapForm{L2: "00", L3: "45"}, pcap.LinkTypeEthernet},
{hex2pcapForm{L7: "00"}, pcap.LinkTypeEthernet},
{hex2pcapForm{L3: "45", LinkType: "113"}, pcap.LinkTypeLinuxSLL},
{hex2pcapForm{L7: "00", LinkType: "custom", CustomLinkType: "228"}, 228},
{hex2pcapForm{L7: "00", LinkType: "custom", CustomLinkType: "0x93"}, 147},
}
for _, tt := range tests {
res, err := buildPacket(tt.form)
if err != nil {
t.Errorf("%+v: %v", tt.form, err)
continue
}
if res.LinkType != tt.want {
t.Errorf("%+v: link type %d, want %d", tt.form, res.LinkType, tt.want)
}
}
}
func TestBuildPacketFixWholeFrameInPayload(t *testing.T) {
// Full Ethernet + IPv4 + ICMP frame pasted into the payload field only.
res, err := buildPacket(hex2pcapForm{
L7: "00112233445566778899aabb0800 45000000000100004001 0000 c0a8000108080808 0800 0000 abcd0001",
Fix: true,
})
if err != nil {
t.Fatal(err)
}
if res.IPOffset != 14 {
t.Errorf("IP offset %d, want 14", res.IPOffset)
}
if got := binary.BigEndian.Uint16(res.Packet[14+2:]); got != 28 {
t.Errorf("IPv4 total length %d, want 28", got)
}
}
func TestBuildPacketFixUsesLayer3Boundary(t *testing.T) {
// IPv4 with a 4 byte option and TCP with 12 bytes of options in layer 3:
// the TCP data offset is set from where the layer 3 field ends.
res, err := buildPacket(hex2pcapForm{
L3: "46000000123440004006 0000 0a0000010a000002 94040000 c350005000000001000000000018ffff00000000 0101080a0000000100000000",
L7: "474554202f",
Fix: true,
})
if err != nil {
t.Fatal(err)
}
if got := res.Packet[24+12] >> 4; got != 8 {
t.Errorf("TCP data offset %d, want 8", got)
}
}
func TestBuildPacketErrors(t *testing.T) {
for name, f := range map[string]hex2pcapForm{
"empty": {},
"bad hex": {L3: "4g"},
"fix non-IP": {L7: "ffffffffffff 66778899aabb 0806 0001", Fix: true},
"bad custom": {L7: "00", LinkType: "custom", CustomLinkType: "x"},
"fix not IP hdr": {L3: "00", Fix: true},
} {
if _, err := buildPacket(f); err == nil {
t.Errorf("%s: expected error", name)
}
}
}
func TestPcapFilename(t *testing.T) {
for in, want := range map[string]string{
"": "packet.pcap",
" ": "packet.pcap",
"dns query": "dns_query.pcap",
"x.pcap": "x.pcap",
"../etc/passwd": ".._etc_passwd.pcap",
`a"b`: "a_b.pcap",
} {
if got := pcapFilename(in); got != want {
t.Errorf("pcapFilename(%q) = %q, want %q", in, got, want)
}
}
}
func TestHandlers(t *testing.T) {
srv, err := New()
if err != nil {
t.Fatal(err)
}
for _, path := range []string{"/", "/hex2pcap", "/hex2pcap?example", "/static/style.css"} {
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, httptest.NewRequest("GET", path, nil))
if rec.Code != http.StatusOK {
t.Errorf("GET %s: %d", path, rec.Code)
}
}
post := func(v url.Values) *httptest.ResponseRecorder {
req := httptest.NewRequest("POST", "/hex2pcap", strings.NewReader(v.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, req)
return rec
}
rec := post(url.Values{"l3": {"4500"}, "l7": {"aabb"}, "action": {"download"}, "filename": {"test"}})
if rec.Code != http.StatusOK {
t.Fatalf("download: %d %s", rec.Code, rec.Body)
}
if cd := rec.Header().Get("Content-Disposition"); cd != `attachment; filename="test.pcap"` {
t.Errorf("Content-Disposition %q", cd)
}
body := rec.Body.Bytes()
if len(body) != 24+16+4 || binary.LittleEndian.Uint32(body[20:]) != pcap.LinkTypeRaw {
t.Errorf("pcap body %x", body)
}
rec = post(url.Values{"l3": {"4500"}, "action": {"preview"}})
if rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), "Hex dump") {
t.Errorf("preview: %d", rec.Code)
}
rec = post(url.Values{"l3": {"zz"}, "action": {"download"}})
if rec.Code != http.StatusBadRequest || !strings.Contains(rec.Body.String(), "invalid character") {
t.Errorf("bad hex: %d", rec.Code)
}
}