|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Original author: ekr@rtfm.com |
|
8 |
|
9 // Some of this code is cut-and-pasted from nICEr. Copyright is: |
|
10 |
|
11 /* |
|
12 Copyright (c) 2007, Adobe Systems, Incorporated |
|
13 All rights reserved. |
|
14 |
|
15 Redistribution and use in source and binary forms, with or without |
|
16 modification, are permitted provided that the following conditions are |
|
17 met: |
|
18 |
|
19 * Redistributions of source code must retain the above copyright |
|
20 notice, this list of conditions and the following disclaimer. |
|
21 |
|
22 * Redistributions in binary form must reproduce the above copyright |
|
23 notice, this list of conditions and the following disclaimer in the |
|
24 documentation and/or other materials provided with the distribution. |
|
25 |
|
26 * Neither the name of Adobe Systems, Network Resonance nor the names of its |
|
27 contributors may be used to endorse or promote products derived from |
|
28 this software without specific prior written permission. |
|
29 |
|
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
41 */ |
|
42 |
|
43 |
|
44 #include <string> |
|
45 #include <vector> |
|
46 |
|
47 #include "logging.h" |
|
48 #include "nsError.h" |
|
49 #include "mozilla/Scoped.h" |
|
50 |
|
51 // nICEr includes |
|
52 extern "C" { |
|
53 #include "nr_api.h" |
|
54 #include "registry.h" |
|
55 #include "async_timer.h" |
|
56 #include "ice_util.h" |
|
57 #include "transport_addr.h" |
|
58 #include "nr_crypto.h" |
|
59 #include "nr_socket.h" |
|
60 #include "nr_socket_local.h" |
|
61 #include "stun_client_ctx.h" |
|
62 #include "stun_server_ctx.h" |
|
63 #include "ice_ctx.h" |
|
64 #include "ice_candidate.h" |
|
65 #include "ice_handler.h" |
|
66 } |
|
67 |
|
68 // Local includes |
|
69 #include "nricectx.h" |
|
70 #include "nricemediastream.h" |
|
71 |
|
72 namespace mozilla { |
|
73 |
|
74 MOZ_MTLOG_MODULE("mtransport") |
|
75 |
|
76 static bool ToNrIceAddr(nr_transport_addr &addr, |
|
77 NrIceAddr *out) { |
|
78 int r; |
|
79 char addrstring[INET6_ADDRSTRLEN + 1]; |
|
80 |
|
81 r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring)); |
|
82 if (r) |
|
83 return false; |
|
84 out->host = addrstring; |
|
85 |
|
86 int port; |
|
87 r = nr_transport_addr_get_port(&addr, &port); |
|
88 if (r) |
|
89 return false; |
|
90 |
|
91 out->port = port; |
|
92 |
|
93 switch (addr.protocol) { |
|
94 case IPPROTO_TCP: |
|
95 out->transport = kNrIceTransportTcp; |
|
96 break; |
|
97 case IPPROTO_UDP: |
|
98 out->transport = kNrIceTransportUdp; |
|
99 break; |
|
100 default: |
|
101 MOZ_CRASH(); |
|
102 return false; |
|
103 } |
|
104 |
|
105 return true; |
|
106 } |
|
107 |
|
108 static bool ToNrIceCandidate(const nr_ice_candidate& candc, |
|
109 NrIceCandidate* out) { |
|
110 MOZ_ASSERT(out); |
|
111 int r; |
|
112 // Const-cast because the internal nICEr code isn't const-correct. |
|
113 nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc); |
|
114 |
|
115 if (!ToNrIceAddr(cand->addr, &out->cand_addr)) |
|
116 return false; |
|
117 |
|
118 if (cand->isock) { |
|
119 nr_transport_addr addr; |
|
120 r = nr_socket_getaddr(cand->isock->sock, &addr); |
|
121 if (r) |
|
122 return false; |
|
123 |
|
124 if (!ToNrIceAddr(addr, &out->local_addr)) |
|
125 return false; |
|
126 } |
|
127 |
|
128 NrIceCandidate::Type type; |
|
129 |
|
130 switch (cand->type) { |
|
131 case HOST: |
|
132 type = NrIceCandidate::ICE_HOST; |
|
133 break; |
|
134 case SERVER_REFLEXIVE: |
|
135 type = NrIceCandidate::ICE_SERVER_REFLEXIVE; |
|
136 break; |
|
137 case PEER_REFLEXIVE: |
|
138 type = NrIceCandidate::ICE_PEER_REFLEXIVE; |
|
139 break; |
|
140 case RELAYED: |
|
141 type = NrIceCandidate::ICE_RELAYED; |
|
142 break; |
|
143 default: |
|
144 return false; |
|
145 } |
|
146 |
|
147 out->type = type; |
|
148 out->codeword = candc.codeword; |
|
149 return true; |
|
150 } |
|
151 |
|
152 // Make an NrIceCandidate from the candidate |cand|. |
|
153 // This is not a member fxn because we want to hide the |
|
154 // defn of nr_ice_candidate but we pass by reference. |
|
155 static NrIceCandidate* MakeNrIceCandidate(const nr_ice_candidate& candc) { |
|
156 ScopedDeletePtr<NrIceCandidate> out(new NrIceCandidate()); |
|
157 |
|
158 if (!ToNrIceCandidate(candc, out)) { |
|
159 return nullptr; |
|
160 } |
|
161 return out.forget(); |
|
162 } |
|
163 |
|
164 // NrIceMediaStream |
|
165 RefPtr<NrIceMediaStream> |
|
166 NrIceMediaStream::Create(NrIceCtx *ctx, |
|
167 const std::string& name, |
|
168 int components) { |
|
169 RefPtr<NrIceMediaStream> stream = |
|
170 new NrIceMediaStream(ctx, name, components); |
|
171 |
|
172 int r = nr_ice_add_media_stream(ctx->ctx(), |
|
173 const_cast<char *>(name.c_str()), |
|
174 components, &stream->stream_); |
|
175 if (r) { |
|
176 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '" |
|
177 << name << "'"); |
|
178 return nullptr; |
|
179 } |
|
180 |
|
181 return stream; |
|
182 } |
|
183 |
|
184 NrIceMediaStream::~NrIceMediaStream() { |
|
185 // We do not need to destroy anything. All major resources |
|
186 // are attached to the ice ctx. |
|
187 } |
|
188 |
|
189 nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>& |
|
190 attributes) { |
|
191 if (!stream_) |
|
192 return NS_ERROR_FAILURE; |
|
193 |
|
194 std::vector<char *> attributes_in; |
|
195 |
|
196 for (size_t i=0; i<attributes.size(); ++i) { |
|
197 attributes_in.push_back(const_cast<char *>(attributes[i].c_str())); |
|
198 } |
|
199 |
|
200 // Still need to call nr_ice_ctx_parse_stream_attributes. |
|
201 int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_->peer(), |
|
202 stream_, |
|
203 attributes_in.size() ? |
|
204 &attributes_in[0] : nullptr, |
|
205 attributes_in.size()); |
|
206 if (r) { |
|
207 MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream " |
|
208 << name_ << "'"); |
|
209 return NS_ERROR_FAILURE; |
|
210 } |
|
211 |
|
212 return NS_OK; |
|
213 } |
|
214 |
|
215 // Parse trickle ICE candidate |
|
216 nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) { |
|
217 int r; |
|
218 |
|
219 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->name() << ")/STREAM(" << |
|
220 name() << ") : parsing trickle candidate " << candidate); |
|
221 |
|
222 r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_->peer(), |
|
223 stream_, |
|
224 const_cast<char *>( |
|
225 candidate.c_str()) |
|
226 ); |
|
227 if (r) { |
|
228 if (r == R_ALREADY) { |
|
229 MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '" |
|
230 << name_ << "' because it is completed"); |
|
231 |
|
232 } else { |
|
233 MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '" |
|
234 << name_ << "'"); |
|
235 return NS_ERROR_FAILURE; |
|
236 } |
|
237 } |
|
238 |
|
239 return NS_OK; |
|
240 } |
|
241 |
|
242 // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled. |
|
243 nsresult NrIceMediaStream::GetActivePair(int component, |
|
244 NrIceCandidate **localp, |
|
245 NrIceCandidate **remotep) { |
|
246 int r; |
|
247 nr_ice_candidate *local_int; |
|
248 nr_ice_candidate *remote_int; |
|
249 |
|
250 r = nr_ice_media_stream_get_active(ctx_->peer(), |
|
251 stream_, |
|
252 component, |
|
253 &local_int, &remote_int); |
|
254 // If result is R_REJECTED then component is unpaired or disabled. |
|
255 if (r == R_REJECTED) |
|
256 return NS_ERROR_NOT_AVAILABLE; |
|
257 |
|
258 if (r) |
|
259 return NS_ERROR_FAILURE; |
|
260 |
|
261 ScopedDeletePtr<NrIceCandidate> local( |
|
262 MakeNrIceCandidate(*local_int)); |
|
263 if (!local) |
|
264 return NS_ERROR_FAILURE; |
|
265 |
|
266 ScopedDeletePtr<NrIceCandidate> remote( |
|
267 MakeNrIceCandidate(*remote_int)); |
|
268 if (!remote) |
|
269 return NS_ERROR_FAILURE; |
|
270 |
|
271 if (localp) |
|
272 *localp = local.forget(); |
|
273 if (remotep) |
|
274 *remotep = remote.forget(); |
|
275 |
|
276 return NS_OK; |
|
277 } |
|
278 |
|
279 |
|
280 nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>* |
|
281 out_pairs) const { |
|
282 MOZ_ASSERT(out_pairs); |
|
283 |
|
284 // Get the check_list on the peer stream (this is where the check_list |
|
285 // actually lives, not in stream_) |
|
286 nr_ice_media_stream* peer_stream; |
|
287 int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); |
|
288 if (r != 0) { |
|
289 return NS_ERROR_FAILURE; |
|
290 } |
|
291 |
|
292 nr_ice_cand_pair *p1; |
|
293 out_pairs->clear(); |
|
294 |
|
295 TAILQ_FOREACH(p1, &peer_stream->check_list, entry) { |
|
296 MOZ_ASSERT(p1); |
|
297 MOZ_ASSERT(p1->local); |
|
298 MOZ_ASSERT(p1->remote); |
|
299 NrIceCandidatePair pair; |
|
300 |
|
301 switch (p1->state) { |
|
302 case NR_ICE_PAIR_STATE_FROZEN: |
|
303 pair.state = NrIceCandidatePair::State::STATE_FROZEN; |
|
304 break; |
|
305 case NR_ICE_PAIR_STATE_WAITING: |
|
306 pair.state = NrIceCandidatePair::State::STATE_WAITING; |
|
307 break; |
|
308 case NR_ICE_PAIR_STATE_IN_PROGRESS: |
|
309 pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS; |
|
310 break; |
|
311 case NR_ICE_PAIR_STATE_FAILED: |
|
312 pair.state = NrIceCandidatePair::State::STATE_FAILED; |
|
313 break; |
|
314 case NR_ICE_PAIR_STATE_SUCCEEDED: |
|
315 pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED; |
|
316 break; |
|
317 case NR_ICE_PAIR_STATE_CANCELLED: |
|
318 pair.state = NrIceCandidatePair::State::STATE_CANCELLED; |
|
319 break; |
|
320 default: |
|
321 MOZ_ASSERT(0); |
|
322 } |
|
323 |
|
324 pair.priority = p1->priority; |
|
325 pair.nominated = p1->peer_nominated || p1->nominated; |
|
326 pair.selected = p1->remote->component && |
|
327 p1->remote->component->active == p1; |
|
328 pair.codeword = p1->codeword; |
|
329 |
|
330 if (!ToNrIceCandidate(*(p1->local), &pair.local) || |
|
331 !ToNrIceCandidate(*(p1->remote), &pair.remote)) { |
|
332 return NS_ERROR_FAILURE; |
|
333 } |
|
334 |
|
335 out_pairs->push_back(pair); |
|
336 } |
|
337 |
|
338 return NS_OK; |
|
339 } |
|
340 |
|
341 nsresult NrIceMediaStream::GetDefaultCandidate(int component, |
|
342 std::string *addrp, |
|
343 int *portp) { |
|
344 nr_ice_candidate *cand; |
|
345 int r; |
|
346 |
|
347 r = nr_ice_media_stream_get_default_candidate(stream_, |
|
348 component, &cand); |
|
349 if (r) { |
|
350 if (ctx_->generating_trickle()) { |
|
351 // Generate default trickle candidates. |
|
352 // draft-ivov-mmusic-trickle-ice-01.txt says to use port 9 |
|
353 // but "::" instead of "0.0.0.0". Since we don't do any |
|
354 // IPv6 we are ignoring that for now. |
|
355 *addrp = "0.0.0.0"; |
|
356 *portp = 9; |
|
357 } |
|
358 else { |
|
359 MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '" |
|
360 << name_ << "'"); |
|
361 |
|
362 return NS_ERROR_NOT_AVAILABLE; |
|
363 } |
|
364 } |
|
365 else { |
|
366 char addr[64]; // Enough for IPv6 with colons. |
|
367 r = nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr)); |
|
368 if (r) |
|
369 return NS_ERROR_FAILURE; |
|
370 |
|
371 int port; |
|
372 r=nr_transport_addr_get_port(&cand->addr,&port); |
|
373 if (r) |
|
374 return NS_ERROR_FAILURE; |
|
375 |
|
376 *addrp = addr; |
|
377 *portp = port; |
|
378 } |
|
379 |
|
380 return NS_OK; |
|
381 } |
|
382 |
|
383 std::vector<std::string> NrIceMediaStream::GetCandidates() const { |
|
384 char **attrs = 0; |
|
385 int attrct; |
|
386 int r; |
|
387 std::vector<std::string> ret; |
|
388 |
|
389 r = nr_ice_media_stream_get_attributes(stream_, |
|
390 &attrs, &attrct); |
|
391 if (r) { |
|
392 MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" |
|
393 << name_ << "'"); |
|
394 return ret; |
|
395 } |
|
396 |
|
397 for (int i=0; i<attrct; i++) { |
|
398 ret.push_back(attrs[i]); |
|
399 RFREE(attrs[i]); |
|
400 } |
|
401 |
|
402 RFREE(attrs); |
|
403 |
|
404 return ret; |
|
405 } |
|
406 |
|
407 static nsresult GetCandidatesFromStream( |
|
408 nr_ice_media_stream *stream, |
|
409 std::vector<NrIceCandidate> *candidates) { |
|
410 MOZ_ASSERT(candidates); |
|
411 nr_ice_component* comp=STAILQ_FIRST(&stream->components); |
|
412 while(comp){ |
|
413 if (comp->state != NR_ICE_COMPONENT_DISABLED) { |
|
414 nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates); |
|
415 while(cand){ |
|
416 NrIceCandidate new_cand; |
|
417 // This can fail if the candidate is server reflexive or relayed, and |
|
418 // has not yet received a response (ie; it doesn't know its address |
|
419 // yet). For the purposes of this code, this isn't a candidate we're |
|
420 // interested in, since it is not fully baked yet. |
|
421 if (ToNrIceCandidate(*cand, &new_cand)) { |
|
422 candidates->push_back(new_cand); |
|
423 } |
|
424 cand=TAILQ_NEXT(cand,entry_comp); |
|
425 } |
|
426 } |
|
427 comp=STAILQ_NEXT(comp,entry); |
|
428 } |
|
429 |
|
430 return NS_OK; |
|
431 } |
|
432 |
|
433 nsresult NrIceMediaStream::GetLocalCandidates( |
|
434 std::vector<NrIceCandidate>* candidates) const { |
|
435 return GetCandidatesFromStream(stream_, candidates); |
|
436 } |
|
437 |
|
438 nsresult NrIceMediaStream::GetRemoteCandidates( |
|
439 std::vector<NrIceCandidate>* candidates) const { |
|
440 nr_ice_media_stream* peer_stream; |
|
441 int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); |
|
442 if (r != 0) { |
|
443 return NS_ERROR_FAILURE; |
|
444 } |
|
445 |
|
446 return GetCandidatesFromStream(peer_stream, candidates); |
|
447 } |
|
448 |
|
449 |
|
450 nsresult NrIceMediaStream::DisableComponent(int component_id) { |
|
451 if (!stream_) |
|
452 return NS_ERROR_FAILURE; |
|
453 |
|
454 int r = nr_ice_media_stream_disable_component(stream_, |
|
455 component_id); |
|
456 if (r) { |
|
457 MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << |
|
458 component_id); |
|
459 return NS_ERROR_FAILURE; |
|
460 } |
|
461 |
|
462 return NS_OK; |
|
463 } |
|
464 |
|
465 nsresult NrIceMediaStream::SendPacket(int component_id, |
|
466 const unsigned char *data, |
|
467 size_t len) { |
|
468 if (!stream_) |
|
469 return NS_ERROR_FAILURE; |
|
470 |
|
471 int r = nr_ice_media_stream_send(ctx_->peer(), stream_, |
|
472 component_id, |
|
473 const_cast<unsigned char *>(data), len); |
|
474 if (r) { |
|
475 MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'"); |
|
476 if (r == R_WOULDBLOCK) { |
|
477 return NS_BASE_STREAM_WOULD_BLOCK; |
|
478 } |
|
479 |
|
480 return NS_BASE_STREAM_OSERROR; |
|
481 } |
|
482 |
|
483 return NS_OK; |
|
484 } |
|
485 |
|
486 |
|
487 void NrIceMediaStream::Ready() { |
|
488 // This function is called whenever a stream becomes ready, but it |
|
489 // gets fired multiple times when a stream gets nominated repeatedly. |
|
490 if (state_ != ICE_OPEN) { |
|
491 MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'"); |
|
492 state_ = ICE_OPEN; |
|
493 SignalReady(this); |
|
494 } |
|
495 else { |
|
496 MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'"); |
|
497 } |
|
498 } |
|
499 |
|
500 void NrIceMediaStream::Close() { |
|
501 MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); |
|
502 state_ = ICE_CLOSED; |
|
503 stream_ = nullptr; |
|
504 } |
|
505 } // close namespace |