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.
This commit is contained in:
Acts1631
2026-07-28 17:32:00 +01:00
committed by GitHub
parent 19f8b69518
commit 439e518bda
2 changed files with 24 additions and 0 deletions
+8
View File
@@ -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
}
+16
View File
@@ -57,6 +57,22 @@ func TestInit(t *testing.T) {
startServer(t, f)
}
func TestServiceControlRejectsOversizedBody(t *testing.T) {
body := `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>` +
`<u:RegisterDevice xmlns:u="urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1">` +
strings.Repeat("x", maxSOAPBodySize) +
`</u:RegisterDevice></s:Body></s:Envelope>`
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)