123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- //go:build !js
- // +build !js
- package webrtc
- import (
- "encoding/json"
- "github.com/pion/stun"
- "github.com/pion/webrtc/v3/pkg/rtcerr"
- )
- // ICEServer describes a single STUN and TURN server that can be used by
- // the ICEAgent to establish a connection with a peer.
- type ICEServer struct {
- URLs []string `json:"urls"`
- Username string `json:"username,omitempty"`
- Credential interface{} `json:"credential,omitempty"`
- CredentialType ICECredentialType `json:"credentialType,omitempty"`
- }
- func (s ICEServer) parseURL(i int) (*stun.URI, error) {
- return stun.ParseURI(s.URLs[i])
- }
- func (s ICEServer) validate() error {
- _, err := s.urls()
- return err
- }
- func (s ICEServer) urls() ([]*stun.URI, error) {
- urls := []*stun.URI{}
- for i := range s.URLs {
- url, err := s.parseURL(i)
- if err != nil {
- return nil, &rtcerr.InvalidAccessError{Err: err}
- }
- if url.Scheme == stun.SchemeTypeTURN || url.Scheme == stun.SchemeTypeTURNS {
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2)
- if s.Username == "" || s.Credential == nil {
- return nil, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}
- }
- url.Username = s.Username
- switch s.CredentialType {
- case ICECredentialTypePassword:
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.3)
- password, ok := s.Credential.(string)
- if !ok {
- return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
- }
- url.Password = password
- case ICECredentialTypeOauth:
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.4)
- if _, ok := s.Credential.(OAuthCredential); !ok {
- return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
- }
- default:
- return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
- }
- }
- urls = append(urls, url)
- }
- return urls, nil
- }
- func iceserverUnmarshalUrls(val interface{}) (*[]string, error) {
- s, ok := val.([]interface{})
- if !ok {
- return nil, errInvalidICEServer
- }
- out := make([]string, len(s))
- for idx, url := range s {
- out[idx], ok = url.(string)
- if !ok {
- return nil, errInvalidICEServer
- }
- }
- return &out, nil
- }
- func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) {
- c, ok := val.(map[string]interface{})
- if !ok {
- return nil, errInvalidICEServer
- }
- MACKey, ok := c["MACKey"].(string)
- if !ok {
- return nil, errInvalidICEServer
- }
- AccessToken, ok := c["AccessToken"].(string)
- if !ok {
- return nil, errInvalidICEServer
- }
- return &OAuthCredential{
- MACKey: MACKey,
- AccessToken: AccessToken,
- }, nil
- }
- func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error {
- if val, ok := m["urls"]; ok {
- u, err := iceserverUnmarshalUrls(val)
- if err != nil {
- return err
- }
- s.URLs = *u
- } else {
- s.URLs = []string{}
- }
- if val, ok := m["username"]; ok {
- s.Username, ok = val.(string)
- if !ok {
- return errInvalidICEServer
- }
- }
- if val, ok := m["credentialType"]; ok {
- ct, ok := val.(string)
- if !ok {
- return errInvalidICEServer
- }
- tpe, err := newICECredentialType(ct)
- if err != nil {
- return err
- }
- s.CredentialType = tpe
- } else {
- s.CredentialType = ICECredentialTypePassword
- }
- if val, ok := m["credential"]; ok {
- switch s.CredentialType {
- case ICECredentialTypePassword:
- s.Credential = val
- case ICECredentialTypeOauth:
- c, err := iceserverUnmarshalOauth(val)
- if err != nil {
- return err
- }
- s.Credential = *c
- default:
- return errInvalidICECredentialTypeString
- }
- }
- return nil
- }
- // UnmarshalJSON parses the JSON-encoded data and stores the result
- func (s *ICEServer) UnmarshalJSON(b []byte) error {
- var tmp interface{}
- err := json.Unmarshal(b, &tmp)
- if err != nil {
- return err
- }
- if m, ok := tmp.(map[string]interface{}); ok {
- return s.iceserverUnmarshalFields(m)
- }
- return errInvalidICEServer
- }
- // MarshalJSON returns the JSON encoding
- func (s ICEServer) MarshalJSON() ([]byte, error) {
- m := make(map[string]interface{})
- m["urls"] = s.URLs
- if s.Username != "" {
- m["username"] = s.Username
- }
- if s.Credential != nil {
- m["credential"] = s.Credential
- }
- m["credentialType"] = s.CredentialType
- return json.Marshal(m)
- }
|