Add a reorderpcap tool
This commit is contained in:
@@ -23,6 +23,7 @@ type Tool struct {
|
||||
var tools = []Tool{
|
||||
{Name: "Hex → pcap", Path: "/hex2pcap", Description: "Build a pcap file from hex-encoded layer 2, layer 3 and payload bytes, optionally fixing IP lengths and checksums."},
|
||||
{Name: "Split pcap", Path: "/split", Description: "Split a pcap file into several files by packet count or file size, downloaded as a zip."},
|
||||
{Name: "Reorder pcap", Path: "/reorder", Description: "Sort the packets of a pcap file by timestamp, like reordercap."},
|
||||
}
|
||||
|
||||
// Config holds server settings.
|
||||
@@ -43,7 +44,7 @@ func New(cfg Config) (*Server, error) {
|
||||
|
||||
// Each page is parsed together with the shared layout so pages can define
|
||||
// their own "title" and "content" blocks.
|
||||
for _, page := range []string{"index.html", "hex2pcap.html", "split.html"} {
|
||||
for _, page := range []string{"index.html", "hex2pcap.html", "split.html", "reorder.html"} {
|
||||
t, err := template.New(page).ParseFS(assets, "templates/layout.html", "templates/"+page)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,6 +62,8 @@ func New(cfg Config) (*Server, error) {
|
||||
s.mux.HandleFunc("POST /hex2pcap", s.handleHex2Pcap)
|
||||
s.mux.HandleFunc("GET /split", s.handleSplitForm)
|
||||
s.mux.HandleFunc("POST /split", s.handleSplit)
|
||||
s.mux.HandleFunc("GET /reorder", s.handleReorderForm)
|
||||
s.mux.HandleFunc("POST /reorder", s.handleReorder)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
||||
+6
-22
@@ -9,7 +9,6 @@ import (
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -50,7 +49,7 @@ func (s *Server) splitPage(f splitForm) splitPage {
|
||||
return splitPage{
|
||||
Form: f,
|
||||
Units: units,
|
||||
MaxUpload: fmt.Sprintf("%d MB", s.cfg.MaxUploadBytes/1e6),
|
||||
MaxUpload: s.maxUpload(),
|
||||
MaxParts: maxSplitParts,
|
||||
}
|
||||
}
|
||||
@@ -67,19 +66,12 @@ func (s *Server) handleSplit(w http.ResponseWriter, r *http.Request) {
|
||||
s.render(w, status, "split.html", "/split", page)
|
||||
}
|
||||
|
||||
// Allow some slack over the file size limit for the multipart framing and form fields.
|
||||
r.Body = http.MaxBytesReader(w, r.Body, s.cfg.MaxUploadBytes+64<<10)
|
||||
// Uploads bigger than 1 MB are spooled to temporary files rather than kept in memory.
|
||||
if err := r.ParseMultipartForm(1 << 20); err != nil {
|
||||
var tooBig *http.MaxBytesError
|
||||
if errors.As(err, &tooBig) {
|
||||
fail(http.StatusRequestEntityTooLarge, "file too large; the maximum upload is "+page.MaxUpload)
|
||||
} else {
|
||||
fail(http.StatusBadRequest, "bad upload: "+err.Error())
|
||||
}
|
||||
file, hdr, status, msg := s.parseUpload(w, r)
|
||||
if status != 0 {
|
||||
fail(status, msg)
|
||||
return
|
||||
}
|
||||
defer r.MultipartForm.RemoveAll()
|
||||
defer file.Close()
|
||||
|
||||
page.Form = splitForm{
|
||||
Mode: r.FormValue("mode"),
|
||||
@@ -92,13 +84,6 @@ func (s *Server) handleSplit(w http.ResponseWriter, r *http.Request) {
|
||||
fail(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
file, hdr, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
fail(http.StatusBadRequest, "choose a pcap file to split")
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// First pass: read the whole file and count the parts, so problems are
|
||||
// reported on the page before any of the zip is sent.
|
||||
st, err := pcap.Split(file, limit, func(part int) (io.Writer, error) {
|
||||
@@ -116,8 +101,7 @@ func (s *Server) handleSplit(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
name := safeName(hdr.Filename, "capture.pcap")
|
||||
base := strings.TrimSuffix(name, path.Ext(name))
|
||||
base := uploadStem(hdr)
|
||||
digits := max(3, len(strconv.Itoa(st.Parts)))
|
||||
|
||||
w.Header().Set("Content-Type", "application/zip")
|
||||
|
||||
@@ -28,7 +28,7 @@ func testPcap(t *testing.T, packets int) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func postSplit(t *testing.T, srv *Server, filename string, file []byte, fields map[string]string) *httptest.ResponseRecorder {
|
||||
func postUpload(t *testing.T, srv *Server, target, filename string, file []byte, fields map[string]string) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
var body bytes.Buffer
|
||||
mw := multipart.NewWriter(&body)
|
||||
@@ -43,7 +43,7 @@ func postSplit(t *testing.T, srv *Server, filename string, file []byte, fields m
|
||||
fw.Write(file)
|
||||
}
|
||||
mw.Close()
|
||||
req := httptest.NewRequest("POST", "/split", &body)
|
||||
req := httptest.NewRequest("POST", target, &body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
rec := httptest.NewRecorder()
|
||||
srv.ServeHTTP(rec, req)
|
||||
@@ -68,7 +68,7 @@ func TestSplitHandler(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rec := postSplit(t, srv, `C:\caps\my capture.pcap`, in, tt.fields)
|
||||
rec := postUpload(t, srv, "/split", `C:\caps\my capture.pcap`, in, tt.fields)
|
||||
if rec.Code != http.StatusOK || rec.Header().Get("Content-Type") != "application/zip" {
|
||||
t.Fatalf("status %d %s: %s", rec.Code, rec.Header().Get("Content-Type"), rec.Body)
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func TestSplitHandlerErrors(t *testing.T) {
|
||||
{"no mode", testPcap(t, 1), map[string]string{}, http.StatusBadRequest, "choose whether"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
rec := postSplit(t, srv, "x.pcap", tt.file, tt.fields)
|
||||
rec := postUpload(t, srv, "/split", "x.pcap", tt.file, tt.fields)
|
||||
if rec.Code != tt.status || !strings.Contains(rec.Body.String(), tt.want) {
|
||||
t.Errorf("%s: status %d, body %.300s", tt.name, rec.Code, rec.Body)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user