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