From 439e518bda671e55d0d83b9ce2ac0e0f359ecd89 Mon Sep 17 00:00:00 2001 From: Acts1631 <69813585+acts-1631@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:32:00 -0400 Subject: [PATCH] serve dlna: bound SOAP request bodies The unauthenticated DLNA control endpoint decoded arbitrary SOAP bodies into an in-memory XML field. A LAN client could send a large request and exhaust the server's memory. Limit SOAP request bodies to 1 MiB and return 413 when the limit is exceeded. --- cmd/serve/dlna/dlna.go | 8 ++++++++ cmd/serve/dlna/dlna_test.go | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/cmd/serve/dlna/dlna.go b/cmd/serve/dlna/dlna.go index bfa4a464b..d24200b75 100644 --- a/cmd/serve/dlna/dlna.go +++ b/cmd/serve/dlna/dlna.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "encoding/xml" + "errors" "fmt" "net" "net/http" @@ -148,6 +149,7 @@ const ( rootDescPath = "/rootDesc.xml" resPath = "/r/" serviceControlURL = "/ctl" + maxSOAPBodySize = 1 << 20 ) type server struct { @@ -303,7 +305,13 @@ func (s *server) serviceControlHandler(w http.ResponseWriter, r *http.Request) { return } var env soap.Envelope + r.Body = http.MaxBytesReader(w, r.Body, maxSOAPBodySize) if err := xml.NewDecoder(r.Body).Decode(&env); err != nil { + var maxBytesErr *http.MaxBytesError + if errors.As(err, &maxBytesErr) { + http.Error(w, "SOAP request body too large", http.StatusRequestEntityTooLarge) + return + } serveError(ctx, s, w, "Could not parse SOAP request body", err) return } diff --git a/cmd/serve/dlna/dlna_test.go b/cmd/serve/dlna/dlna_test.go index cfef1a555..0a4bc738a 100644 --- a/cmd/serve/dlna/dlna_test.go +++ b/cmd/serve/dlna/dlna_test.go @@ -57,6 +57,22 @@ func TestInit(t *testing.T) { startServer(t, f) } +func TestServiceControlRejectsOversizedBody(t *testing.T) { + body := `` + + `` + + strings.Repeat("x", maxSOAPBodySize) + + `` + req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(body)) + require.NoError(t, err) + req.Header.Set("SOAPACTION", `"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1#RegisterDevice"`) + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer func() { + require.NoError(t, resp.Body.Close()) + }() + assert.Equal(t, http.StatusRequestEntityTooLarge, resp.StatusCode) +} + // Make sure that it serves rootDesc.xml (SCPD in uPnP parlance). func TestRootSCPD(t *testing.T) { req, err := http.NewRequest("GET", baseURL+rootDescPath, nil)