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 }