dlna: fix SOAP action header parsing - fixes #6354

Changes in github.com/anacrolix/dms changed upnp.ServiceURN to include a
namespace identifier. This identifier was previously hardcoded, but is
now parsed out of the URN. The old SOAP action header parsing logic was
duplicated in rclone and did not handle this field. Resulting responses
included a URN with an empty namespace identifier, breaking clients.
This commit is contained in:
Joram Schrijver 2022-08-06 02:59:00 +02:00 committed by Nick Craig-Wood
parent 5788d3fc29
commit f675da9053
3 changed files with 3 additions and 35 deletions

View File

@ -186,7 +186,7 @@ func (s *server) rootDescHandler(w http.ResponseWriter, r *http.Request) {
// Handle a service control HTTP request. // Handle a service control HTTP request.
func (s *server) serviceControlHandler(w http.ResponseWriter, r *http.Request) { func (s *server) serviceControlHandler(w http.ResponseWriter, r *http.Request) {
soapActionString := r.Header.Get("SOAPACTION") soapActionString := r.Header.Get("SOAPACTION")
soapAction, err := parseActionHTTPHeader(soapActionString) soapAction, err := upnp.ParseActionHTTPHeader(soapActionString)
if err != nil { if err != nil {
serveError(s, w, "Could not parse SOAPACTION header", err) serveError(s, w, "Could not parse SOAPACTION header", err)
return return

View File

@ -119,6 +119,8 @@ func TestContentDirectoryBrowseMetadata(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err) require.NoError(t, err)
// should contain an appropriate URN
require.Contains(t, string(body), "urn:schemas-upnp-org:service:ContentDirectory:1")
// expect a <container> element // expect a <container> element
require.Contains(t, string(body), html.EscapeString("<container ")) require.Contains(t, string(body), html.EscapeString("<container "))
require.NotContains(t, string(body), html.EscapeString("<item ")) require.NotContains(t, string(body), html.EscapeString("<item "))

View File

@ -3,7 +3,6 @@ package dlna
import ( import (
"crypto/md5" "crypto/md5"
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -12,9 +11,6 @@ import (
"net/http/httptest" "net/http/httptest"
"net/http/httputil" "net/http/httputil"
"os" "os"
"regexp"
"strconv"
"strings"
"github.com/anacrolix/dms/soap" "github.com/anacrolix/dms/soap"
"github.com/anacrolix/dms/upnp" "github.com/anacrolix/dms/upnp"
@ -89,36 +85,6 @@ func marshalSOAPResponse(sa upnp.SoapAction, args map[string]string) []byte {
sa.Action, sa.ServiceURN.String(), mustMarshalXML(soapArgs))) sa.Action, sa.ServiceURN.String(), mustMarshalXML(soapArgs)))
} }
var serviceURNRegexp = regexp.MustCompile(`:service:(\w+):(\d+)$`)
func parseServiceType(s string) (ret upnp.ServiceURN, err error) {
matches := serviceURNRegexp.FindStringSubmatch(s)
if matches == nil {
err = errors.New(s)
return
}
if len(matches) != 3 {
log.Panicf("Invalid serviceURNRegexp ?")
}
ret.Type = matches[1]
ret.Version, err = strconv.ParseUint(matches[2], 0, 0)
return
}
func parseActionHTTPHeader(s string) (ret upnp.SoapAction, err error) {
if s[0] != '"' || s[len(s)-1] != '"' {
return
}
s = s[1 : len(s)-1]
hashIndex := strings.LastIndex(s, "#")
if hashIndex == -1 {
return
}
ret.Action = s[hashIndex+1:]
ret.ServiceURN, err = parseServiceType(s[:hashIndex])
return
}
type loggingResponseWriter struct { type loggingResponseWriter struct {
http.ResponseWriter http.ResponseWriter
request *http.Request request *http.Request