Skip to content

Commit

Permalink
add arguments --read-user --read-pass
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed May 10, 2020
1 parent dc52266 commit 5cbfaa4
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 28 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type args struct {
writeTimeout time.Duration
publishUser string
publishPass string
readUser string
readPass string
preScript string
postScript string
}
Expand Down Expand Up @@ -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()

Expand All @@ -192,6 +196,8 @@ func main() {
writeTimeout: *argWriteTimeout,
publishUser: *argPublishUser,
publishPass: *argPublishPass,
readUser: *argReadUser,
readPass: *argReadPass,
preScript: *argPreScript,
postScript: *argPostScript,
})
Expand Down
45 changes: 44 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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()))
}
89 changes: 62 additions & 27 deletions server-client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"fmt"
"io"
"log"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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"]
Expand Down Expand Up @@ -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"]
Expand Down

0 comments on commit 5cbfaa4

Please sign in to comment.