@desklink/host 0.5.3 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +33 -19
  2. package/dist/bridge.js +3 -3
  3. package/dist/bridge.js.map +1 -1
  4. package/dist/engineProcess.d.ts +5 -0
  5. package/dist/engineProcess.d.ts.map +1 -1
  6. package/dist/engineProcess.js +6 -3
  7. package/dist/engineProcess.js.map +1 -1
  8. package/dist/protocol.d.ts +39 -0
  9. package/dist/protocol.d.ts.map +1 -1
  10. package/dist/protocol.js.map +1 -1
  11. package/docs/BOUNDARY.md +4 -2
  12. package/docs/PROTOCOL.md +70 -7
  13. package/engine/Cargo.lock +6 -8
  14. package/engine/Cargo.toml +13 -1
  15. package/engine/src/audio.rs +398 -0
  16. package/engine/src/clipboard.rs +0 -34
  17. package/engine/src/convert.rs +0 -170
  18. package/engine/src/cursor.rs +0 -23
  19. package/engine/src/input.rs +0 -65
  20. package/engine/src/input_mac.rs +0 -11
  21. package/engine/src/keeper.rs +0 -137
  22. package/engine/src/keymap.rs +0 -107
  23. package/engine/src/keymap_mac.rs +0 -25
  24. package/engine/src/mac.rs +1 -28
  25. package/engine/src/main.rs +22 -0
  26. package/engine/src/peer.rs +297 -0
  27. package/engine/src/portal.rs +0 -18
  28. package/engine/src/protocol.rs +22 -162
  29. package/engine/src/session.rs +176 -3
  30. package/engine/src/win.rs +1 -0
  31. package/engine/src/x11.rs +0 -13
  32. package/engine/vendor/rtc-ice/Cargo.toml +137 -0
  33. package/engine/vendor/rtc-ice/DESKLINK-PATCH.md +111 -0
  34. package/engine/vendor/rtc-ice/LICENSE-APACHE +201 -0
  35. package/engine/vendor/rtc-ice/LICENSE-MIT +21 -0
  36. package/engine/vendor/rtc-ice/README.md +30 -0
  37. package/engine/vendor/rtc-ice/examples/ping_pong.rs +376 -0
  38. package/engine/vendor/rtc-ice/src/agent/agent_config.rs +126 -0
  39. package/engine/vendor/rtc-ice/src/agent/agent_proto.rs +172 -0
  40. package/engine/vendor/rtc-ice/src/agent/agent_selector.rs +647 -0
  41. package/engine/vendor/rtc-ice/src/agent/agent_stats.rs +292 -0
  42. package/engine/vendor/rtc-ice/src/agent/agent_test.rs +3594 -0
  43. package/engine/vendor/rtc-ice/src/agent/mod.rs +1753 -0
  44. package/engine/vendor/rtc-ice/src/attributes/control/control_test.rs +168 -0
  45. package/engine/vendor/rtc-ice/src/attributes/control/mod.rs +142 -0
  46. package/engine/vendor/rtc-ice/src/attributes/mod.rs +6 -0
  47. package/engine/vendor/rtc-ice/src/attributes/priority/mod.rs +37 -0
  48. package/engine/vendor/rtc-ice/src/attributes/priority/priority_test.rs +39 -0
  49. package/engine/vendor/rtc-ice/src/attributes/use_candidate/mod.rs +33 -0
  50. package/engine/vendor/rtc-ice/src/attributes/use_candidate/use_candidate_test.rs +19 -0
  51. package/engine/vendor/rtc-ice/src/candidate/candidate_host.rs +53 -0
  52. package/engine/vendor/rtc-ice/src/candidate/candidate_pair.rs +215 -0
  53. package/engine/vendor/rtc-ice/src/candidate/candidate_pair_test.rs +201 -0
  54. package/engine/vendor/rtc-ice/src/candidate/candidate_peer_reflexive.rs +49 -0
  55. package/engine/vendor/rtc-ice/src/candidate/candidate_relay.rs +53 -0
  56. package/engine/vendor/rtc-ice/src/candidate/candidate_relay_test.rs +114 -0
  57. package/engine/vendor/rtc-ice/src/candidate/candidate_server_reflexive.rs +53 -0
  58. package/engine/vendor/rtc-ice/src/candidate/candidate_server_reflexive_test.rs +91 -0
  59. package/engine/vendor/rtc-ice/src/candidate/candidate_test.rs +540 -0
  60. package/engine/vendor/rtc-ice/src/candidate/mod.rs +679 -0
  61. package/engine/vendor/rtc-ice/src/dl_trace.rs +49 -0
  62. package/engine/vendor/rtc-ice/src/lib.rs +88 -0
  63. package/engine/vendor/rtc-ice/src/mdns/mdns_test.rs +150 -0
  64. package/engine/vendor/rtc-ice/src/mdns/mod.rs +69 -0
  65. package/engine/vendor/rtc-ice/src/network_type/mod.rs +162 -0
  66. package/engine/vendor/rtc-ice/src/network_type/network_type_test.rs +95 -0
  67. package/engine/vendor/rtc-ice/src/rand/mod.rs +91 -0
  68. package/engine/vendor/rtc-ice/src/rand/rand_test.rs +115 -0
  69. package/engine/vendor/rtc-ice/src/state/mod.rs +106 -0
  70. package/engine/vendor/rtc-ice/src/state/state_test.rs +46 -0
  71. package/engine/vendor/rtc-ice/src/stats/mod.rs +166 -0
  72. package/engine/vendor/rtc-ice/src/tcp_type/mod.rs +47 -0
  73. package/engine/vendor/rtc-ice/src/tcp_type/tcp_type_test.rs +18 -0
  74. package/engine/vendor/rtc-ice/src/url/mod.rs +266 -0
  75. package/engine/vendor/rtc-ice/src/url/url_test.rs +142 -0
  76. package/package.json +8 -3
@@ -0,0 +1,647 @@
1
+ use crate::agent::Agent;
2
+ use log::{debug, error, trace, warn};
3
+ use std::net::SocketAddr;
4
+ use std::time::Instant;
5
+ use stun::attributes::*;
6
+ use stun::fingerprint::*;
7
+ use stun::integrity::*;
8
+ use stun::message::*;
9
+ use stun::textattrs::*;
10
+
11
+ use crate::attributes::{control::*, priority::*, use_candidate::*};
12
+ use crate::candidate::{candidate_pair::*, *};
13
+ // desklink scout trace (dl-roam-postnom-attr):
14
+ use crate::dl_trace;
15
+ use crate::dl_trace::dl_ice;
16
+
17
+ trait ControllingSelector {
18
+ fn start(&mut self, now: Instant);
19
+ fn contact_candidates(&mut self, now: Instant);
20
+ fn ping_candidate(&mut self, now: Instant, local_index: usize, remote_index: usize);
21
+ fn handle_success_response(
22
+ &mut self,
23
+ now: Instant,
24
+ m: &Message,
25
+ local_index: usize,
26
+ remote_index: usize,
27
+ remote_addr: SocketAddr,
28
+ );
29
+ fn handle_binding_request(
30
+ &mut self,
31
+ now: Instant,
32
+ m: &Message,
33
+ local_index: usize,
34
+ remote_index: usize,
35
+ );
36
+ }
37
+
38
+ trait ControlledSelector {
39
+ fn start(&mut self, now: Instant);
40
+ fn contact_candidates(&mut self, now: Instant);
41
+ fn ping_candidate(&mut self, now: Instant, local_index: usize, remote_index: usize);
42
+ fn handle_success_response(
43
+ &mut self,
44
+ now: Instant,
45
+ m: &Message,
46
+ local_index: usize,
47
+ remote_index: usize,
48
+ remote_addr: SocketAddr,
49
+ );
50
+ fn handle_binding_request(
51
+ &mut self,
52
+ now: Instant,
53
+ m: &Message,
54
+ local_index: usize,
55
+ remote_index: usize,
56
+ );
57
+ }
58
+
59
+ impl Agent {
60
+ /// Whether `index` has been waiting long enough to be nominated, as of `now`.
61
+ ///
62
+ /// `now` is supplied by the caller rather than sampled here so that one nomination decision
63
+ /// makes one time observation: both halves of a pair are tested against the same instant.
64
+ fn is_nominatable(&self, now: Instant, index: usize, is_local: bool) -> bool {
65
+ let c = if is_local {
66
+ &self.local_candidates[index]
67
+ } else {
68
+ &self.remote_candidates[index]
69
+ };
70
+ let min_wait = match c.candidate_type() {
71
+ CandidateType::Host => self.host_acceptance_min_wait,
72
+ CandidateType::ServerReflexive => self.srflx_acceptance_min_wait,
73
+ CandidateType::PeerReflexive => self.prflx_acceptance_min_wait,
74
+ CandidateType::Relay => self.relay_acceptance_min_wait,
75
+ CandidateType::Unspecified => {
76
+ error!(
77
+ "is_nominatable invalid candidate type {}",
78
+ c.candidate_type()
79
+ );
80
+ return false;
81
+ }
82
+ };
83
+ now.saturating_duration_since(self.start_time) > min_wait
84
+ }
85
+
86
+ fn nominate_pair(&mut self, now: Instant) {
87
+ let result = {
88
+ let Some(remote_credentials) = &self.ufrag_pwd.remote_credentials else {
89
+ error!("ufrag_pwd.remote_credentials is none");
90
+ return;
91
+ };
92
+ if let Some(pair_index) = &self.nominated_pair {
93
+ let pair = &self.candidate_pairs[*pair_index];
94
+ // The controlling agent MUST include the USE-CANDIDATE attribute in
95
+ // order to nominate a candidate pair (Section 8.1.1). The controlled
96
+ // agent MUST NOT include the USE-CANDIDATE attribute in a Binding
97
+ // request.
98
+
99
+ let (msg, result) = {
100
+ let username = remote_credentials.ufrag.clone()
101
+ + ":"
102
+ + self.ufrag_pwd.local_credentials.ufrag.as_str();
103
+ let mut msg = Message::new();
104
+ let result = msg.build(&[
105
+ Box::new(BINDING_REQUEST),
106
+ Box::new(TransactionId::new()),
107
+ Box::new(Username::new(ATTR_USERNAME, username)),
108
+ Box::<UseCandidateAttr>::default(),
109
+ Box::new(AttrControlling(self.tie_breaker)),
110
+ Box::new(PriorityAttr(pair.local_priority)),
111
+ Box::new(MessageIntegrity::new_short_term_integrity_with_provider(
112
+ remote_credentials.pwd.clone(),
113
+ self.crypto_provider.crypto(),
114
+ )),
115
+ Box::new(FINGERPRINT),
116
+ ]);
117
+ (msg, result)
118
+ };
119
+
120
+ if let Err(err) = result {
121
+ error!("{}", err);
122
+ None
123
+ } else {
124
+ trace!(
125
+ "ping STUN (nominate candidate pair from {} to {}",
126
+ self.local_candidates[pair.local_index],
127
+ self.remote_candidates[pair.remote_index],
128
+ );
129
+ let local = pair.local_index;
130
+ let remote = pair.remote_index;
131
+ Some((msg, local, remote))
132
+ }
133
+ } else {
134
+ None
135
+ }
136
+ };
137
+
138
+ if let Some((msg, local, remote)) = result {
139
+ // desklink scout trace: a USE-CANDIDATE (nomination) request going
140
+ // out — the first transmission, or a tick-gated retransmission
141
+ // while the previous one is still unacknowledged.
142
+ if dl_trace::enabled() {
143
+ dl_ice!(
144
+ "{} NOMINATE txn={:?} local={} remote={} nominated_pair={:?} outstanding={}",
145
+ self.get_name(),
146
+ msg.transaction_id,
147
+ self.local_candidates[local].addr(),
148
+ self.remote_candidates[remote].addr(),
149
+ self.nominated_pair,
150
+ self.pending_binding_requests.len(),
151
+ );
152
+ }
153
+ self.send_binding_request(now, &msg, local, remote);
154
+ }
155
+ }
156
+
157
+ pub(crate) fn start(&mut self, now: Instant) {
158
+ if self.is_controlling {
159
+ ControllingSelector::start(self, now);
160
+ } else {
161
+ ControlledSelector::start(self, now);
162
+ }
163
+ }
164
+
165
+ pub(crate) fn contact_candidates(&mut self, now: Instant) {
166
+ if self.is_controlling {
167
+ ControllingSelector::contact_candidates(self, now);
168
+ } else {
169
+ ControlledSelector::contact_candidates(self, now);
170
+ }
171
+ }
172
+
173
+ pub(crate) fn ping_candidate(&mut self, now: Instant, local_index: usize, remote_index: usize) {
174
+ trace!("[{}]: ping_candidate", self.get_name());
175
+
176
+ if self.is_controlling {
177
+ ControllingSelector::ping_candidate(self, now, local_index, remote_index);
178
+ } else {
179
+ ControlledSelector::ping_candidate(self, now, local_index, remote_index);
180
+ }
181
+ }
182
+
183
+ pub(crate) fn handle_success_response(
184
+ &mut self,
185
+ now: Instant,
186
+ m: &Message,
187
+ local_index: usize,
188
+ remote_index: usize,
189
+ remote_addr: SocketAddr,
190
+ ) {
191
+ if self.is_controlling {
192
+ ControllingSelector::handle_success_response(
193
+ self,
194
+ now,
195
+ m,
196
+ local_index,
197
+ remote_index,
198
+ remote_addr,
199
+ );
200
+ } else {
201
+ ControlledSelector::handle_success_response(
202
+ self,
203
+ now,
204
+ m,
205
+ local_index,
206
+ remote_index,
207
+ remote_addr,
208
+ );
209
+ }
210
+ }
211
+
212
+ pub(crate) fn handle_binding_request(
213
+ &mut self,
214
+ now: Instant,
215
+ m: &Message,
216
+ local_index: usize,
217
+ remote_index: usize,
218
+ ) {
219
+ if self.is_controlling {
220
+ ControllingSelector::handle_binding_request(self, now, m, local_index, remote_index);
221
+ } else {
222
+ ControlledSelector::handle_binding_request(self, now, m, local_index, remote_index);
223
+ }
224
+ }
225
+ }
226
+
227
+ impl ControllingSelector for Agent {
228
+ fn start(&mut self, now: Instant) {
229
+ self.nominated_pair = None;
230
+ self.start_time = now;
231
+ }
232
+
233
+ fn contact_candidates(&mut self, now: Instant) {
234
+ // A lite selector should not contact candidates
235
+ if self.lite {
236
+ // This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2
237
+ trace!("now falling back to full agent");
238
+ }
239
+
240
+ let nominated_pair_is_some = self.nominated_pair.is_some();
241
+
242
+ // desklink scout trace: which nomination branch this contact() pass took.
243
+ if dl_trace::enabled() {
244
+ let best = self
245
+ .get_best_valid_candidate_pair()
246
+ .map(|i| {
247
+ let p = &self.candidate_pairs[i];
248
+ format!(
249
+ "{}<->{}",
250
+ self.local_candidates[p.local_index].addr(),
251
+ self.remote_candidates[p.remote_index].addr()
252
+ )
253
+ })
254
+ .unwrap_or_else(|| "-none-valid-".to_string());
255
+ dl_ice!(
256
+ "{} contact_branch selected={:?} nominated={nominated_pair_is_some} best_valid={best} pairs={}",
257
+ self.get_name(),
258
+ self.selected_pair,
259
+ self.candidate_pairs.len(),
260
+ );
261
+ }
262
+
263
+ if self.get_selected_pair().is_some() {
264
+ if self.validate_selected_pair(now) {
265
+ self.check_keepalive(now);
266
+ }
267
+ } else if nominated_pair_is_some {
268
+ self.nominate_pair(now);
269
+ } else {
270
+ let has_nominated_pair = if let Some(pair_index) = self.get_best_valid_candidate_pair()
271
+ {
272
+ let p = self.candidate_pairs[pair_index];
273
+ self.is_nominatable(now, p.local_index, true)
274
+ && self.is_nominatable(now, p.remote_index, false)
275
+ } else {
276
+ false
277
+ };
278
+
279
+ if has_nominated_pair {
280
+ if let Some(pair_index) = self.get_best_valid_candidate_pair() {
281
+ let p = &mut self.candidate_pairs[pair_index];
282
+ trace!(
283
+ "Nominatable pair found, nominating ({}, {})",
284
+ self.local_candidates[p.local_index],
285
+ self.remote_candidates[p.remote_index],
286
+ );
287
+ p.nominated = true;
288
+ self.nominated_pair = Some(pair_index);
289
+ }
290
+
291
+ self.nominate_pair(now);
292
+ } else {
293
+ self.ping_all_candidates(now);
294
+ }
295
+ }
296
+ }
297
+
298
+ fn ping_candidate(&mut self, now: Instant, local_index: usize, remote_index: usize) {
299
+ let (msg, result) = {
300
+ let Some(remote_credentials) = &self.ufrag_pwd.remote_credentials else {
301
+ error!("ufrag_pwd.remote_credentials is none");
302
+ return;
303
+ };
304
+
305
+ let username = remote_credentials.ufrag.clone()
306
+ + ":"
307
+ + self.ufrag_pwd.local_credentials.ufrag.as_str();
308
+ let mut msg = Message::new();
309
+ let result = msg.build(&[
310
+ Box::new(BINDING_REQUEST),
311
+ Box::new(TransactionId::new()),
312
+ Box::new(Username::new(ATTR_USERNAME, username)),
313
+ Box::new(AttrControlling(self.tie_breaker)),
314
+ Box::new(PriorityAttr(self.local_candidates[local_index].priority())),
315
+ Box::new(MessageIntegrity::new_short_term_integrity_with_provider(
316
+ remote_credentials.pwd.clone(),
317
+ self.crypto_provider.crypto(),
318
+ )),
319
+ Box::new(FINGERPRINT),
320
+ ]);
321
+ (msg, result)
322
+ };
323
+
324
+ if let Err(err) = result {
325
+ error!("{}", err);
326
+ } else {
327
+ self.send_binding_request(now, &msg, local_index, remote_index);
328
+ }
329
+ }
330
+
331
+ fn handle_success_response(
332
+ &mut self,
333
+ now: Instant,
334
+ m: &Message,
335
+ local_index: usize,
336
+ remote_index: usize,
337
+ remote_addr: SocketAddr,
338
+ ) {
339
+ if let Some(pending_request) = self.handle_inbound_binding_success(now, m.transaction_id) {
340
+ let transaction_addr = pending_request.destination;
341
+
342
+ // Assert that NAT is not symmetric
343
+ // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
344
+ if transaction_addr != remote_addr {
345
+ debug!(
346
+ "discard message: transaction source and destination does not match expected({}), actual({})",
347
+ transaction_addr, remote_index
348
+ );
349
+ return;
350
+ }
351
+
352
+ trace!(
353
+ "inbound STUN (SuccessResponse) from {} to {}",
354
+ self.remote_candidates[remote_index], self.local_candidates[local_index]
355
+ );
356
+ let selected_pair_is_none = self.get_selected_pair().is_none();
357
+
358
+ if let Some(pair_index) = self.find_pair(local_index, remote_index) {
359
+ let p = &mut self.candidate_pairs[pair_index];
360
+ p.state = CandidatePairState::Succeeded;
361
+
362
+ // Calculate RTT and track response received
363
+ let rtt = now.duration_since(pending_request.timestamp);
364
+ p.on_response_received(rtt);
365
+
366
+ trace!(
367
+ "Found valid candidate pair: {} (local_addr {} <-> remote_addr {}), p.state: {}, isUseCandidate: {}, {}, rtt: {:.3}ms",
368
+ *p,
369
+ self.local_candidates[p.local_index].addr(),
370
+ self.remote_candidates[p.remote_index].addr(),
371
+ p.state,
372
+ pending_request.is_use_candidate,
373
+ selected_pair_is_none,
374
+ rtt.as_millis()
375
+ );
376
+ if pending_request.is_use_candidate && selected_pair_is_none {
377
+ // desklink scout trace: the browser's answer to a nomination
378
+ // request — this is what converts nominated into selected.
379
+ if dl_trace::enabled() {
380
+ dl_ice!(
381
+ "{} NOMINATION_ACK txn={:?} rtt_us={} local={} remote={}",
382
+ self.get_name(),
383
+ m.transaction_id,
384
+ rtt.as_micros(),
385
+ self.local_candidates[local_index].addr(),
386
+ self.remote_candidates[remote_index].addr(),
387
+ );
388
+ }
389
+ self.set_selected_pair(Some(now), Some(pair_index));
390
+ } else {
391
+ // desklink patch: nomination is decided in `contact_candidates`,
392
+ // and that only runs on a `check_interval` tick, so a pair that
393
+ // becomes valid just after a tick waits a full tick for its
394
+ // USE-CANDIDATE. Ask for the check pass now, when this is the pair
395
+ // that tick would nominate. `request_connectivity_check` is the
396
+ // deferred-check request (issue #88): it makes `poll_timeout`
397
+ // report an already-due deadline, so the driver runs `contact()`
398
+ // at once instead of at the next tick. See DESKLINK-PATCH.md.
399
+ if self.get_selected_pair().is_none()
400
+ && self.nominated_pair.is_none()
401
+ && self.get_best_valid_candidate_pair() == Some(pair_index)
402
+ {
403
+ let p = &self.candidate_pairs[pair_index];
404
+ if self.is_nominatable(now, p.local_index, true)
405
+ && self.is_nominatable(now, p.remote_index, false)
406
+ {
407
+ self.request_connectivity_check();
408
+ }
409
+ }
410
+ }
411
+ } else {
412
+ // This shouldn't happen
413
+ error!("Success response from invalid candidate pair");
414
+ }
415
+ } else {
416
+ warn!(
417
+ "discard message from ({}), unknown TransactionID 0x{:?}",
418
+ self.remote_candidates[remote_index], m.transaction_id
419
+ );
420
+ }
421
+ }
422
+
423
+ fn handle_binding_request(
424
+ &mut self,
425
+ now: Instant,
426
+ m: &Message,
427
+ local_index: usize,
428
+ remote_index: usize,
429
+ ) {
430
+ self.send_binding_success(now, m, local_index, remote_index);
431
+ trace!("controllingSelector: sendBindingSuccess");
432
+
433
+ if let Some(pair_index) = self.find_pair(local_index, remote_index) {
434
+ // Track request received on the candidate pair
435
+ self.candidate_pairs[pair_index].on_request_received();
436
+
437
+ let p = &self.candidate_pairs[pair_index];
438
+ let nominated_pair_is_none = self.nominated_pair.is_none();
439
+
440
+ // desklink scout trace: an inbound binding request on the
441
+ // controlling side — the browser's own connectivity check.
442
+ if dl_trace::enabled() {
443
+ dl_ice!(
444
+ "{} RECV_REQUEST controlling use_candidate={} pair_state={:?} nominated={nominated_pair_is_none}",
445
+ self.get_name(),
446
+ m.contains(ATTR_USE_CANDIDATE),
447
+ p.state,
448
+ );
449
+ }
450
+
451
+ trace!(
452
+ "controllingSelector: after findPair {}, p.state: {}, {}",
453
+ p,
454
+ p.state,
455
+ nominated_pair_is_none,
456
+ //self.get_selected_pair().await.is_none() //, {}
457
+ );
458
+ if p.state == CandidatePairState::Succeeded
459
+ && nominated_pair_is_none
460
+ && self.get_selected_pair().is_none()
461
+ {
462
+ if let Some(best_pair_index) = self.get_best_available_pair() {
463
+ trace!(
464
+ "controllingSelector: getBestAvailableCandidatePair {}",
465
+ best_pair_index
466
+ );
467
+ if best_pair_index == pair_index
468
+ && self.is_nominatable(now, p.local_index, true)
469
+ && self.is_nominatable(now, p.remote_index, false)
470
+ {
471
+ trace!(
472
+ "The candidate ({}, {}) is the best candidate available, marking it as nominated",
473
+ p.local_index, p.remote_index
474
+ );
475
+ self.nominated_pair = Some(pair_index);
476
+ self.nominate_pair(now);
477
+ }
478
+ } else {
479
+ trace!("No best pair available");
480
+ }
481
+ }
482
+ } else {
483
+ trace!("controllingSelector: addPair");
484
+ self.add_pair(local_index, remote_index);
485
+ // Track request received on newly created pair
486
+ if let Some(pair_index) = self.find_pair(local_index, remote_index) {
487
+ self.candidate_pairs[pair_index].on_request_received();
488
+ }
489
+ }
490
+ }
491
+ }
492
+
493
+ impl ControlledSelector for Agent {
494
+ fn start(&mut self, _now: Instant) {}
495
+
496
+ fn contact_candidates(&mut self, now: Instant) {
497
+ // A lite selector should not contact candidates
498
+ if self.lite {
499
+ self.validate_selected_pair(now);
500
+ } else if self.get_selected_pair().is_some() {
501
+ if self.validate_selected_pair(now) {
502
+ self.check_keepalive(now);
503
+ }
504
+ } else {
505
+ self.ping_all_candidates(now);
506
+ }
507
+ }
508
+
509
+ fn ping_candidate(&mut self, now: Instant, local_index: usize, remote_index: usize) {
510
+ let (msg, result) = {
511
+ let Some(remote_credentials) = &self.ufrag_pwd.remote_credentials else {
512
+ error!("ufrag_pwd.remote_credentials is none");
513
+ return;
514
+ };
515
+
516
+ let username = remote_credentials.ufrag.clone()
517
+ + ":"
518
+ + self.ufrag_pwd.local_credentials.ufrag.as_str();
519
+ let mut msg = Message::new();
520
+ let result = msg.build(&[
521
+ Box::new(BINDING_REQUEST),
522
+ Box::new(TransactionId::new()),
523
+ Box::new(Username::new(ATTR_USERNAME, username)),
524
+ Box::new(AttrControlled(self.tie_breaker)),
525
+ Box::new(PriorityAttr(self.local_candidates[local_index].priority())),
526
+ Box::new(MessageIntegrity::new_short_term_integrity_with_provider(
527
+ remote_credentials.pwd.clone(),
528
+ self.crypto_provider.crypto(),
529
+ )),
530
+ Box::new(FINGERPRINT),
531
+ ]);
532
+ (msg, result)
533
+ };
534
+
535
+ if let Err(err) = result {
536
+ error!("{}", err);
537
+ } else {
538
+ self.send_binding_request(now, &msg, local_index, remote_index);
539
+ }
540
+ }
541
+
542
+ fn handle_success_response(
543
+ &mut self,
544
+ now: Instant,
545
+ m: &Message,
546
+ local_index: usize,
547
+ remote_index: usize,
548
+ remote_addr: SocketAddr,
549
+ ) {
550
+ // https://tools.ietf.org/html/rfc8445#section-7.3.1.5
551
+ // If the controlled agent does not accept the request from the
552
+ // controlling agent, the controlled agent MUST reject the nomination
553
+ // request with an appropriate error code response (e.g., 400)
554
+ // [RFC5389].
555
+
556
+ if let Some(pending_request) = self.handle_inbound_binding_success(now, m.transaction_id) {
557
+ let transaction_addr = pending_request.destination;
558
+
559
+ // Assert that NAT is not symmetric
560
+ // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
561
+ if transaction_addr != remote_addr {
562
+ debug!(
563
+ "discard message: transaction source and destination does not match expected({}), actual({})",
564
+ transaction_addr, remote_index
565
+ );
566
+ return;
567
+ }
568
+
569
+ trace!(
570
+ "inbound STUN (SuccessResponse) from {} to {}",
571
+ self.remote_candidates[remote_index], self.local_candidates[local_index]
572
+ );
573
+
574
+ if let Some(pair_index) = self.find_pair(local_index, remote_index) {
575
+ let p = &mut self.candidate_pairs[pair_index];
576
+ p.state = CandidatePairState::Succeeded;
577
+
578
+ // Calculate RTT and track response received
579
+ let rtt = now.duration_since(pending_request.timestamp);
580
+ p.on_response_received(rtt);
581
+
582
+ trace!(
583
+ "Found valid candidate pair: {} (local_addr {} <-> remote_addr {}), rtt: {:.3}ms",
584
+ *p,
585
+ self.local_candidates[p.local_index].addr(),
586
+ self.remote_candidates[p.remote_index].addr(),
587
+ rtt.as_millis()
588
+ );
589
+ } else {
590
+ // This shouldn't happen
591
+ error!("Success response from invalid candidate pair");
592
+ }
593
+ } else {
594
+ warn!(
595
+ "discard message from ({}), unknown TransactionID 0x{:?}",
596
+ self.remote_candidates[remote_index], m.transaction_id
597
+ );
598
+ }
599
+ }
600
+
601
+ fn handle_binding_request(
602
+ &mut self,
603
+ now: Instant,
604
+ m: &Message,
605
+ local_index: usize,
606
+ remote_index: usize,
607
+ ) {
608
+ if self.find_pair(local_index, remote_index).is_none() {
609
+ self.add_pair(local_index, remote_index);
610
+ }
611
+
612
+ if let Some(pair_index) = self.find_pair(local_index, remote_index) {
613
+ // Track request received on the candidate pair
614
+ self.candidate_pairs[pair_index].on_request_received();
615
+
616
+ let p = &self.candidate_pairs[pair_index];
617
+ let use_candidate = m.contains(ATTR_USE_CANDIDATE);
618
+ if use_candidate {
619
+ // https://tools.ietf.org/html/rfc8445#section-7.3.1.5
620
+
621
+ if p.state == CandidatePairState::Succeeded {
622
+ // If the state of this pair is Succeeded, it means that the check
623
+ // previously sent by this pair produced a successful response and
624
+ // generated a valid pair (Section 7.2.5.3.2). The agent sets the
625
+ // nominated flag value of the valid pair to true.
626
+ if self.get_selected_pair().is_none() {
627
+ self.set_selected_pair(Some(now), Some(pair_index));
628
+ }
629
+ self.send_binding_success(now, m, local_index, remote_index);
630
+ } else {
631
+ // If the received Binding request triggered a new check to be
632
+ // enqueued in the triggered-check queue (Section 7.3.1.4), once the
633
+ // check is sent and if it generates a successful response, and
634
+ // generates a valid pair, the agent sets the nominated flag of the
635
+ // pair to true. If the request fails (Section 7.2.5.2), the agent
636
+ // MUST remove the candidate pair from the valid list, set the
637
+ // candidate pair state to Failed, and set the checklist state to
638
+ // Failed.
639
+ self.ping_candidate(now, local_index, remote_index);
640
+ }
641
+ } else {
642
+ self.send_binding_success(now, m, local_index, remote_index);
643
+ self.ping_candidate(now, local_index, remote_index);
644
+ }
645
+ }
646
+ }
647
+ }