Browse Source

add arguments --read-user --read-pass

pull/31/head v0.6.0
aler9 5 years ago
parent
commit
5cbfaa4cd8
  1. 2
      README.md
  2. 6
      main.go
  3. 45
      main_test.go
  4. 89
      server-client.go

2
README.md

@ -77,6 +77,8 @@ Flags:
--write-timeout=5s timeout for write operations --write-timeout=5s timeout for write operations
--publish-user="" optional username required to publish --publish-user="" optional username required to publish
--publish-pass="" optional password 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 --pre-script="" optional script to run on client connect
--post-script="" optional script to run on client disconnect --post-script="" optional script to run on client disconnect
``` ```

6
main.go

@ -49,6 +49,8 @@ type args struct {
writeTimeout time.Duration writeTimeout time.Duration
publishUser string publishUser string
publishPass string publishPass string
readUser string
readPass string
preScript string preScript string
postScript string postScript string
} }
@ -177,6 +179,8 @@ func main() {
argWriteTimeout := kingpin.Flag("write-timeout", "timeout for write operations").Default("5s").Duration() argWriteTimeout := kingpin.Flag("write-timeout", "timeout for write operations").Default("5s").Duration()
argPublishUser := kingpin.Flag("publish-user", "optional username required to publish").Default("").String() argPublishUser := kingpin.Flag("publish-user", "optional username required to publish").Default("").String()
argPublishPass := kingpin.Flag("publish-pass", "optional password 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() 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() argPostScript := kingpin.Flag("post-script", "optional script to run on client disconnect").Default("").String()
@ -192,6 +196,8 @@ func main() {
writeTimeout: *argWriteTimeout, writeTimeout: *argWriteTimeout,
publishUser: *argPublishUser, publishUser: *argPublishUser,
publishPass: *argPublishPass, publishPass: *argPublishPass,
readUser: *argReadUser,
readPass: *argReadPass,
preScript: *argPreScript, preScript: *argPreScript,
postScript: *argPostScript, postScript: *argPostScript,
}) })

45
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{ p, err := newProgram(args{
publishUser: "testuser", publishUser: "testuser",
publishPass: "testpass", publishPass: "testpass",
@ -144,3 +144,46 @@ func TestAuthentication(t *testing.T) {
require.Equal(t, "all right\n", string(cnt2.stdout.Bytes())) 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
server-client.go

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -108,7 +109,8 @@ type serverClient struct {
conn *gortsplib.ConnServer conn *gortsplib.ConnServer
state clientState state clientState
path string path string
as *gortsplib.AuthServer publishAuth *gortsplib.AuthServer
readAuth *gortsplib.AuthServer
streamSdpText []byte // filled only if publisher streamSdpText []byte // filled only if publisher
streamSdpParsed *sdp.Message // filled only if publisher streamSdpParsed *sdp.Message // filled only if publisher
streamProtocol streamProtocol 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 { func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
c.log(string(req.Method)) c.log(string(req.Method))
@ -293,6 +333,14 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
return false 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) { sdp, err := func() ([]byte, error) {
c.p.tcpl.mutex.RLock() c.p.tcpl.mutex.RLock()
defer c.p.tcpl.mutex.RUnlock() defer c.p.tcpl.mutex.RUnlock()
@ -326,33 +374,12 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
if c.p.args.publishUser != "" { err := c.validateAuth(req, c.p.args.publishUser, c.p.args.publishPass, &c.publishAuth)
initialRequest := false if err != nil {
if c.as == nil { if err == errAuthCritical {
initialRequest = true return false
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
} }
return true
} }
ct, ok := req.Header["Content-Type"] ct, ok := req.Header["Content-Type"]
@ -420,6 +447,14 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
switch c.state { switch c.state {
// play // play
case _CLIENT_STATE_STARTING, _CLIENT_STATE_PRE_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 // play via UDP
if func() bool { if func() bool {
_, ok := th["RTP/AVP"] _, ok := th["RTP/AVP"]

Loading…
Cancel
Save