@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.
- package/README.md +33 -19
- package/dist/bridge.js +3 -3
- package/dist/bridge.js.map +1 -1
- package/dist/engineProcess.d.ts +5 -0
- package/dist/engineProcess.d.ts.map +1 -1
- package/dist/engineProcess.js +6 -3
- package/dist/engineProcess.js.map +1 -1
- package/dist/protocol.d.ts +39 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/docs/BOUNDARY.md +4 -2
- package/docs/PROTOCOL.md +70 -7
- package/engine/Cargo.lock +6 -8
- package/engine/Cargo.toml +13 -1
- package/engine/src/audio.rs +398 -0
- package/engine/src/clipboard.rs +0 -34
- package/engine/src/convert.rs +0 -170
- package/engine/src/cursor.rs +0 -23
- package/engine/src/input.rs +0 -65
- package/engine/src/input_mac.rs +0 -11
- package/engine/src/keeper.rs +0 -137
- package/engine/src/keymap.rs +0 -107
- package/engine/src/keymap_mac.rs +0 -25
- package/engine/src/mac.rs +1 -28
- package/engine/src/main.rs +22 -0
- package/engine/src/peer.rs +297 -0
- package/engine/src/portal.rs +0 -18
- package/engine/src/protocol.rs +22 -162
- package/engine/src/session.rs +176 -3
- package/engine/src/win.rs +1 -0
- package/engine/src/x11.rs +0 -13
- package/engine/vendor/rtc-ice/Cargo.toml +137 -0
- package/engine/vendor/rtc-ice/DESKLINK-PATCH.md +111 -0
- package/engine/vendor/rtc-ice/LICENSE-APACHE +201 -0
- package/engine/vendor/rtc-ice/LICENSE-MIT +21 -0
- package/engine/vendor/rtc-ice/README.md +30 -0
- package/engine/vendor/rtc-ice/examples/ping_pong.rs +376 -0
- package/engine/vendor/rtc-ice/src/agent/agent_config.rs +126 -0
- package/engine/vendor/rtc-ice/src/agent/agent_proto.rs +172 -0
- package/engine/vendor/rtc-ice/src/agent/agent_selector.rs +647 -0
- package/engine/vendor/rtc-ice/src/agent/agent_stats.rs +292 -0
- package/engine/vendor/rtc-ice/src/agent/agent_test.rs +3594 -0
- package/engine/vendor/rtc-ice/src/agent/mod.rs +1753 -0
- package/engine/vendor/rtc-ice/src/attributes/control/control_test.rs +168 -0
- package/engine/vendor/rtc-ice/src/attributes/control/mod.rs +142 -0
- package/engine/vendor/rtc-ice/src/attributes/mod.rs +6 -0
- package/engine/vendor/rtc-ice/src/attributes/priority/mod.rs +37 -0
- package/engine/vendor/rtc-ice/src/attributes/priority/priority_test.rs +39 -0
- package/engine/vendor/rtc-ice/src/attributes/use_candidate/mod.rs +33 -0
- package/engine/vendor/rtc-ice/src/attributes/use_candidate/use_candidate_test.rs +19 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_host.rs +53 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_pair.rs +215 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_pair_test.rs +201 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_peer_reflexive.rs +49 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_relay.rs +53 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_relay_test.rs +114 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_server_reflexive.rs +53 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_server_reflexive_test.rs +91 -0
- package/engine/vendor/rtc-ice/src/candidate/candidate_test.rs +540 -0
- package/engine/vendor/rtc-ice/src/candidate/mod.rs +679 -0
- package/engine/vendor/rtc-ice/src/dl_trace.rs +49 -0
- package/engine/vendor/rtc-ice/src/lib.rs +88 -0
- package/engine/vendor/rtc-ice/src/mdns/mdns_test.rs +150 -0
- package/engine/vendor/rtc-ice/src/mdns/mod.rs +69 -0
- package/engine/vendor/rtc-ice/src/network_type/mod.rs +162 -0
- package/engine/vendor/rtc-ice/src/network_type/network_type_test.rs +95 -0
- package/engine/vendor/rtc-ice/src/rand/mod.rs +91 -0
- package/engine/vendor/rtc-ice/src/rand/rand_test.rs +115 -0
- package/engine/vendor/rtc-ice/src/state/mod.rs +106 -0
- package/engine/vendor/rtc-ice/src/state/state_test.rs +46 -0
- package/engine/vendor/rtc-ice/src/stats/mod.rs +166 -0
- package/engine/vendor/rtc-ice/src/tcp_type/mod.rs +47 -0
- package/engine/vendor/rtc-ice/src/tcp_type/tcp_type_test.rs +18 -0
- package/engine/vendor/rtc-ice/src/url/mod.rs +266 -0
- package/engine/vendor/rtc-ice/src/url/url_test.rs +142 -0
- 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
|
+
}
|