Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 // Original author: ekr@rtfm.com
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
11 /*
12 Copyright (c) 2007, Adobe Systems, Incorporated
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
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.
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.
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 */
44 #include <string>
45 #include <vector>
47 #include "logging.h"
48 #include "nsError.h"
49 #include "mozilla/Scoped.h"
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 }
68 // Local includes
69 #include "nricectx.h"
70 #include "nricemediastream.h"
72 namespace mozilla {
74 MOZ_MTLOG_MODULE("mtransport")
76 static bool ToNrIceAddr(nr_transport_addr &addr,
77 NrIceAddr *out) {
78 int r;
79 char addrstring[INET6_ADDRSTRLEN + 1];
81 r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
82 if (r)
83 return false;
84 out->host = addrstring;
86 int port;
87 r = nr_transport_addr_get_port(&addr, &port);
88 if (r)
89 return false;
91 out->port = port;
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 }
105 return true;
106 }
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);
115 if (!ToNrIceAddr(cand->addr, &out->cand_addr))
116 return false;
118 if (cand->isock) {
119 nr_transport_addr addr;
120 r = nr_socket_getaddr(cand->isock->sock, &addr);
121 if (r)
122 return false;
124 if (!ToNrIceAddr(addr, &out->local_addr))
125 return false;
126 }
128 NrIceCandidate::Type type;
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 }
147 out->type = type;
148 out->codeword = candc.codeword;
149 return true;
150 }
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());
158 if (!ToNrIceCandidate(candc, out)) {
159 return nullptr;
160 }
161 return out.forget();
162 }
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);
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 }
181 return stream;
182 }
184 NrIceMediaStream::~NrIceMediaStream() {
185 // We do not need to destroy anything. All major resources
186 // are attached to the ice ctx.
187 }
189 nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
190 attributes) {
191 if (!stream_)
192 return NS_ERROR_FAILURE;
194 std::vector<char *> attributes_in;
196 for (size_t i=0; i<attributes.size(); ++i) {
197 attributes_in.push_back(const_cast<char *>(attributes[i].c_str()));
198 }
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 }
212 return NS_OK;
213 }
215 // Parse trickle ICE candidate
216 nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
217 int r;
219 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->name() << ")/STREAM(" <<
220 name() << ") : parsing trickle candidate " << candidate);
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");
232 } else {
233 MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
234 << name_ << "'");
235 return NS_ERROR_FAILURE;
236 }
237 }
239 return NS_OK;
240 }
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;
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;
258 if (r)
259 return NS_ERROR_FAILURE;
261 ScopedDeletePtr<NrIceCandidate> local(
262 MakeNrIceCandidate(*local_int));
263 if (!local)
264 return NS_ERROR_FAILURE;
266 ScopedDeletePtr<NrIceCandidate> remote(
267 MakeNrIceCandidate(*remote_int));
268 if (!remote)
269 return NS_ERROR_FAILURE;
271 if (localp)
272 *localp = local.forget();
273 if (remotep)
274 *remotep = remote.forget();
276 return NS_OK;
277 }
280 nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
281 out_pairs) const {
282 MOZ_ASSERT(out_pairs);
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 }
292 nr_ice_cand_pair *p1;
293 out_pairs->clear();
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;
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 }
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;
330 if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
331 !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
332 return NS_ERROR_FAILURE;
333 }
335 out_pairs->push_back(pair);
336 }
338 return NS_OK;
339 }
341 nsresult NrIceMediaStream::GetDefaultCandidate(int component,
342 std::string *addrp,
343 int *portp) {
344 nr_ice_candidate *cand;
345 int r;
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_ << "'");
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;
371 int port;
372 r=nr_transport_addr_get_port(&cand->addr,&port);
373 if (r)
374 return NS_ERROR_FAILURE;
376 *addrp = addr;
377 *portp = port;
378 }
380 return NS_OK;
381 }
383 std::vector<std::string> NrIceMediaStream::GetCandidates() const {
384 char **attrs = 0;
385 int attrct;
386 int r;
387 std::vector<std::string> ret;
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 }
397 for (int i=0; i<attrct; i++) {
398 ret.push_back(attrs[i]);
399 RFREE(attrs[i]);
400 }
402 RFREE(attrs);
404 return ret;
405 }
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 }
430 return NS_OK;
431 }
433 nsresult NrIceMediaStream::GetLocalCandidates(
434 std::vector<NrIceCandidate>* candidates) const {
435 return GetCandidatesFromStream(stream_, candidates);
436 }
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 }
446 return GetCandidatesFromStream(peer_stream, candidates);
447 }
450 nsresult NrIceMediaStream::DisableComponent(int component_id) {
451 if (!stream_)
452 return NS_ERROR_FAILURE;
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 }
462 return NS_OK;
463 }
465 nsresult NrIceMediaStream::SendPacket(int component_id,
466 const unsigned char *data,
467 size_t len) {
468 if (!stream_)
469 return NS_ERROR_FAILURE;
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 }
480 return NS_BASE_STREAM_OSERROR;
481 }
483 return NS_OK;
484 }
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 }
500 void NrIceMediaStream::Close() {
501 MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
502 state_ = ICE_CLOSED;
503 stream_ = nullptr;
504 }
505 } // close namespace