github.com/pion/webrtc/v3@v3.2.24/icetransport.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build !js
     5  // +build !js
     6  
     7  package webrtc
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"sync"
    13  	"sync/atomic"
    14  	"time"
    15  
    16  	"github.com/pion/ice/v2"
    17  	"github.com/pion/logging"
    18  	"github.com/pion/webrtc/v3/internal/mux"
    19  )
    20  
    21  // ICETransport allows an application access to information about the ICE
    22  // transport over which packets are sent and received.
    23  type ICETransport struct {
    24  	lock sync.RWMutex
    25  
    26  	role ICERole
    27  
    28  	onConnectionStateChangeHandler         atomic.Value // func(ICETransportState)
    29  	internalOnConnectionStateChangeHandler atomic.Value // func(ICETransportState)
    30  	onSelectedCandidatePairChangeHandler   atomic.Value // func(*ICECandidatePair)
    31  
    32  	state atomic.Value // ICETransportState
    33  
    34  	gatherer *ICEGatherer
    35  	conn     *ice.Conn
    36  	mux      *mux.Mux
    37  
    38  	ctx       context.Context
    39  	ctxCancel func()
    40  
    41  	loggerFactory logging.LoggerFactory
    42  
    43  	log logging.LeveledLogger
    44  }
    45  
    46  // GetSelectedCandidatePair returns the selected candidate pair on which packets are sent
    47  // if there is no selected pair nil is returned
    48  func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) {
    49  	agent := t.gatherer.getAgent()
    50  	if agent == nil {
    51  		return nil, nil //nolint:nilnil
    52  	}
    53  
    54  	icePair, err := agent.GetSelectedCandidatePair()
    55  	if icePair == nil || err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	local, err := newICECandidateFromICE(icePair.Local)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	remote, err := newICECandidateFromICE(icePair.Remote)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return &ICECandidatePair{Local: &local, Remote: &remote}, nil
    70  }
    71  
    72  // NewICETransport creates a new NewICETransport.
    73  func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) *ICETransport {
    74  	iceTransport := &ICETransport{
    75  		gatherer:      gatherer,
    76  		loggerFactory: loggerFactory,
    77  		log:           loggerFactory.NewLogger("ortc"),
    78  	}
    79  	iceTransport.setState(ICETransportStateNew)
    80  	return iceTransport
    81  }
    82  
    83  // Start incoming connectivity checks based on its configured role.
    84  func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *ICERole) error {
    85  	t.lock.Lock()
    86  	defer t.lock.Unlock()
    87  
    88  	if t.State() != ICETransportStateNew {
    89  		return errICETransportNotInNew
    90  	}
    91  
    92  	if gatherer != nil {
    93  		t.gatherer = gatherer
    94  	}
    95  
    96  	if err := t.ensureGatherer(); err != nil {
    97  		return err
    98  	}
    99  
   100  	agent := t.gatherer.getAgent()
   101  	if agent == nil {
   102  		return fmt.Errorf("%w: unable to start ICETransport", errICEAgentNotExist)
   103  	}
   104  
   105  	if err := agent.OnConnectionStateChange(func(iceState ice.ConnectionState) {
   106  		state := newICETransportStateFromICE(iceState)
   107  
   108  		t.setState(state)
   109  		t.onConnectionStateChange(state)
   110  	}); err != nil {
   111  		return err
   112  	}
   113  	if err := agent.OnSelectedCandidatePairChange(func(local, remote ice.Candidate) {
   114  		candidates, err := newICECandidatesFromICE([]ice.Candidate{local, remote})
   115  		if err != nil {
   116  			t.log.Warnf("%w: %s", errICECandiatesCoversionFailed, err)
   117  			return
   118  		}
   119  		t.onSelectedCandidatePairChange(NewICECandidatePair(&candidates[0], &candidates[1]))
   120  	}); err != nil {
   121  		return err
   122  	}
   123  
   124  	if role == nil {
   125  		controlled := ICERoleControlled
   126  		role = &controlled
   127  	}
   128  	t.role = *role
   129  
   130  	t.ctx, t.ctxCancel = context.WithCancel(context.Background())
   131  
   132  	// Drop the lock here to allow ICE candidates to be
   133  	// added so that the agent can complete a connection
   134  	t.lock.Unlock()
   135  
   136  	var iceConn *ice.Conn
   137  	var err error
   138  	switch *role {
   139  	case ICERoleControlling:
   140  		iceConn, err = agent.Dial(t.ctx,
   141  			params.UsernameFragment,
   142  			params.Password)
   143  
   144  	case ICERoleControlled:
   145  		iceConn, err = agent.Accept(t.ctx,
   146  			params.UsernameFragment,
   147  			params.Password)
   148  
   149  	default:
   150  		err = errICERoleUnknown
   151  	}
   152  
   153  	// Reacquire the lock to set the connection/mux
   154  	t.lock.Lock()
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	t.conn = iceConn
   160  
   161  	config := mux.Config{
   162  		Conn:          t.conn,
   163  		BufferSize:    int(t.gatherer.api.settingEngine.getReceiveMTU()),
   164  		LoggerFactory: t.loggerFactory,
   165  	}
   166  	t.mux = mux.NewMux(config)
   167  
   168  	return nil
   169  }
   170  
   171  // restart is not exposed currently because ORTC has users create a whole new ICETransport
   172  // so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs
   173  func (t *ICETransport) restart() error {
   174  	t.lock.Lock()
   175  	defer t.lock.Unlock()
   176  
   177  	agent := t.gatherer.getAgent()
   178  	if agent == nil {
   179  		return fmt.Errorf("%w: unable to restart ICETransport", errICEAgentNotExist)
   180  	}
   181  
   182  	if err := agent.Restart(t.gatherer.api.settingEngine.candidates.UsernameFragment, t.gatherer.api.settingEngine.candidates.Password); err != nil {
   183  		return err
   184  	}
   185  	return t.gatherer.Gather()
   186  }
   187  
   188  // Stop irreversibly stops the ICETransport.
   189  func (t *ICETransport) Stop() error {
   190  	t.lock.Lock()
   191  	defer t.lock.Unlock()
   192  
   193  	t.setState(ICETransportStateClosed)
   194  
   195  	if t.ctxCancel != nil {
   196  		t.ctxCancel()
   197  	}
   198  
   199  	if t.mux != nil {
   200  		return t.mux.Close()
   201  	} else if t.gatherer != nil {
   202  		return t.gatherer.Close()
   203  	}
   204  	return nil
   205  }
   206  
   207  // OnSelectedCandidatePairChange sets a handler that is invoked when a new
   208  // ICE candidate pair is selected
   209  func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandidatePair)) {
   210  	t.onSelectedCandidatePairChangeHandler.Store(f)
   211  }
   212  
   213  func (t *ICETransport) onSelectedCandidatePairChange(pair *ICECandidatePair) {
   214  	if handler, ok := t.onSelectedCandidatePairChangeHandler.Load().(func(*ICECandidatePair)); ok {
   215  		handler(pair)
   216  	}
   217  }
   218  
   219  // OnConnectionStateChange sets a handler that is fired when the ICE
   220  // connection state changes.
   221  func (t *ICETransport) OnConnectionStateChange(f func(ICETransportState)) {
   222  	t.onConnectionStateChangeHandler.Store(f)
   223  }
   224  
   225  func (t *ICETransport) onConnectionStateChange(state ICETransportState) {
   226  	if handler, ok := t.onConnectionStateChangeHandler.Load().(func(ICETransportState)); ok {
   227  		handler(state)
   228  	}
   229  	if handler, ok := t.internalOnConnectionStateChangeHandler.Load().(func(ICETransportState)); ok {
   230  		handler(state)
   231  	}
   232  }
   233  
   234  // Role indicates the current role of the ICE transport.
   235  func (t *ICETransport) Role() ICERole {
   236  	t.lock.RLock()
   237  	defer t.lock.RUnlock()
   238  
   239  	return t.role
   240  }
   241  
   242  // SetRemoteCandidates sets the sequence of candidates associated with the remote ICETransport.
   243  func (t *ICETransport) SetRemoteCandidates(remoteCandidates []ICECandidate) error {
   244  	t.lock.RLock()
   245  	defer t.lock.RUnlock()
   246  
   247  	if err := t.ensureGatherer(); err != nil {
   248  		return err
   249  	}
   250  
   251  	agent := t.gatherer.getAgent()
   252  	if agent == nil {
   253  		return fmt.Errorf("%w: unable to set remote candidates", errICEAgentNotExist)
   254  	}
   255  
   256  	for _, c := range remoteCandidates {
   257  		i, err := c.toICE()
   258  		if err != nil {
   259  			return err
   260  		}
   261  
   262  		if err = agent.AddRemoteCandidate(i); err != nil {
   263  			return err
   264  		}
   265  	}
   266  
   267  	return nil
   268  }
   269  
   270  // AddRemoteCandidate adds a candidate associated with the remote ICETransport.
   271  func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error {
   272  	t.lock.RLock()
   273  	defer t.lock.RUnlock()
   274  
   275  	var (
   276  		c   ice.Candidate
   277  		err error
   278  	)
   279  
   280  	if err = t.ensureGatherer(); err != nil {
   281  		return err
   282  	}
   283  
   284  	if remoteCandidate != nil {
   285  		if c, err = remoteCandidate.toICE(); err != nil {
   286  			return err
   287  		}
   288  	}
   289  
   290  	agent := t.gatherer.getAgent()
   291  	if agent == nil {
   292  		return fmt.Errorf("%w: unable to add remote candidates", errICEAgentNotExist)
   293  	}
   294  
   295  	return agent.AddRemoteCandidate(c)
   296  }
   297  
   298  // State returns the current ice transport state.
   299  func (t *ICETransport) State() ICETransportState {
   300  	if v, ok := t.state.Load().(ICETransportState); ok {
   301  		return v
   302  	}
   303  	return ICETransportState(0)
   304  }
   305  
   306  // GetLocalParameters returns an IceParameters object which provides information
   307  // uniquely identifying the local peer for the duration of the ICE session.
   308  func (t *ICETransport) GetLocalParameters() (ICEParameters, error) {
   309  	if err := t.ensureGatherer(); err != nil {
   310  		return ICEParameters{}, err
   311  	}
   312  
   313  	return t.gatherer.GetLocalParameters()
   314  }
   315  
   316  func (t *ICETransport) setState(i ICETransportState) {
   317  	t.state.Store(i)
   318  }
   319  
   320  func (t *ICETransport) newEndpoint(f mux.MatchFunc) *mux.Endpoint {
   321  	t.lock.Lock()
   322  	defer t.lock.Unlock()
   323  	return t.mux.NewEndpoint(f)
   324  }
   325  
   326  func (t *ICETransport) ensureGatherer() error {
   327  	if t.gatherer == nil {
   328  		return errICEGathererNotStarted
   329  	} else if t.gatherer.getAgent() == nil {
   330  		if err := t.gatherer.createAgent(); err != nil {
   331  			return err
   332  		}
   333  	}
   334  
   335  	return nil
   336  }
   337  
   338  func (t *ICETransport) collectStats(collector *statsReportCollector) {
   339  	t.lock.Lock()
   340  	conn := t.conn
   341  	t.lock.Unlock()
   342  
   343  	collector.Collecting()
   344  
   345  	stats := TransportStats{
   346  		Timestamp: statsTimestampFrom(time.Now()),
   347  		Type:      StatsTypeTransport,
   348  		ID:        "iceTransport",
   349  	}
   350  
   351  	if conn != nil {
   352  		stats.BytesSent = conn.BytesSent()
   353  		stats.BytesReceived = conn.BytesReceived()
   354  	}
   355  
   356  	collector.Collect(stats.ID, stats)
   357  }
   358  
   359  func (t *ICETransport) haveRemoteCredentialsChange(newUfrag, newPwd string) bool {
   360  	t.lock.Lock()
   361  	defer t.lock.Unlock()
   362  
   363  	agent := t.gatherer.getAgent()
   364  	if agent == nil {
   365  		return false
   366  	}
   367  
   368  	uFrag, uPwd, err := agent.GetRemoteUserCredentials()
   369  	if err != nil {
   370  		return false
   371  	}
   372  
   373  	return uFrag != newUfrag || uPwd != newPwd
   374  }
   375  
   376  func (t *ICETransport) setRemoteCredentials(newUfrag, newPwd string) error {
   377  	t.lock.Lock()
   378  	defer t.lock.Unlock()
   379  
   380  	agent := t.gatherer.getAgent()
   381  	if agent == nil {
   382  		return fmt.Errorf("%w: unable to SetRemoteCredentials", errICEAgentNotExist)
   383  	}
   384  
   385  	return agent.SetRemoteCredentials(newUfrag, newPwd)
   386  }