Skip to content

Commit db334b3

Browse files
authored
server: fix crash with invalid SETUP request (bluenviron/mediamtx#4025) (#652)
1 parent fb61e9a commit db334b3

File tree

3 files changed

+92
-39
lines changed

3 files changed

+92
-39
lines changed

pkg/liberrors/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,12 @@ func (e ErrServerStreamClosed) Error() string {
261261
return "stream is closed"
262262
}
263263

264-
// ErrServerPathNoSlash is an error that can be returned by a server.
265-
type ErrServerPathNoSlash struct{}
264+
// ErrServerInvalidSetupPath is an error that can be returned by a server.
265+
type ErrServerInvalidSetupPath struct{}
266266

267267
// Error implements the error interface.
268-
func (ErrServerPathNoSlash) Error() string {
269-
return "path of a SETUP request must end with a slash. " +
268+
func (ErrServerInvalidSetupPath) Error() string {
269+
return "invalid SETUP path. " +
270270
"This typically happens when VLC fails a request, and then switches to an " +
271271
"unsupported RTSP dialect"
272272
}

server_play_test.go

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -174,38 +174,38 @@ func TestServerPlayPath(t *testing.T) {
174174
path string
175175
}{
176176
{
177-
"normal",
177+
"standard",
178178
"rtsp://localhost:8554/teststream[control]",
179179
"rtsp://localhost:8554/teststream/",
180180
"/teststream",
181181
},
182182
{
183-
"with query, ffmpeg format",
183+
"no query, ffmpeg format",
184184
"rtsp://localhost:8554/teststream?testing=123[control]",
185185
"rtsp://localhost:8554/teststream/",
186186
"/teststream",
187187
},
188188
{
189-
"with query, gstreamer format",
189+
"no query, gstreamer format",
190190
"rtsp://localhost:8554/teststream[control]?testing=123",
191191
"rtsp://localhost:8554/teststream/",
192192
"/teststream",
193193
},
194194
{
195195
// this is needed to support reading mpegts with ffmpeg
196-
"without media id",
196+
"no control",
197197
"rtsp://localhost:8554/teststream/",
198198
"rtsp://localhost:8554/teststream/",
199199
"/teststream",
200200
},
201201
{
202-
"without media id, query, ffmpeg",
202+
"no control, query, ffmpeg",
203203
"rtsp://localhost:8554/teststream?testing=123/",
204204
"rtsp://localhost:8554/teststream/",
205205
"/teststream",
206206
},
207207
{
208-
"without media id, query, gstreamer",
208+
"no control, query, gstreamer",
209209
"rtsp://localhost:8554/teststream/?testing=123",
210210
"rtsp://localhost:8554/teststream/",
211211
"/teststream",
@@ -217,7 +217,7 @@ func TestServerPlayPath(t *testing.T) {
217217
"/test/stream",
218218
},
219219
{
220-
"subpath without media id",
220+
"subpath, no control",
221221
"rtsp://localhost:8554/test/stream/",
222222
"rtsp://localhost:8554/test/stream/",
223223
"/test/stream",
@@ -235,7 +235,7 @@ func TestServerPlayPath(t *testing.T) {
235235
"/test/stream",
236236
},
237237
{
238-
"no slash",
238+
"no path, no slash",
239239
"rtsp://localhost:8554[control]",
240240
"rtsp://localhost:8554",
241241
"",
@@ -246,6 +246,18 @@ func TestServerPlayPath(t *testing.T) {
246246
"rtsp://localhost:8554//",
247247
"/",
248248
},
249+
{
250+
"no control, no path",
251+
"rtsp://localhost:8554/",
252+
"rtsp://localhost:8554/",
253+
"/",
254+
},
255+
{
256+
"no control, no path, no slash",
257+
"rtsp://localhost:8554",
258+
"rtsp://localhost:8554",
259+
"",
260+
},
249261
} {
250262
t.Run(ca.name, func(t *testing.T) {
251263
var stream *ServerStream
@@ -316,9 +328,10 @@ func TestServerPlayPath(t *testing.T) {
316328

317329
func TestServerPlaySetupErrors(t *testing.T) {
318330
for _, ca := range []string{
331+
"invalid path",
332+
"closed stream",
319333
"different paths",
320334
"double setup",
321-
"closed stream",
322335
"different protocols",
323336
} {
324337
t.Run(ca, func(t *testing.T) {
@@ -329,15 +342,19 @@ func TestServerPlaySetupErrors(t *testing.T) {
329342
Handler: &testServerHandler{
330343
onConnClose: func(ctx *ServerHandlerOnConnCloseCtx) {
331344
switch ca {
345+
case "invalid path":
346+
require.EqualError(t, ctx.Error, "invalid SETUP path. "+
347+
"This typically happens when VLC fails a request, and then switches to an unsupported RTSP dialect")
348+
349+
case "closed stream":
350+
require.EqualError(t, ctx.Error, "stream is closed")
351+
332352
case "different paths":
333353
require.EqualError(t, ctx.Error, "can't setup medias with different paths")
334354

335355
case "double setup":
336356
require.EqualError(t, ctx.Error, "media has already been setup")
337357

338-
case "closed stream":
339-
require.EqualError(t, ctx.Error, "stream is closed")
340-
341358
case "different protocols":
342359
require.EqualError(t, ctx.Error, "can't setup medias with different protocols")
343360
}
@@ -375,30 +392,64 @@ func TestServerPlaySetupErrors(t *testing.T) {
375392
defer nconn.Close()
376393
conn := conn.NewConn(nconn)
377394

378-
desc := doDescribe(t, conn)
395+
var desc *description.Session
396+
var th *headers.Transport
397+
var res *base.Response
379398

380-
th := &headers.Transport{
381-
Protocol: headers.TransportProtocolUDP,
382-
Delivery: deliveryPtr(headers.TransportDeliveryUnicast),
383-
Mode: transportModePtr(headers.TransportModePlay),
384-
ClientPorts: &[2]int{35466, 35467},
385-
}
399+
switch ca {
400+
case "invalid path":
401+
th = &headers.Transport{
402+
Protocol: headers.TransportProtocolUDP,
403+
Delivery: deliveryPtr(headers.TransportDeliveryUnicast),
404+
Mode: transportModePtr(headers.TransportModePlay),
405+
ClientPorts: &[2]int{35466, 35467},
406+
}
386407

387-
res, err := writeReqReadRes(conn, base.Request{
388-
Method: base.Setup,
389-
URL: mediaURL(t, desc.BaseURL, desc.Medias[0]),
390-
Header: base.Header{
391-
"CSeq": base.HeaderValue{"2"},
392-
"Transport": th.Marshal(),
393-
},
394-
})
408+
res, err = writeReqReadRes(conn, base.Request{
409+
Method: base.Setup,
410+
URL: &base.URL{
411+
Scheme: "rtsp",
412+
Path: "/invalid",
413+
},
414+
Header: base.Header{
415+
"CSeq": base.HeaderValue{"2"},
416+
"Transport": th.Marshal(),
417+
},
418+
})
395419

396-
if ca != "closed stream" {
397420
require.NoError(t, err)
398-
require.Equal(t, base.StatusOK, res.StatusCode)
421+
require.Equal(t, base.StatusBadRequest, res.StatusCode)
422+
423+
default:
424+
desc = doDescribe(t, conn)
425+
426+
th = &headers.Transport{
427+
Protocol: headers.TransportProtocolUDP,
428+
Delivery: deliveryPtr(headers.TransportDeliveryUnicast),
429+
Mode: transportModePtr(headers.TransportModePlay),
430+
ClientPorts: &[2]int{35466, 35467},
431+
}
432+
433+
res, err = writeReqReadRes(conn, base.Request{
434+
Method: base.Setup,
435+
URL: mediaURL(t, desc.BaseURL, desc.Medias[0]),
436+
Header: base.Header{
437+
"CSeq": base.HeaderValue{"2"},
438+
"Transport": th.Marshal(),
439+
},
440+
})
441+
442+
if ca != "closed stream" {
443+
require.NoError(t, err)
444+
require.Equal(t, base.StatusOK, res.StatusCode)
445+
}
399446
}
400447

401448
switch ca {
449+
case "closed stream":
450+
require.NoError(t, err)
451+
require.Equal(t, base.StatusBadRequest, res.StatusCode)
452+
402453
case "different paths":
403454
session := readSession(t, res)
404455

@@ -433,10 +484,6 @@ func TestServerPlaySetupErrors(t *testing.T) {
433484
require.NoError(t, err)
434485
require.Equal(t, base.StatusBadRequest, res.StatusCode)
435486

436-
case "closed stream":
437-
require.NoError(t, err)
438-
require.Equal(t, base.StatusBadRequest, res.StatusCode)
439-
440487
case "different protocols":
441488
session := readSession(t, res)
442489

server_session.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,17 @@ func getPathAndQueryAndTrackID(u *base.URL) (string, string, string, error) {
7676
if strings.HasSuffix(u.RawQuery, "/") {
7777
return u.Path, u.RawQuery[:len(u.RawQuery)-1], "0", nil
7878
}
79-
if strings.HasSuffix(u.Path[1:], "/") {
79+
if len(u.Path) >= 1 && strings.HasSuffix(u.Path[1:], "/") {
8080
return u.Path[:len(u.Path)-1], u.RawQuery, "0", nil
8181
}
8282

83-
return "", "", "", liberrors.ErrServerPathNoSlash{}
83+
// special case for empty path
84+
if u.Path == "" || u.Path == "/" {
85+
return u.Path, u.RawQuery, "0", nil
86+
}
87+
88+
// no slash at the end of the path.
89+
return "", "", "", liberrors.ErrServerInvalidSetupPath{}
8490
}
8591

8692
// used for SETUP when recording

0 commit comments

Comments
 (0)