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 }