Files
webrtc/rtcconfiguration.go
2018-08-11 13:56:28 -07:00

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
}