From 5cbfaa4cd88e86dffc4e6a2b6e978754205589a4 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sun, 10 May 2020 22:56:46 +0200 Subject: [PATCH] add arguments --read-user --read-pass --- README.md | 2 ++ main.go | 6 ++++ main_test.go | 45 +++++++++++++++++++++++- server-client.go | 89 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 114 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index fd6ba845..23191907 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ Flags: --write-timeout=5s timeout for write operations --publish-user="" optional username required to publish --publish-pass="" optional password required to publish + --read-user="" optional username required to read + --read-pass="" optional password required to read --pre-script="" optional script to run on client connect --post-script="" optional script to run on client disconnect ``` diff --git a/main.go b/main.go index ffe0c423..8bdebd1a 100644 --- a/main.go +++ b/main.go @@ -49,6 +49,8 @@ type args struct { writeTimeout time.Duration publishUser string publishPass string + readUser string + readPass string preScript string postScript string } @@ -177,6 +179,8 @@ func main() { argWriteTimeout := kingpin.Flag("write-timeout", "timeout for write operations").Default("5s").Duration() argPublishUser := kingpin.Flag("publish-user", "optional username required to publish").Default("").String() argPublishPass := kingpin.Flag("publish-pass", "optional password required to publish").Default("").String() + argReadUser := kingpin.Flag("read-user", "optional username required to read").Default("").String() + argReadPass := kingpin.Flag("read-pass", "optional password required to read").Default("").String() argPreScript := kingpin.Flag("pre-script", "optional script to run on client connect").Default("").String() argPostScript := kingpin.Flag("post-script", "optional script to run on client disconnect").Default("").String() @@ -192,6 +196,8 @@ func main() { writeTimeout: *argWriteTimeout, publishUser: *argPublishUser, publishPass: *argPublishPass, + readUser: *argReadUser, + readPass: *argReadPass, preScript: *argPreScript, postScript: *argPostScript, }) diff --git a/main_test.go b/main_test.go index c2d622cc..f2af2bf9 100644 --- a/main_test.go +++ b/main_test.go @@ -102,7 +102,7 @@ func TestProtocols(t *testing.T) { } } -func TestAuthentication(t *testing.T) { +func TestPublishAuth(t *testing.T) { p, err := newProgram(args{ publishUser: "testuser", publishPass: "testpass", @@ -144,3 +144,46 @@ func TestAuthentication(t *testing.T) { require.Equal(t, "all right\n", string(cnt2.stdout.Bytes())) } + +func TestReadAuth(t *testing.T) { + p, err := newProgram(args{ + readUser: "testuser", + readPass: "testpass", + }) + require.NoError(t, err) + defer p.close() + + time.Sleep(1 * time.Second) + + cnt1, err := newContainer("ffmpeg", "source", []string{ + "-hide_banner", + "-loglevel", "panic", + "-re", + "-stream_loop", "-1", + "-i", "/emptyvideo.ts", + "-c", "copy", + "-f", "rtsp", + "-rtsp_transport", "udp", + "rtsp://localhost:8554/teststream", + }) + require.NoError(t, err) + defer cnt1.close() + + time.Sleep(1 * time.Second) + + cnt2, err := newContainer("ffmpeg", "dest", []string{ + "-hide_banner", + "-loglevel", "panic", + "-rtsp_transport", "udp", + "-i", "rtsp://testuser:testpass@localhost:8554/teststream", + "-vframes", "1", + "-f", "image2", + "-y", "/dev/null", + }) + require.NoError(t, err) + defer cnt2.close() + + cnt2.wait() + + require.Equal(t, "all right\n", string(cnt2.stdout.Bytes())) +} diff --git a/server-client.go b/server-client.go index 85f173f7..8ed14c93 100644 --- a/server-client.go +++ b/server-client.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "io" "log" @@ -108,7 +109,8 @@ type serverClient struct { conn *gortsplib.ConnServer state clientState path string - as *gortsplib.AuthServer + publishAuth *gortsplib.AuthServer + readAuth *gortsplib.AuthServer streamSdpText []byte // filled only if publisher streamSdpParsed *sdp.Message // filled only if publisher streamProtocol streamProtocol @@ -240,6 +242,44 @@ func (c *serverClient) writeResError(req *gortsplib.Request, code gortsplib.Stat }) } +var errAuthCritical = errors.New("auth critical") +var errAuthNotCritical = errors.New("auth not critical") + +func (c *serverClient) validateAuth(req *gortsplib.Request, user string, pass string, auth **gortsplib.AuthServer) error { + if user == "" { + return nil + } + + initialRequest := false + if *auth == nil { + initialRequest = true + *auth = gortsplib.NewAuthServer(user, pass) + } + + err := (*auth).ValidateHeader(req.Header["Authorization"], req.Method, req.Url) + if err != nil { + if !initialRequest { + c.log("ERR: Unauthorized: %s", err) + } + + c.conn.WriteResponse(&gortsplib.Response{ + StatusCode: gortsplib.StatusUnauthorized, + Header: gortsplib.Header{ + "CSeq": []string{req.Header["CSeq"][0]}, + "WWW-Authenticate": (*auth).GenerateHeader(), + }, + }) + + if !initialRequest { + return errAuthCritical + } + + return errAuthNotCritical + } + + return nil +} + func (c *serverClient) handleRequest(req *gortsplib.Request) bool { c.log(string(req.Method)) @@ -293,6 +333,14 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool { return false } + err := c.validateAuth(req, c.p.args.readUser, c.p.args.readPass, &c.readAuth) + if err != nil { + if err == errAuthCritical { + return false + } + return true + } + sdp, err := func() ([]byte, error) { c.p.tcpl.mutex.RLock() defer c.p.tcpl.mutex.RUnlock() @@ -326,33 +374,12 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool { return false } - if c.p.args.publishUser != "" { - initialRequest := false - if c.as == nil { - initialRequest = true - c.as = gortsplib.NewAuthServer(c.p.args.publishUser, c.p.args.publishPass) - } - - err := c.as.ValidateHeader(req.Header["Authorization"], gortsplib.ANNOUNCE, req.Url) - if err != nil { - if !initialRequest { - c.log("ERR: Unauthorized: %s", err) - } - - c.conn.WriteResponse(&gortsplib.Response{ - StatusCode: gortsplib.StatusUnauthorized, - Header: gortsplib.Header{ - "CSeq": []string{cseq[0]}, - "WWW-Authenticate": c.as.GenerateHeader(), - }, - }) - - if !initialRequest { - return false - } - - return true + err := c.validateAuth(req, c.p.args.publishUser, c.p.args.publishPass, &c.publishAuth) + if err != nil { + if err == errAuthCritical { + return false } + return true } ct, ok := req.Header["Content-Type"] @@ -420,6 +447,14 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool { switch c.state { // play case _CLIENT_STATE_STARTING, _CLIENT_STATE_PRE_PLAY: + err := c.validateAuth(req, c.p.args.readUser, c.p.args.readPass, &c.readAuth) + if err != nil { + if err == errAuthCritical { + return false + } + return true + } + // play via UDP if func() bool { _, ok := th["RTP/AVP"]