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": "passwd.pcap", `C:\\x\\y.pcap`: "y.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(Config{MaxUploadBytes: 1 << 20}) 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) } }