snapshot of https://github.com/pion/webrtc.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
Fuzz / fuzz (push) Failing after 0s
CodeQL / analyze (push) Failing after 0s
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
Fuzz / fuzz (push) Failing after 0s
CodeQL / analyze (push) Failing after 0s
This commit is contained in:
735
dtlstransport.go
Normal file
735
dtlstransport.go
Normal file
@@ -0,0 +1,735 @@
|
||||
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v3"
|
||||
"github.com/pion/dtls/v3/pkg/crypto/fingerprint"
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/srtp/v3"
|
||||
"github.com/pion/webrtc/v4/internal/mux"
|
||||
"github.com/pion/webrtc/v4/internal/util"
|
||||
"github.com/pion/webrtc/v4/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// DTLSTransport allows an application access to information about the DTLS
|
||||
// transport over which RTP and RTCP packets are sent and received by
|
||||
// RTPSender and RTPReceiver, as well other data such as SCTP packets sent
|
||||
// and received by data channels.
|
||||
type DTLSTransport struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
iceTransport *ICETransport
|
||||
certificates []Certificate
|
||||
remoteParameters DTLSParameters
|
||||
remoteCertificate []byte
|
||||
state DTLSTransportState
|
||||
srtpProtectionProfile srtp.ProtectionProfile
|
||||
|
||||
onStateChangeHandler func(DTLSTransportState)
|
||||
internalOnCloseHandler func()
|
||||
|
||||
conn *dtls.Conn
|
||||
|
||||
srtpSession, srtcpSession atomic.Value
|
||||
srtpEndpoint, srtcpEndpoint *mux.Endpoint
|
||||
simulcastStreams []simulcastStreamPair
|
||||
srtpReady chan struct{}
|
||||
|
||||
dtlsMatcher mux.MatchFunc
|
||||
|
||||
api *API
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
type simulcastStreamPair struct {
|
||||
srtp *srtp.ReadStreamSRTP
|
||||
srtcp *srtp.ReadStreamSRTCP
|
||||
}
|
||||
|
||||
type streamsForSSRCResult struct {
|
||||
rtpReadStream *srtp.ReadStreamSRTP
|
||||
rtpInterceptor interceptor.RTPReader
|
||||
startRTPReaderImmediately bool
|
||||
rtcpReadStream *srtp.ReadStreamSRTCP
|
||||
rtcpInterceptor interceptor.RTCPReader
|
||||
}
|
||||
|
||||
type srtpRTPReader struct {
|
||||
readStream *srtp.ReadStreamSRTP
|
||||
}
|
||||
|
||||
func (r *srtpRTPReader) Read(in []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||
n, err := r.readStream.Read(in)
|
||||
|
||||
return n, a, err
|
||||
}
|
||||
|
||||
// NewDTLSTransport creates a new DTLSTransport.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certificate) (*DTLSTransport, error) {
|
||||
trans := &DTLSTransport{
|
||||
iceTransport: transport,
|
||||
api: api,
|
||||
state: DTLSTransportStateNew,
|
||||
dtlsMatcher: mux.MatchDTLS,
|
||||
srtpReady: make(chan struct{}),
|
||||
log: api.settingEngine.LoggerFactory.NewLogger("DTLSTransport"),
|
||||
}
|
||||
|
||||
if len(certificates) > 0 {
|
||||
now := time.Now()
|
||||
for _, x509Cert := range certificates {
|
||||
if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}
|
||||
}
|
||||
trans.certificates = append(trans.certificates, x509Cert)
|
||||
}
|
||||
} else {
|
||||
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
certificate, err := GenerateCertificate(sk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trans.certificates = []Certificate{*certificate}
|
||||
}
|
||||
|
||||
return trans, nil
|
||||
}
|
||||
|
||||
// ICETransport returns the currently-configured *ICETransport or nil
|
||||
// if one has not been configured.
|
||||
func (t *DTLSTransport) ICETransport() *ICETransport {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
return t.iceTransport
|
||||
}
|
||||
|
||||
// onStateChange requires the caller holds the lock.
|
||||
func (t *DTLSTransport) onStateChange(state DTLSTransportState) {
|
||||
t.state = state
|
||||
handler := t.onStateChangeHandler
|
||||
if handler != nil {
|
||||
handler(state)
|
||||
}
|
||||
}
|
||||
|
||||
// OnStateChange sets a handler that is fired when the DTLS
|
||||
// connection state changes.
|
||||
func (t *DTLSTransport) OnStateChange(f func(DTLSTransportState)) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.onStateChangeHandler = f
|
||||
}
|
||||
|
||||
// State returns the current dtls transport state.
|
||||
func (t *DTLSTransport) State() DTLSTransportState {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
return t.state
|
||||
}
|
||||
|
||||
// WriteRTCP sends a user provided RTCP packet to the connected peer. If no peer is connected the
|
||||
// packet is discarded.
|
||||
func (t *DTLSTransport) WriteRTCP(pkts []rtcp.Packet) (int, error) {
|
||||
raw, err := rtcp.Marshal(pkts)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
srtcpSession, err := t.getSRTCPSession()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
writeStream, err := srtcpSession.OpenWriteStream()
|
||||
if err != nil {
|
||||
// nolint
|
||||
return 0, fmt.Errorf("%w: %v", errPeerConnWriteRTCPOpenWriteStream, err)
|
||||
}
|
||||
|
||||
return writeStream.Write(raw)
|
||||
}
|
||||
|
||||
// GetLocalParameters returns the DTLS parameters of the local DTLSTransport upon construction.
|
||||
func (t *DTLSTransport) GetLocalParameters() (DTLSParameters, error) {
|
||||
fingerprints := []DTLSFingerprint{}
|
||||
|
||||
for _, c := range t.certificates {
|
||||
prints, err := c.GetFingerprints()
|
||||
if err != nil {
|
||||
return DTLSParameters{}, err
|
||||
}
|
||||
|
||||
fingerprints = append(fingerprints, prints...)
|
||||
}
|
||||
|
||||
return DTLSParameters{
|
||||
Role: DTLSRoleAuto, // always returns the default role
|
||||
Fingerprints: fingerprints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRemoteCertificate returns the certificate chain in use by the remote side
|
||||
// returns an empty list prior to selection of the remote certificate.
|
||||
func (t *DTLSTransport) GetRemoteCertificate() []byte {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
return t.remoteCertificate
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) startSRTP() error {
|
||||
srtpConfig := &srtp.Config{
|
||||
Profile: t.srtpProtectionProfile,
|
||||
BufferFactory: t.api.settingEngine.BufferFactory,
|
||||
LoggerFactory: t.api.settingEngine.LoggerFactory,
|
||||
}
|
||||
if t.api.settingEngine.replayProtection.SRTP != nil {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTPReplayProtection(*t.api.settingEngine.replayProtection.SRTP),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.disableSRTPReplayProtection {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTPNoReplayProtection(),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.replayProtection.SRTCP != nil {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTCPReplayProtection(*t.api.settingEngine.replayProtection.SRTCP),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.disableSRTCPReplayProtection {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTCPNoReplayProtection(),
|
||||
)
|
||||
}
|
||||
|
||||
connState, ok := t.conn.ConnectionState()
|
||||
if !ok {
|
||||
// nolint
|
||||
return fmt.Errorf("%w: Failed to get DTLS ConnectionState", errDtlsKeyExtractionFailed)
|
||||
}
|
||||
|
||||
err := srtpConfig.ExtractSessionKeysFromDTLS(&connState, t.role() == DTLSRoleClient)
|
||||
if err != nil {
|
||||
// nolint
|
||||
return fmt.Errorf("%w: %v", errDtlsKeyExtractionFailed, err)
|
||||
}
|
||||
|
||||
srtpSession, err := srtp.NewSessionSRTP(t.srtpEndpoint, srtpConfig)
|
||||
if err != nil {
|
||||
// nolint
|
||||
return fmt.Errorf("%w: %v", errFailedToStartSRTP, err)
|
||||
}
|
||||
|
||||
srtcpSession, err := srtp.NewSessionSRTCP(t.srtcpEndpoint, srtpConfig)
|
||||
if err != nil {
|
||||
// nolint
|
||||
return fmt.Errorf("%w: %v", errFailedToStartSRTCP, err)
|
||||
}
|
||||
|
||||
t.srtpSession.Store(srtpSession)
|
||||
t.srtcpSession.Store(srtcpSession)
|
||||
close(t.srtpReady)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) getSRTPSession() (*srtp.SessionSRTP, error) {
|
||||
if value, ok := t.srtpSession.Load().(*srtp.SessionSRTP); ok {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
return nil, errDtlsTransportNotStarted
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) {
|
||||
if value, ok := t.srtcpSession.Load().(*srtp.SessionSRTCP); ok {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
return nil, errDtlsTransportNotStarted
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) role() DTLSRole {
|
||||
// If remote has an explicit role use the inverse
|
||||
switch t.remoteParameters.Role {
|
||||
case DTLSRoleClient:
|
||||
return DTLSRoleServer
|
||||
case DTLSRoleServer:
|
||||
return DTLSRoleClient
|
||||
default:
|
||||
}
|
||||
|
||||
// If SettingEngine has an explicit role
|
||||
switch t.api.settingEngine.answeringDTLSRole {
|
||||
case DTLSRoleServer:
|
||||
return DTLSRoleServer
|
||||
case DTLSRoleClient:
|
||||
return DTLSRoleClient
|
||||
default:
|
||||
}
|
||||
|
||||
// Remote was auto and no explicit role was configured via SettingEngine
|
||||
if t.iceTransport.Role() == ICERoleControlling {
|
||||
return DTLSRoleServer
|
||||
}
|
||||
|
||||
return defaultDtlsRoleAnswer
|
||||
}
|
||||
|
||||
// Start DTLS transport negotiation with the parameters of the remote DTLS transport.
|
||||
func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error {
|
||||
return t.start(remoteParameters, t.handshakeDTLS)
|
||||
}
|
||||
|
||||
// StartContext starts DTLS transport negotiation with the parameters of the remote DTLS
|
||||
// transport. If the context is canceled before the DTLS handshake is complete, the handshake
|
||||
// is interrupted and an error is returned.
|
||||
func (t *DTLSTransport) StartContext(ctx context.Context, remoteParameters DTLSParameters) error {
|
||||
return t.start(remoteParameters, func(dtlsConn *dtls.Conn) error {
|
||||
return dtlsConn.HandshakeContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) start(remoteParameters DTLSParameters, handshake func(*dtls.Conn) error) error {
|
||||
role, certificate, err := t.prepareStart(remoteParameters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dtlsEndpoint := t.iceTransport.newEndpoint(mux.MatchDTLS)
|
||||
dtlsEndpoint.SetOnClose(t.internalOnCloseHandler)
|
||||
|
||||
sharedOpts := t.dtlsSharedOptions(certificate)
|
||||
|
||||
dtlsConn, err := t.connectDTLS(dtlsEndpoint, role, sharedOpts)
|
||||
if err != nil {
|
||||
dtlsEndpoint.SetOnClose(nil)
|
||||
_ = dtlsEndpoint.Close()
|
||||
|
||||
return t.failStart(err)
|
||||
}
|
||||
|
||||
if err = handshake(dtlsConn); err != nil {
|
||||
dtlsEndpoint.SetOnClose(nil)
|
||||
_ = dtlsConn.Close()
|
||||
|
||||
return t.failStart(err)
|
||||
}
|
||||
|
||||
if err = t.completeStart(dtlsConn); err != nil {
|
||||
dtlsEndpoint.SetOnClose(nil)
|
||||
_ = dtlsConn.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) prepareStart(remoteParameters DTLSParameters) (DTLSRole, tls.Certificate, error) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if err := t.ensureICEConn(); err != nil {
|
||||
return DTLSRole(0), tls.Certificate{}, err
|
||||
}
|
||||
|
||||
if t.state != DTLSTransportStateNew {
|
||||
return DTLSRole(0), tls.Certificate{}, &rtcerr.InvalidStateError{
|
||||
Err: fmt.Errorf("%w: %s", errInvalidDTLSStart, t.state),
|
||||
}
|
||||
}
|
||||
|
||||
t.srtpEndpoint = t.iceTransport.newEndpoint(mux.MatchSRTP)
|
||||
t.srtcpEndpoint = t.iceTransport.newEndpoint(mux.MatchSRTCP)
|
||||
t.remoteParameters = remoteParameters
|
||||
|
||||
cert := t.certificates[0]
|
||||
t.onStateChange(DTLSTransportStateConnecting)
|
||||
|
||||
return t.role(), tls.Certificate{
|
||||
Certificate: [][]byte{cert.x509Cert.Raw},
|
||||
PrivateKey: cert.privateKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) dtlsSharedOptions(certificate tls.Certificate) []dtls.Option {
|
||||
sharedOpts := []dtls.Option{
|
||||
dtls.WithCertificates(certificate),
|
||||
dtls.WithSRTPProtectionProfiles(t.srtpProtectionProfiles()...),
|
||||
dtls.WithExtendedMasterSecret(t.api.settingEngine.dtls.extendedMasterSecret),
|
||||
dtls.WithInsecureSkipVerify(!t.api.settingEngine.dtls.disableInsecureSkipVerify),
|
||||
dtls.WithLoggerFactory(t.api.settingEngine.LoggerFactory),
|
||||
dtls.WithVerifyPeerCertificate(t.verifyPeerCertificateFunc()),
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.customCipherSuites != nil {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithCustomCipherSuites(t.api.settingEngine.dtls.customCipherSuites),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.retransmissionInterval > 0 {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithFlightInterval(t.api.settingEngine.dtls.retransmissionInterval),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.replayProtection.DTLS != nil {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithReplayProtectionWindow(int(*t.api.settingEngine.replayProtection.DTLS)), //nolint:gosec // G115
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.cipherSuites != nil {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithCipherSuites(t.api.settingEngine.dtls.cipherSuites...),
|
||||
)
|
||||
}
|
||||
|
||||
if len(t.api.settingEngine.dtls.ellipticCurves) > 0 {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithEllipticCurves(t.api.settingEngine.dtls.ellipticCurves...),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.rootCAs != nil {
|
||||
sharedOpts = append(sharedOpts, dtls.WithRootCAs(t.api.settingEngine.dtls.rootCAs))
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.keyLogWriter != nil {
|
||||
sharedOpts = append(sharedOpts, dtls.WithKeyLogWriter(t.api.settingEngine.dtls.keyLogWriter))
|
||||
}
|
||||
|
||||
if len(t.api.settingEngine.dtls.supportedProtocols) > 0 {
|
||||
sharedOpts = append(
|
||||
sharedOpts,
|
||||
dtls.WithSupportedProtocols(t.api.settingEngine.dtls.supportedProtocols...),
|
||||
)
|
||||
}
|
||||
|
||||
return sharedOpts
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) srtpProtectionProfiles() []dtls.SRTPProtectionProfile {
|
||||
if len(t.api.settingEngine.srtpProtectionProfiles) > 0 {
|
||||
return t.api.settingEngine.srtpProtectionProfiles
|
||||
}
|
||||
|
||||
return defaultSrtpProtectionProfiles()
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) verifyPeerCertificateFunc() func([][]byte, [][]*x509.Certificate) error {
|
||||
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
if len(rawCerts) == 0 {
|
||||
return errNoRemoteCertificate
|
||||
}
|
||||
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.remoteCertificate = rawCerts[0]
|
||||
|
||||
if t.api.settingEngine.disableCertificateFingerprintVerification {
|
||||
return nil
|
||||
}
|
||||
|
||||
parsedRemoteCert, err := x509.ParseCertificate(t.remoteCertificate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.validateFingerPrint(parsedRemoteCert)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) connectDTLS(
|
||||
dtlsEndpoint *mux.Endpoint,
|
||||
role DTLSRole,
|
||||
sharedOpts []dtls.Option,
|
||||
) (*dtls.Conn, error) {
|
||||
if role == DTLSRoleClient {
|
||||
clientOpts := t.toDTLSClientOptions(sharedOpts)
|
||||
|
||||
return dtls.ClientWithOptions(
|
||||
dtlsEndpoint,
|
||||
dtlsEndpoint.RemoteAddr(),
|
||||
clientOpts...,
|
||||
)
|
||||
}
|
||||
|
||||
serverOpts := t.toDTLSServerOptions(sharedOpts)
|
||||
|
||||
return dtls.ServerWithOptions(
|
||||
dtlsEndpoint,
|
||||
dtlsEndpoint.RemoteAddr(),
|
||||
serverOpts...,
|
||||
)
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) toDTLSServerOptions(sharedOpts []dtls.Option) []dtls.ServerOption {
|
||||
serverOpts := make([]dtls.ServerOption, 0, len(sharedOpts)+5)
|
||||
for _, opt := range sharedOpts {
|
||||
serverOpts = append(serverOpts, opt)
|
||||
}
|
||||
|
||||
clientAuth := dtls.RequireAnyClientCert
|
||||
if t.api.settingEngine.dtls.clientAuth != nil {
|
||||
clientAuth = *t.api.settingEngine.dtls.clientAuth
|
||||
}
|
||||
|
||||
serverOpts = append(serverOpts,
|
||||
dtls.WithClientAuth(clientAuth),
|
||||
dtls.WithClientCAs(t.api.settingEngine.dtls.clientCAs),
|
||||
dtls.WithInsecureSkipVerifyHello(t.api.settingEngine.dtls.insecureSkipHelloVerify),
|
||||
)
|
||||
|
||||
if t.api.settingEngine.dtls.serverHelloMessageHook != nil {
|
||||
serverOpts = append(
|
||||
serverOpts,
|
||||
dtls.WithServerHelloMessageHook(t.api.settingEngine.dtls.serverHelloMessageHook),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.certificateRequestMessageHook != nil {
|
||||
serverOpts = append(
|
||||
serverOpts,
|
||||
dtls.WithCertificateRequestMessageHook(t.api.settingEngine.dtls.certificateRequestMessageHook),
|
||||
)
|
||||
}
|
||||
|
||||
return serverOpts
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) toDTLSClientOptions(sharedOpts []dtls.Option) []dtls.ClientOption {
|
||||
clientOpts := make([]dtls.ClientOption, 0, len(sharedOpts)+1)
|
||||
for _, opt := range sharedOpts {
|
||||
clientOpts = append(clientOpts, opt)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.dtls.clientHelloMessageHook != nil {
|
||||
clientOpts = append(
|
||||
clientOpts,
|
||||
dtls.WithClientHelloMessageHook(t.api.settingEngine.dtls.clientHelloMessageHook),
|
||||
)
|
||||
}
|
||||
|
||||
return clientOpts
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) handshakeDTLS(dtlsConn *dtls.Conn) error {
|
||||
if t.api.settingEngine.dtls.connectContextMaker == nil {
|
||||
return dtlsConn.Handshake()
|
||||
}
|
||||
|
||||
handshakeCtx, cancel := t.api.settingEngine.dtls.connectContextMaker()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
return dtlsConn.HandshakeContext(handshakeCtx)
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) completeStart(dtlsConn *dtls.Conn) error {
|
||||
srtpProtectionProfile, err := srtpProtectionProfileFromDTLSConn(dtlsConn)
|
||||
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if err != nil {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
t.srtpProtectionProfile = srtpProtectionProfile
|
||||
t.conn = dtlsConn
|
||||
t.onStateChange(DTLSTransportStateConnected)
|
||||
|
||||
return t.startSRTP()
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) failStart(err error) error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func srtpProtectionProfileFromDTLSConn(dtlsConn *dtls.Conn) (srtp.ProtectionProfile, error) {
|
||||
srtpProfile, ok := dtlsConn.SelectedSRTPProtectionProfile()
|
||||
if !ok {
|
||||
return 0, ErrNoSRTPProtectionProfile
|
||||
}
|
||||
|
||||
return srtpProtectionProfileFromDTLS(srtpProfile)
|
||||
}
|
||||
|
||||
func srtpProtectionProfileFromDTLS(srtpProfile dtls.SRTPProtectionProfile) (srtp.ProtectionProfile, error) {
|
||||
switch srtpProfile {
|
||||
case dtls.SRTP_AEAD_AES_128_GCM:
|
||||
return srtp.ProtectionProfileAeadAes128Gcm, nil
|
||||
case dtls.SRTP_AEAD_AES_256_GCM:
|
||||
return srtp.ProtectionProfileAeadAes256Gcm, nil
|
||||
case dtls.SRTP_AES128_CM_HMAC_SHA1_80:
|
||||
return srtp.ProtectionProfileAes128CmHmacSha1_80, nil
|
||||
case dtls.SRTP_NULL_HMAC_SHA1_80:
|
||||
return srtp.ProtectionProfileNullHmacSha1_80, nil
|
||||
default:
|
||||
return 0, ErrNoSRTPProtectionProfile
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops and closes the DTLSTransport object.
|
||||
func (t *DTLSTransport) Stop() error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
// Try closing everything and collect the errors
|
||||
var closeErrs []error
|
||||
|
||||
if srtpSession, err := t.getSRTPSession(); err == nil && srtpSession != nil {
|
||||
closeErrs = append(closeErrs, srtpSession.Close())
|
||||
}
|
||||
|
||||
if srtcpSession, err := t.getSRTCPSession(); err == nil && srtcpSession != nil {
|
||||
closeErrs = append(closeErrs, srtcpSession.Close())
|
||||
}
|
||||
|
||||
for i := range t.simulcastStreams {
|
||||
closeErrs = append(closeErrs, t.simulcastStreams[i].srtp.Close())
|
||||
closeErrs = append(closeErrs, t.simulcastStreams[i].srtcp.Close())
|
||||
}
|
||||
|
||||
if t.conn != nil {
|
||||
// dtls connection may be closed on sctp close.
|
||||
if err := t.conn.Close(); err != nil && !errors.Is(err, dtls.ErrConnClosed) {
|
||||
closeErrs = append(closeErrs, err)
|
||||
}
|
||||
}
|
||||
t.onStateChange(DTLSTransportStateClosed)
|
||||
|
||||
return util.FlattenErrs(closeErrs)
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) validateFingerPrint(remoteCert *x509.Certificate) error {
|
||||
for _, fp := range t.remoteParameters.Fingerprints {
|
||||
hashAlgo, err := fingerprint.HashFromString(fp.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteValue, err := fingerprint.Fingerprint(remoteCert, hashAlgo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.EqualFold(remoteValue, fp.Value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errNoMatchingCertificateFingerprint
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) ensureICEConn() error {
|
||||
if t.iceTransport == nil {
|
||||
return errICEConnectionNotStarted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) storeSimulcastStream(
|
||||
srtpReadStream *srtp.ReadStreamSRTP,
|
||||
srtcpReadStream *srtp.ReadStreamSRTCP,
|
||||
) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
t.simulcastStreams = append(t.simulcastStreams, simulcastStreamPair{srtpReadStream, srtcpReadStream})
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) streamsForSSRC(
|
||||
ssrc SSRC,
|
||||
streamInfo interceptor.StreamInfo,
|
||||
) (*streamsForSSRCResult, error) {
|
||||
srtpSession, err := t.getSRTPSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtpReadStream, err := srtpSession.OpenReadStream(uint32(ssrc))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtpReader := &srtpRTPReader{readStream: rtpReadStream}
|
||||
rtpInterceptor := t.api.interceptor.BindRemoteStream(&streamInfo, rtpReader)
|
||||
|
||||
srtcpSession, err := t.getSRTCPSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(ssrc))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtcpInterceptor := t.api.interceptor.BindRTCPReader(interceptor.RTCPReaderFunc(
|
||||
func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
|
||||
n, err = rtcpReadStream.Read(in)
|
||||
|
||||
return n, a, err
|
||||
}),
|
||||
)
|
||||
|
||||
return &streamsForSSRCResult{
|
||||
rtpReadStream: rtpReadStream,
|
||||
rtpInterceptor: rtpInterceptor,
|
||||
startRTPReaderImmediately: rtpInterceptor != rtpReader && t.api.settingEngine.BufferFactory == nil,
|
||||
rtcpReadStream: rtcpReadStream,
|
||||
rtcpInterceptor: rtcpInterceptor,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user