mirror of
https://github.com/aler9/gortsplib
synced 2025-10-04 06:46:42 +08:00
implement client TLS support
This commit is contained in:
@@ -12,6 +12,7 @@ Features:
|
||||
* Client
|
||||
* Read streams from servers with UDP or TCP
|
||||
* Publish streams to servers with UDP or TCP
|
||||
* Encrypt streams with TLS (RTSPS)
|
||||
* Query servers about published streams
|
||||
* Read only selected tracks of a stream
|
||||
* Pause reading or publishing without disconnecting from the server
|
||||
|
@@ -2,6 +2,7 @@ package gortsplib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -18,8 +19,8 @@ import (
|
||||
var DefaultClientConf = ClientConf{}
|
||||
|
||||
// Dial connects to a server.
|
||||
func Dial(host string) (*ClientConn, error) {
|
||||
return DefaultClientConf.Dial(host)
|
||||
func Dial(scheme string, host string) (*ClientConn, error) {
|
||||
return DefaultClientConf.Dial(scheme, host)
|
||||
}
|
||||
|
||||
// DialRead connects to a server and starts reading all tracks.
|
||||
@@ -40,6 +41,10 @@ type ClientConf struct {
|
||||
// It defaults to nil.
|
||||
StreamProtocol *StreamProtocol
|
||||
|
||||
// A TLS configuration to connect to TLS (RTSPS) servers.
|
||||
// It defaults to &tls.Config{InsecureSkipVerify:true}
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// timeout of read operations.
|
||||
// It defaults to 10 seconds.
|
||||
ReadTimeout time.Duration
|
||||
@@ -74,7 +79,10 @@ type ClientConf struct {
|
||||
}
|
||||
|
||||
// Dial connects to a server.
|
||||
func (c ClientConf) Dial(host string) (*ClientConn, error) {
|
||||
func (c ClientConf) Dial(scheme string, host string) (*ClientConn, error) {
|
||||
if c.TLSConfig == nil {
|
||||
c.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
if c.ReadTimeout == 0 {
|
||||
c.ReadTimeout = 10 * time.Second
|
||||
}
|
||||
@@ -91,6 +99,10 @@ func (c ClientConf) Dial(host string) (*ClientConn, error) {
|
||||
c.ListenPacket = net.ListenPacket
|
||||
}
|
||||
|
||||
if scheme != "rtsp" && scheme != "rtsps" {
|
||||
return nil, fmt.Errorf("unsupported scheme '%s'", scheme)
|
||||
}
|
||||
|
||||
if !strings.Contains(host, ":") {
|
||||
host += ":554"
|
||||
}
|
||||
@@ -100,11 +112,18 @@ func (c ClientConf) Dial(host string) (*ClientConn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn := func() net.Conn {
|
||||
if scheme == "rtsps" {
|
||||
return tls.Client(nconn, c.TLSConfig)
|
||||
}
|
||||
return nconn
|
||||
}()
|
||||
|
||||
return &ClientConn{
|
||||
conf: c,
|
||||
nconn: nconn,
|
||||
br: bufio.NewReaderSize(nconn, clientReadBufferSize),
|
||||
bw: bufio.NewWriterSize(nconn, clientWriteBufferSize),
|
||||
br: bufio.NewReaderSize(conn, clientReadBufferSize),
|
||||
bw: bufio.NewWriterSize(conn, clientWriteBufferSize),
|
||||
udpRtpListeners: make(map[int]*clientConnUDPListener),
|
||||
udpRtcpListeners: make(map[int]*clientConnUDPListener),
|
||||
rtcpReceivers: make(map[int]*rtcpreceiver.RtcpReceiver),
|
||||
@@ -122,7 +141,7 @@ func (c ClientConf) DialRead(address string) (*ClientConn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := c.Dial(u.Host)
|
||||
conn, err := c.Dial(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,7 +182,7 @@ func (c ClientConf) DialPublish(address string, tracks Tracks) (*ClientConn, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := c.Dial(u.Host)
|
||||
conn, err := c.Dial(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -60,12 +60,38 @@ func (c *container) wait() int {
|
||||
}
|
||||
|
||||
func TestClientDialRead(t *testing.T) {
|
||||
for _, proto := range []string{
|
||||
"udp",
|
||||
"tcp",
|
||||
for _, ca := range []struct {
|
||||
encrypted bool
|
||||
proto string
|
||||
}{
|
||||
{false, "udp"},
|
||||
{false, "tcp"},
|
||||
{true, "tcp"},
|
||||
} {
|
||||
t.Run(proto, func(t *testing.T) {
|
||||
cnt1, err := newContainer("rtsp-simple-server", "server", []string{"{}"})
|
||||
encryptedStr := func() string {
|
||||
if ca.encrypted {
|
||||
return "encrypted"
|
||||
}
|
||||
return "plain"
|
||||
}()
|
||||
|
||||
t.Run(encryptedStr+"_"+ca.proto, func(t *testing.T) {
|
||||
var scheme string
|
||||
var port string
|
||||
var serverConf string
|
||||
if !ca.encrypted {
|
||||
scheme = "rtsp"
|
||||
port = "8554"
|
||||
serverConf = "{}"
|
||||
} else {
|
||||
scheme = "rtsps"
|
||||
port = "8555"
|
||||
serverConf = "readTimeout: 20s\n" +
|
||||
"protocols: [tcp]\n" +
|
||||
"encryption: yes\n"
|
||||
}
|
||||
|
||||
cnt1, err := newContainer("rtsp-simple-server", "server", []string{serverConf})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
@@ -78,7 +104,7 @@ func TestClientDialRead(t *testing.T) {
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"-rtsp_transport", "udp",
|
||||
"rtsp://localhost:8554/teststream",
|
||||
scheme + "://localhost:" + port + "/teststream",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
@@ -87,7 +113,7 @@ func TestClientDialRead(t *testing.T) {
|
||||
|
||||
conf := ClientConf{
|
||||
StreamProtocol: func() *StreamProtocol {
|
||||
if proto == "udp" {
|
||||
if ca.proto == "udp" {
|
||||
v := StreamProtocolUDP
|
||||
return &v
|
||||
}
|
||||
@@ -96,7 +122,7 @@ func TestClientDialRead(t *testing.T) {
|
||||
}(),
|
||||
}
|
||||
|
||||
conn, err := conf.DialRead("rtsp://localhost:8554/teststream")
|
||||
conn, err := conf.DialRead(scheme + "://localhost:" + port + "/teststream")
|
||||
require.NoError(t, err)
|
||||
|
||||
var firstFrame int32
|
||||
|
@@ -318,7 +318,7 @@ func (c *ClientConn) Describe(u *base.URL) (Tracks, *base.Response, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
nc, err := c.conf.Dial(u.Host)
|
||||
nc, err := c.conf.Dial(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := gortsplib.Dial(u.Host)
|
||||
conn, err := gortsplib.Dial(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := gortsplib.Dial(u.Host)
|
||||
conn, err := gortsplib.Dial(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
10
server.go
10
server.go
@@ -24,10 +24,12 @@ func (s *Server) Accept() (*ServerConn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn := nconn
|
||||
if s.conf.TLSConfig != nil {
|
||||
conn = tls.Server(conn, s.conf.TLSConfig)
|
||||
}
|
||||
conn := func() net.Conn {
|
||||
if s.conf.TLSConfig != nil {
|
||||
return tls.Server(nconn, s.conf.TLSConfig)
|
||||
}
|
||||
return nconn
|
||||
}()
|
||||
|
||||
return &ServerConn{
|
||||
s: s,
|
||||
|
@@ -271,18 +271,18 @@ y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
||||
|
||||
func TestServerPublishReadTCP(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
encrypted bool
|
||||
publisher string
|
||||
reader string
|
||||
encrypted bool
|
||||
}{
|
||||
{"ffmpeg", "ffmpeg", false},
|
||||
{"ffmpeg", "ffmpeg", true},
|
||||
{"ffmpeg", "gstreamer", false},
|
||||
{"ffmpeg", "gstreamer", true},
|
||||
{"gstreamer", "ffmpeg", false},
|
||||
{"gstreamer", "ffmpeg", true},
|
||||
{"gstreamer", "gstreamer", false},
|
||||
{"gstreamer", "gstreamer", true},
|
||||
{false, "ffmpeg", "ffmpeg"},
|
||||
{false, "ffmpeg", "gstreamer"},
|
||||
{false, "gstreamer", "ffmpeg"},
|
||||
{false, "gstreamer", "gstreamer"},
|
||||
{true, "ffmpeg", "ffmpeg"},
|
||||
{true, "ffmpeg", "gstreamer"},
|
||||
{true, "gstreamer", "ffmpeg"},
|
||||
{true, "gstreamer", "gstreamer"},
|
||||
} {
|
||||
encryptedStr := func() string {
|
||||
if ca.encrypted {
|
||||
@@ -291,7 +291,7 @@ func TestServerPublishReadTCP(t *testing.T) {
|
||||
return "plain"
|
||||
}()
|
||||
|
||||
t.Run(ca.publisher+"_"+ca.reader+"_"+encryptedStr, func(t *testing.T) {
|
||||
t.Run(encryptedStr+"_"+ca.publisher+"_"+ca.reader, func(t *testing.T) {
|
||||
var proto string
|
||||
var tlsConf *tls.Config
|
||||
if !ca.encrypted {
|
||||
|
@@ -1,5 +1,14 @@
|
||||
############################
|
||||
FROM aler9/rtsp-simple-server:latest AS server
|
||||
####################################
|
||||
FROM amd64/golang:1.15-alpine3.12 AS server
|
||||
|
||||
RUN apk --no-cache add git
|
||||
|
||||
RUN git clone https://github.com/aler9/rtsp-simple-server
|
||||
|
||||
RUN cd rtsp-simple-server \
|
||||
&& CGO_ENABLED=0 \
|
||||
go build -o /rtsp-simple-server .
|
||||
|
||||
############################
|
||||
FROM alpine:3.12
|
||||
|
||||
|
@@ -2,4 +2,54 @@
|
||||
|
||||
echo "$@" > /rtsp-simple-server.yml
|
||||
|
||||
echo "-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUXw1hEC3LFpTsllv7D3ARJyEq7sIwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMTMxNzQ0NThaFw0zMDEy
|
||||
MTExNzQ0NThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQDG8DyyS51810GsGwgWr5rjJK7OE1kTTLSNEEKax8Bj
|
||||
zOyiaz8rA2JGl2VUEpi2UjDr9Cm7nd+YIEVs91IIBOb7LGqObBh1kGF3u5aZxLkv
|
||||
NJE+HrLVvUhaDobK2NU+Wibqc/EI3DfUkt1rSINvv9flwTFu1qHeuLWhoySzDKEp
|
||||
OzYxpFhwjVSokZIjT4Red3OtFz7gl2E6OAWe2qoh5CwLYVdMWtKR0Xuw3BkDPk9I
|
||||
qkQKx3fqv97LPEzhyZYjDT5WvGrgZ1WDAN3booxXF3oA1H3GHQc4m/vcLatOtb8e
|
||||
nI59gMQLEbnp08cl873bAuNuM95EZieXTHNbwUnq5iybAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQBKhJh8eWu0a4au9X/2fKhkFX2vjAfBgNVHSMEGDAWgBQBKhJh8eWu0a4a
|
||||
u9X/2fKhkFX2vjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBj
|
||||
3aCW0YPKukYgVK9cwN0IbVy/D0C1UPT4nupJcy/E0iC7MXPZ9D/SZxYQoAkdptdO
|
||||
xfI+RXkpQZLdODNx9uvV+cHyZHZyjtE5ENu/i5Rer2cWI/mSLZm5lUQyx+0KZ2Yu
|
||||
tEI1bsebDK30msa8QSTn0WidW9XhFnl3gRi4wRdimcQapOWYVs7ih+nAlSvng7NI
|
||||
XpAyRs8PIEbpDDBMWnldrX4TP6EWYUi49gCp8OUDRREKX3l6Ls1vZ02F34yHIt/7
|
||||
7IV/XSKG096bhW+icKBWV0IpcEsgTzPK1J1hMxgjhzIMxGboAeUU+kidthOob6Sd
|
||||
XQxaORfgM//NzX9LhUPk
|
||||
-----END CERTIFICATE-----" > /server.crt
|
||||
|
||||
echo "-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAxvA8skudfNdBrBsIFq+a4ySuzhNZE0y0jRBCmsfAY8zsoms/
|
||||
KwNiRpdlVBKYtlIw6/Qpu53fmCBFbPdSCATm+yxqjmwYdZBhd7uWmcS5LzSRPh6y
|
||||
1b1IWg6GytjVPlom6nPxCNw31JLda0iDb7/X5cExbtah3ri1oaMkswyhKTs2MaRY
|
||||
cI1UqJGSI0+EXndzrRc+4JdhOjgFntqqIeQsC2FXTFrSkdF7sNwZAz5PSKpECsd3
|
||||
6r/eyzxM4cmWIw0+Vrxq4GdVgwDd26KMVxd6ANR9xh0HOJv73C2rTrW/HpyOfYDE
|
||||
CxG56dPHJfO92wLjbjPeRGYnl0xzW8FJ6uYsmwIDAQABAoIBACi0BKcyQ3HElSJC
|
||||
kaAao+Uvnzh4yvPg8Nwf5JDIp/uDdTMyIEWLtrLczRWrjGVZYbsVROinP5VfnPTT
|
||||
kYwkfKINj2u+gC6lsNuPnRuvHXikF8eO/mYvCTur1zZvsQnF5kp4GGwIqr+qoPUP
|
||||
bB0UMndG1PdpoMryHe+JcrvTrLHDmCeH10TqOwMsQMLHYLkowvxwJWsmTY7/Qr5S
|
||||
Wm3PPpOcW2i0uyPVuyuv4yD1368fqnqJ8QFsQp1K6QtYsNnJ71Hut1/IoxK/e6hj
|
||||
5Z+byKtHVtmcLnABuoOT7BhleJNFBksX9sh83jid4tMBgci+zXNeGmgqo2EmaWAb
|
||||
agQslkECgYEA8B1rzjOHVQx/vwSzDa4XOrpoHQRfyElrGNz9JVBvnoC7AorezBXQ
|
||||
M9WTHQIFTGMjzD8pb+YJGi3gj93VN51r0SmJRxBaBRh1ZZI9kFiFzngYev8POgD3
|
||||
ygmlS3kTHCNxCK/CJkB+/jMBgtPj5ygDpCWVcTSuWlQFphePkW7jaaECgYEA1Blz
|
||||
ulqgAyJHZaqgcbcCsI2q6m527hVr9pjzNjIVmkwu38yS9RTCgdlbEVVDnS0hoifl
|
||||
+jVMEGXjF3xjyMvL50BKbQUH+KAa+V4n1WGlnZOxX9TMny8MBjEuSX2+362vQ3BX
|
||||
4vOlX00gvoc+sY+lrzvfx/OdPCHQGVYzoKCxhLsCgYA07HcviuIAV/HsO2/vyvhp
|
||||
xF5gTu+BqNUHNOZDDDid+ge+Jre2yfQLCL8VPLXIQW3Jff53IH/PGl+NtjphuLvj
|
||||
7UDJvgvpZZuymIojP6+2c3gJ3CASC9aR3JBnUzdoE1O9s2eaoMqc4scpe+SWtZYf
|
||||
3vzSZ+cqF6zrD/Rf/M35IQKBgHTU4E6ShPm09CcoaeC5sp2WK8OevZw/6IyZi78a
|
||||
r5Oiy18zzO97U/k6xVMy6F+38ILl/2Rn31JZDVJujniY6eSkIVsUHmPxrWoXV1HO
|
||||
y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
||||
94TpAoGAY4/PejWQj9psZfAhyk5dRGra++gYRQ/gK1IIc1g+Dd2/BxbT/RHr05GK
|
||||
6vwrfjsoRyMWteC1SsNs/CurjfQ/jqCfHNP5XPvxgd5Ec8sRJIiV7V5RTuWJsPu1
|
||||
+3K6cnKEyg+0ekYmLertRFIY6SwWmY1fyKgTvxudMcsBY7dC4xs=
|
||||
-----END RSA PRIVATE KEY-----" > /server.key
|
||||
|
||||
exec /rtsp-simple-server
|
||||
|
Reference in New Issue
Block a user