mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 15:16:52 +08:00
287 lines
7.7 KiB
Go
287 lines
7.7 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/pions/webrtc/pkg/ice"
|
|
)
|
|
|
|
// RTCICECredentialType indicates the type of credentials used to connect to an ICE server
|
|
type RTCICECredentialType int
|
|
|
|
const (
|
|
// RTCICECredentialTypePassword describes username+pasword based credentials
|
|
RTCICECredentialTypePassword RTCICECredentialType = iota + 1
|
|
// RTCICECredentialTypeOauth describes token based credentials
|
|
RTCICECredentialTypeOauth
|
|
)
|
|
|
|
func (t RTCICECredentialType) String() string {
|
|
switch t {
|
|
case RTCICECredentialTypePassword:
|
|
return "password"
|
|
case RTCICECredentialTypeOauth:
|
|
return "oauth"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// RTCCertificate represents a certificate used to authenticate WebRTC communications.
|
|
type RTCCertificate struct {
|
|
expires time.Time
|
|
// TODO: Finish during DTLS implementation
|
|
}
|
|
|
|
// Equals determines if two certificates are identical
|
|
func (c RTCCertificate) Equals(other RTCCertificate) bool {
|
|
return c.expires == other.expires
|
|
}
|
|
|
|
// RTCICEServer describes a single ICE server, as well as required credentials
|
|
type RTCICEServer struct {
|
|
URLs []string
|
|
Username string
|
|
Credential RTCICECredential
|
|
CredentialType RTCICECredentialType
|
|
}
|
|
|
|
// RTCICECredential represents credentials used to connect to an ICE server
|
|
// Two types of credentials are supported:
|
|
// - Password (type string)
|
|
// - Password (type RTCOAuthCredential)
|
|
type RTCICECredential interface{}
|
|
|
|
// RTCOAuthCredential represents OAuth credentials used to connect to an ICE server
|
|
type RTCOAuthCredential struct {
|
|
MacKey string
|
|
AccessToken string
|
|
}
|
|
|
|
// RTCICETransportPolicy defines the ICE candidate policy [JSEP] (section 3.5.3.) used to surface the permitted candidates
|
|
type RTCICETransportPolicy int
|
|
|
|
const (
|
|
// RTCICETransportPolicyRelay indicates only media relay candidates such as candidates passing through a TURN server are used
|
|
RTCICETransportPolicyRelay RTCICETransportPolicy = iota + 1
|
|
|
|
// RTCICETransportPolicyAll indicates any type of candidate is used
|
|
RTCICETransportPolicyAll
|
|
)
|
|
|
|
func (t RTCICETransportPolicy) String() string {
|
|
switch t {
|
|
case RTCICETransportPolicyRelay:
|
|
return "relay"
|
|
case RTCICETransportPolicyAll:
|
|
return "all"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// RTCBundlePolicy affects which media tracks are negotiated if the remote endpoint is not bundle-aware, and what ICE candidates are gathered.
|
|
type RTCBundlePolicy int
|
|
|
|
const (
|
|
|
|
// RTCRtcpMuxPolicyBalanced indicates to gather ICE candidates for each media type in use (audio, video, and data).
|
|
RTCRtcpMuxPolicyBalanced RTCBundlePolicy = iota + 1
|
|
|
|
// RTCRtcpMuxPolicyMaxCompat indicates to gather ICE candidates for each track.
|
|
RTCRtcpMuxPolicyMaxCompat
|
|
|
|
// RTCRtcpMuxPolicyMaxBundle indicates to gather ICE candidates for only one track.
|
|
RTCRtcpMuxPolicyMaxBundle
|
|
)
|
|
|
|
func (t RTCBundlePolicy) String() string {
|
|
switch t {
|
|
case RTCRtcpMuxPolicyBalanced:
|
|
return "balanced"
|
|
case RTCRtcpMuxPolicyMaxCompat:
|
|
return "max-compat"
|
|
case RTCRtcpMuxPolicyMaxBundle:
|
|
return "max-bundle"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// RTCRtcpMuxPolicy affects what ICE candidates are gathered to support non-multiplexed RTCP
|
|
type RTCRtcpMuxPolicy int
|
|
|
|
const (
|
|
// RTCRtcpMuxPolicyNegotiate indicates to gather ICE candidates for both RTP and RTCP candidates.
|
|
RTCRtcpMuxPolicyNegotiate RTCRtcpMuxPolicy = iota + 1
|
|
|
|
// RTCRtcpMuxPolicyRequire indicates to gather ICE candidates only for RTP and multiplex RTCP on the RTP candidates
|
|
RTCRtcpMuxPolicyRequire
|
|
)
|
|
|
|
func (t RTCRtcpMuxPolicy) String() string {
|
|
switch t {
|
|
case RTCRtcpMuxPolicyNegotiate:
|
|
return "negotiate"
|
|
case RTCRtcpMuxPolicyRequire:
|
|
return "require"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// RTCConfiguration contains RTCPeerConfiguration options
|
|
type RTCConfiguration struct {
|
|
// ICEServers holds multiple RTCICEServer instances, each describing one server which may be used by the ICE agent;
|
|
// these are typically STUN and/or TURN servers. If this isn't specified, the ICE agent may choose to use its own ICE servers;
|
|
// otherwise, the connection attempt will be made with no STUN or TURN server available, which limits the connection to local peers.
|
|
ICEServers []RTCICEServer
|
|
ICETransportPolicy RTCICETransportPolicy
|
|
BundlePolicy RTCBundlePolicy
|
|
RtcpMuxPolicy RTCRtcpMuxPolicy
|
|
PeerIdentity string
|
|
Certificates []RTCCertificate
|
|
ICECandidatePoolSize uint8
|
|
}
|
|
|
|
// SetConfiguration updates the configuration of the RTCPeerConnection
|
|
func (r *RTCPeerConnection) SetConfiguration(config RTCConfiguration) error {
|
|
err := r.validatePeerIdentity(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.validateCertificates(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.validateBundlePolicy(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.validateRtcpMuxPolicy(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.validateICECandidatePoolSize(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = r.setICEServers(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.config = config
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) validatePeerIdentity(config RTCConfiguration) error {
|
|
current := r.config
|
|
if current.PeerIdentity != "" &&
|
|
config.PeerIdentity != "" &&
|
|
config.PeerIdentity != current.PeerIdentity {
|
|
return &InvalidModificationError{Err: ErrModPeerIdentity}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) validateCertificates(config RTCConfiguration) error {
|
|
current := r.config
|
|
if len(current.Certificates) > 0 &&
|
|
len(config.Certificates) > 0 {
|
|
if len(config.Certificates) != len(current.Certificates) {
|
|
return &InvalidModificationError{Err: ErrModCertificates}
|
|
}
|
|
for i, cert := range config.Certificates {
|
|
if !current.Certificates[i].Equals(cert) {
|
|
return &InvalidModificationError{Err: ErrModCertificates}
|
|
}
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
for _, cert := range config.Certificates {
|
|
if now.After(cert.expires) {
|
|
return &InvalidAccessError{Err: ErrCertificateExpired}
|
|
}
|
|
// TODO: Check certificate 'origin'
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) validateBundlePolicy(config RTCConfiguration) error {
|
|
current := r.config
|
|
if config.BundlePolicy != current.BundlePolicy {
|
|
return &InvalidModificationError{Err: ErrModRtcpMuxPolicy}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) validateRtcpMuxPolicy(config RTCConfiguration) error {
|
|
current := r.config
|
|
if config.RtcpMuxPolicy != current.RtcpMuxPolicy {
|
|
return &InvalidModificationError{Err: ErrModRtcpMuxPolicy}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) validateICECandidatePoolSize(config RTCConfiguration) error {
|
|
current := r.config
|
|
if r.LocalDescription != nil &&
|
|
config.ICECandidatePoolSize != current.ICECandidatePoolSize {
|
|
return &InvalidModificationError{Err: ErrModICECandidatePoolSize}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RTCPeerConnection) setICEServers(config RTCConfiguration) error {
|
|
for _, server := range config.ICEServers {
|
|
for _, rawURL := range server.URLs {
|
|
url, err := parseICEServer(server, rawURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.networkManager.AddURL(&url)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseICEServer(server RTCICEServer, rawURL string) (ice.URL, error) {
|
|
iceurl, err := ice.NewURL(rawURL)
|
|
if err != nil {
|
|
return iceurl, &SyntaxError{Err: err}
|
|
}
|
|
|
|
if iceurl.Type == ice.ServerTypeTURN {
|
|
if server.Username == "" {
|
|
return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
|
|
}
|
|
|
|
switch t := server.Credential.(type) {
|
|
case string:
|
|
if t == "" {
|
|
return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
|
|
} else if server.CredentialType != RTCICECredentialTypePassword {
|
|
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
|
}
|
|
|
|
case RTCOAuthCredential:
|
|
if server.CredentialType != RTCICECredentialTypeOauth {
|
|
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
|
}
|
|
|
|
default:
|
|
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
|
|
|
}
|
|
}
|
|
return iceurl, nil
|
|
}
|