mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
implement client TLS support
This commit is contained in:
@@ -12,6 +12,7 @@ Features:
|
|||||||
* Client
|
* Client
|
||||||
* Read streams from servers with UDP or TCP
|
* Read streams from servers with UDP or TCP
|
||||||
* Publish streams to servers with UDP or TCP
|
* Publish streams to servers with UDP or TCP
|
||||||
|
* Encrypt streams with TLS (RTSPS)
|
||||||
* Query servers about published streams
|
* Query servers about published streams
|
||||||
* Read only selected tracks of a stream
|
* Read only selected tracks of a stream
|
||||||
* Pause reading or publishing without disconnecting from the server
|
* Pause reading or publishing without disconnecting from the server
|
||||||
|
@@ -2,6 +2,7 @@ package gortsplib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -18,8 +19,8 @@ import (
|
|||||||
var DefaultClientConf = ClientConf{}
|
var DefaultClientConf = ClientConf{}
|
||||||
|
|
||||||
// Dial connects to a server.
|
// Dial connects to a server.
|
||||||
func Dial(host string) (*ClientConn, error) {
|
func Dial(scheme string, host string) (*ClientConn, error) {
|
||||||
return DefaultClientConf.Dial(host)
|
return DefaultClientConf.Dial(scheme, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialRead connects to a server and starts reading all tracks.
|
// DialRead connects to a server and starts reading all tracks.
|
||||||
@@ -40,6 +41,10 @@ type ClientConf struct {
|
|||||||
// It defaults to nil.
|
// It defaults to nil.
|
||||||
StreamProtocol *StreamProtocol
|
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.
|
// timeout of read operations.
|
||||||
// It defaults to 10 seconds.
|
// It defaults to 10 seconds.
|
||||||
ReadTimeout time.Duration
|
ReadTimeout time.Duration
|
||||||
@@ -74,7 +79,10 @@ type ClientConf struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dial connects to a server.
|
// 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 {
|
if c.ReadTimeout == 0 {
|
||||||
c.ReadTimeout = 10 * time.Second
|
c.ReadTimeout = 10 * time.Second
|
||||||
}
|
}
|
||||||
@@ -91,6 +99,10 @@ func (c ClientConf) Dial(host string) (*ClientConn, error) {
|
|||||||
c.ListenPacket = net.ListenPacket
|
c.ListenPacket = net.ListenPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if scheme != "rtsp" && scheme != "rtsps" {
|
||||||
|
return nil, fmt.Errorf("unsupported scheme '%s'", scheme)
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.Contains(host, ":") {
|
if !strings.Contains(host, ":") {
|
||||||
host += ":554"
|
host += ":554"
|
||||||
}
|
}
|
||||||
@@ -100,11 +112,18 @@ func (c ClientConf) Dial(host string) (*ClientConn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn := func() net.Conn {
|
||||||
|
if scheme == "rtsps" {
|
||||||
|
return tls.Client(nconn, c.TLSConfig)
|
||||||
|
}
|
||||||
|
return nconn
|
||||||
|
}()
|
||||||
|
|
||||||
return &ClientConn{
|
return &ClientConn{
|
||||||
conf: c,
|
conf: c,
|
||||||
nconn: nconn,
|
nconn: nconn,
|
||||||
br: bufio.NewReaderSize(nconn, clientReadBufferSize),
|
br: bufio.NewReaderSize(conn, clientReadBufferSize),
|
||||||
bw: bufio.NewWriterSize(nconn, clientWriteBufferSize),
|
bw: bufio.NewWriterSize(conn, clientWriteBufferSize),
|
||||||
udpRtpListeners: make(map[int]*clientConnUDPListener),
|
udpRtpListeners: make(map[int]*clientConnUDPListener),
|
||||||
udpRtcpListeners: make(map[int]*clientConnUDPListener),
|
udpRtcpListeners: make(map[int]*clientConnUDPListener),
|
||||||
rtcpReceivers: make(map[int]*rtcpreceiver.RtcpReceiver),
|
rtcpReceivers: make(map[int]*rtcpreceiver.RtcpReceiver),
|
||||||
@@ -122,7 +141,7 @@ func (c ClientConf) DialRead(address string) (*ClientConn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := c.Dial(u.Host)
|
conn, err := c.Dial(u.Scheme, u.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -163,7 +182,7 @@ func (c ClientConf) DialPublish(address string, tracks Tracks) (*ClientConn, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := c.Dial(u.Host)
|
conn, err := c.Dial(u.Scheme, u.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -60,12 +60,38 @@ func (c *container) wait() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClientDialRead(t *testing.T) {
|
func TestClientDialRead(t *testing.T) {
|
||||||
for _, proto := range []string{
|
for _, ca := range []struct {
|
||||||
"udp",
|
encrypted bool
|
||||||
"tcp",
|
proto string
|
||||||
|
}{
|
||||||
|
{false, "udp"},
|
||||||
|
{false, "tcp"},
|
||||||
|
{true, "tcp"},
|
||||||
} {
|
} {
|
||||||
t.Run(proto, func(t *testing.T) {
|
encryptedStr := func() string {
|
||||||
cnt1, err := newContainer("rtsp-simple-server", "server", []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)
|
require.NoError(t, err)
|
||||||
defer cnt1.close()
|
defer cnt1.close()
|
||||||
|
|
||||||
@@ -78,7 +104,7 @@ func TestClientDialRead(t *testing.T) {
|
|||||||
"-c", "copy",
|
"-c", "copy",
|
||||||
"-f", "rtsp",
|
"-f", "rtsp",
|
||||||
"-rtsp_transport", "udp",
|
"-rtsp_transport", "udp",
|
||||||
"rtsp://localhost:8554/teststream",
|
scheme + "://localhost:" + port + "/teststream",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cnt2.close()
|
defer cnt2.close()
|
||||||
@@ -87,7 +113,7 @@ func TestClientDialRead(t *testing.T) {
|
|||||||
|
|
||||||
conf := ClientConf{
|
conf := ClientConf{
|
||||||
StreamProtocol: func() *StreamProtocol {
|
StreamProtocol: func() *StreamProtocol {
|
||||||
if proto == "udp" {
|
if ca.proto == "udp" {
|
||||||
v := StreamProtocolUDP
|
v := StreamProtocolUDP
|
||||||
return &v
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var firstFrame int32
|
var firstFrame int32
|
||||||
|
@@ -318,7 +318,7 @@ func (c *ClientConn) Describe(u *base.URL) (Tracks, *base.Response, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nc, err := c.conf.Dial(u.Host)
|
nc, err := c.conf.Dial(u.Scheme, u.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := gortsplib.Dial(u.Host)
|
conn, err := gortsplib.Dial(u.Scheme, u.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := gortsplib.Dial(u.Host)
|
conn, err := gortsplib.Dial(u.Scheme, u.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
10
server.go
10
server.go
@@ -24,10 +24,12 @@ func (s *Server) Accept() (*ServerConn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := nconn
|
conn := func() net.Conn {
|
||||||
if s.conf.TLSConfig != nil {
|
if s.conf.TLSConfig != nil {
|
||||||
conn = tls.Server(conn, s.conf.TLSConfig)
|
return tls.Server(nconn, s.conf.TLSConfig)
|
||||||
}
|
}
|
||||||
|
return nconn
|
||||||
|
}()
|
||||||
|
|
||||||
return &ServerConn{
|
return &ServerConn{
|
||||||
s: s,
|
s: s,
|
||||||
|
@@ -271,18 +271,18 @@ y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
|||||||
|
|
||||||
func TestServerPublishReadTCP(t *testing.T) {
|
func TestServerPublishReadTCP(t *testing.T) {
|
||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
|
encrypted bool
|
||||||
publisher string
|
publisher string
|
||||||
reader string
|
reader string
|
||||||
encrypted bool
|
|
||||||
}{
|
}{
|
||||||
{"ffmpeg", "ffmpeg", false},
|
{false, "ffmpeg", "ffmpeg"},
|
||||||
{"ffmpeg", "ffmpeg", true},
|
{false, "ffmpeg", "gstreamer"},
|
||||||
{"ffmpeg", "gstreamer", false},
|
{false, "gstreamer", "ffmpeg"},
|
||||||
{"ffmpeg", "gstreamer", true},
|
{false, "gstreamer", "gstreamer"},
|
||||||
{"gstreamer", "ffmpeg", false},
|
{true, "ffmpeg", "ffmpeg"},
|
||||||
{"gstreamer", "ffmpeg", true},
|
{true, "ffmpeg", "gstreamer"},
|
||||||
{"gstreamer", "gstreamer", false},
|
{true, "gstreamer", "ffmpeg"},
|
||||||
{"gstreamer", "gstreamer", true},
|
{true, "gstreamer", "gstreamer"},
|
||||||
} {
|
} {
|
||||||
encryptedStr := func() string {
|
encryptedStr := func() string {
|
||||||
if ca.encrypted {
|
if ca.encrypted {
|
||||||
@@ -291,7 +291,7 @@ func TestServerPublishReadTCP(t *testing.T) {
|
|||||||
return "plain"
|
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 proto string
|
||||||
var tlsConf *tls.Config
|
var tlsConf *tls.Config
|
||||||
if !ca.encrypted {
|
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
|
FROM alpine:3.12
|
||||||
|
|
||||||
|
@@ -2,4 +2,54 @@
|
|||||||
|
|
||||||
echo "$@" > /rtsp-simple-server.yml
|
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
|
exec /rtsp-simple-server
|
||||||
|
Reference in New Issue
Block a user