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 */
8
9 /*
10 Based partially on original code from nICEr and nrappkit.
11
12 nICEr copyright:
13
14 Copyright (c) 2007, Adobe Systems, Incorporated
15 All rights reserved.
16
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions are
19 met:
20
21 * Redistributions of source code must retain the above copyright
22 notice, this list of conditions and the following disclaimer.
23
24 * Redistributions in binary form must reproduce the above copyright
25 notice, this list of conditions and the following disclaimer in the
26 documentation and/or other materials provided with the distribution.
27
28 * Neither the name of Adobe Systems, Network Resonance nor the names of its
29 contributors may be used to endorse or promote products derived from
30 this software without specific prior written permission.
31
32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
35 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
39 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
42 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
44
45 nrappkit copyright:
46
47 Copyright (C) 2001-2003, Network Resonance, Inc.
48 Copyright (C) 2006, Network Resonance, Inc.
49 All Rights Reserved
50
51 Redistribution and use in source and binary forms, with or without
52 modification, are permitted provided that the following conditions
53 are met:
54
55 1. Redistributions of source code must retain the above copyright
56 notice, this list of conditions and the following disclaimer.
57 2. Redistributions in binary form must reproduce the above copyright
58 notice, this list of conditions and the following disclaimer in the
59 documentation and/or other materials provided with the distribution.
60 3. Neither the name of Network Resonance, Inc. nor the name of any
61 contributors to this software may be used to endorse or promote
62 products derived from this software without specific prior written
63 permission.
64
65 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
66 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
69 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
70 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
71 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
72 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
73 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
74 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
75 POSSIBILITY OF SUCH DAMAGE.
76
77
78 ekr@rtfm.com Thu Dec 20 20:14:49 2001
79 */
80
81 // Original author: bcampen@mozilla.com [:bwc]
82
83 extern "C" {
84 #include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
85 #include "nr_api.h"
86 #include "async_wait.h"
87 #include "async_timer.h"
88 #include "nr_socket.h"
89 #include "nr_socket_local.h"
90 #include "stun.h"
91 #include "stun_hint.h"
92 #include "transport_addr.h"
93 }
94
95 #include "mozilla/RefPtr.h"
96 #include "test_nr_socket.h"
97 #include "runnable_utils.h"
98
99 namespace mozilla {
100
test_nat_socket_create(void * obj,nr_transport_addr * addr,nr_socket ** sockp)101 static int test_nat_socket_create(void* obj, nr_transport_addr* addr,
102 nr_socket** sockp) {
103 RefPtr<NrSocketBase> sock = new TestNrSocket(static_cast<TestNat*>(obj));
104
105 int r, _status;
106
107 r = sock->create(addr);
108 if (r) ABORT(r);
109
110 r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp);
111 if (r) ABORT(r);
112
113 _status = 0;
114
115 {
116 // We will release this reference in destroy(), not exactly the normal
117 // ownership model, but it is what it is.
118 NrSocketBase* dummy = sock.forget().take();
119 (void)dummy;
120 }
121
122 abort:
123 return _status;
124 }
125
test_nat_socket_factory_destroy(void ** obj)126 static int test_nat_socket_factory_destroy(void** obj) {
127 TestNat* nat = static_cast<TestNat*>(*obj);
128 *obj = nullptr;
129 nat->Release();
130 return 0;
131 }
132
133 static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = {
134 test_nat_socket_create, test_nat_socket_factory_destroy};
135
136 /* static */
ToNatBehavior(const std::string & type)137 TestNat::NatBehavior TestNat::ToNatBehavior(const std::string& type) {
138 if (!type.compare("ENDPOINT_INDEPENDENT")) {
139 return TestNat::ENDPOINT_INDEPENDENT;
140 }
141 if (!type.compare("ADDRESS_DEPENDENT")) {
142 return TestNat::ADDRESS_DEPENDENT;
143 }
144 if (!type.compare("PORT_DEPENDENT")) {
145 return TestNat::PORT_DEPENDENT;
146 }
147
148 MOZ_ASSERT(false, "Invalid NAT behavior");
149 return TestNat::ENDPOINT_INDEPENDENT;
150 }
151
has_port_mappings() const152 bool TestNat::has_port_mappings() const {
153 for (TestNrSocket* sock : sockets_) {
154 if (sock->has_port_mappings()) {
155 return true;
156 }
157 }
158 return false;
159 }
160
is_my_external_tuple(const nr_transport_addr & addr) const161 bool TestNat::is_my_external_tuple(const nr_transport_addr& addr) const {
162 for (TestNrSocket* sock : sockets_) {
163 if (sock->is_my_external_tuple(addr)) {
164 return true;
165 }
166 }
167
168 return false;
169 }
170
is_an_internal_tuple(const nr_transport_addr & addr) const171 bool TestNat::is_an_internal_tuple(const nr_transport_addr& addr) const {
172 for (TestNrSocket* sock : sockets_) {
173 nr_transport_addr addr_behind_nat;
174 if (sock->getaddr(&addr_behind_nat)) {
175 MOZ_CRASH("TestNrSocket::getaddr failed!");
176 }
177
178 if (!nr_transport_addr_cmp(&addr, &addr_behind_nat,
179 NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
180 return true;
181 }
182 }
183 return false;
184 }
185
create_socket_factory(nr_socket_factory ** factorypp)186 int TestNat::create_socket_factory(nr_socket_factory** factorypp) {
187 int r = nr_socket_factory_create_int(this, &test_nat_socket_factory_vtbl,
188 factorypp);
189 if (!r) {
190 AddRef();
191 }
192 return r;
193 }
194
TestNrSocket(TestNat * nat)195 TestNrSocket::TestNrSocket(TestNat* nat)
196 : nat_(nat), tls_(false), timer_handle_(nullptr) {
197 nat_->insert_socket(this);
198 }
199
~TestNrSocket()200 TestNrSocket::~TestNrSocket() { nat_->erase_socket(this); }
201
create_external_socket(const nr_transport_addr & dest_addr) const202 RefPtr<NrSocketBase> TestNrSocket::create_external_socket(
203 const nr_transport_addr& dest_addr) const {
204 MOZ_ASSERT(nat_->enabled_);
205 MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
206
207 int r;
208 nr_transport_addr nat_external_addr;
209
210 // Open the socket on an arbitrary port, on the same address.
211 if ((r = nr_transport_addr_copy(&nat_external_addr,
212 &internal_socket_->my_addr()))) {
213 r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
214 __FUNCTION__, r);
215 return nullptr;
216 }
217
218 if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) {
219 r_log(LOG_GENERIC, LOG_CRIT,
220 "%s: Failure in nr_transport_addr_set_port: %d", __FUNCTION__, r);
221 return nullptr;
222 }
223
224 RefPtr<NrSocketBase> external_socket;
225 r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket, nullptr);
226
227 if (r) {
228 r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in NrSocket::create: %d",
229 __FUNCTION__, r);
230 return nullptr;
231 }
232
233 return external_socket;
234 }
235
create(nr_transport_addr * addr)236 int TestNrSocket::create(nr_transport_addr* addr) {
237 tls_ = addr->tls;
238
239 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p create %s", this,
240 addr->as_string);
241 return NrSocketBase::CreateSocket(addr, &internal_socket_, nullptr);
242 }
243
getaddr(nr_transport_addr * addrp)244 int TestNrSocket::getaddr(nr_transport_addr* addrp) {
245 return internal_socket_->getaddr(addrp);
246 }
247
close()248 void TestNrSocket::close() {
249 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s closing", this,
250 internal_socket_->my_addr().as_string);
251 if (timer_handle_) {
252 NR_async_timer_cancel(timer_handle_);
253 timer_handle_ = nullptr;
254 }
255 internal_socket_->close();
256 for (RefPtr<PortMapping>& port_mapping : port_mappings_) {
257 port_mapping->external_socket_->close();
258 }
259 }
260
listen(int backlog)261 int TestNrSocket::listen(int backlog) {
262 MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
263 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s listening", this,
264 internal_socket_->my_addr().as_string);
265
266 return internal_socket_->listen(backlog);
267 }
268
accept(nr_transport_addr * addrp,nr_socket ** sockp)269 int TestNrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) {
270 MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
271 int r = internal_socket_->accept(addrp, sockp);
272 if (r) {
273 return r;
274 }
275
276 if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) {
277 nr_socket_destroy(sockp);
278 return R_IO_ERROR;
279 }
280
281 return 0;
282 }
283
process_delayed_cb(NR_SOCKET s,int how,void * cb_arg)284 void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void* cb_arg) {
285 DeferredPacket* op = static_cast<DeferredPacket*>(cb_arg);
286 op->socket_->timer_handle_ = nullptr;
287 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s sending delayed STUN response",
288 op->internal_socket_->my_addr().as_string);
289 op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(),
290 op->flags_, &op->to_);
291
292 delete op;
293 }
294
sendto(const void * msg,size_t len,int flags,const nr_transport_addr * to)295 int TestNrSocket::sendto(const void* msg, size_t len, int flags,
296 const nr_transport_addr* to) {
297 MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
298 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s %s", this, __FUNCTION__,
299 to->as_string);
300
301 if (nat_->nat_delegate_ &&
302 nat_->nat_delegate_->on_sendto(nat_, msg, len, flags, to)) {
303 return nat_->error_code_for_drop_;
304 }
305
306 UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
307 if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
308 return nat_->error_code_for_drop_;
309 }
310
311 if (nr_is_stun_request_message(buf, len) &&
312 maybe_send_fake_response(buf, len, to)) {
313 return 0;
314 }
315
316 /* TODO: improve the functionality of this in bug 1253657 */
317 if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
318 if (nat_->delay_stun_resp_ms_ && nr_is_stun_response_message(buf, len)) {
319 NR_ASYNC_TIMER_SET(
320 nat_->delay_stun_resp_ms_, process_delayed_cb,
321 new DeferredPacket(this, msg, len, flags, to, internal_socket_),
322 &timer_handle_);
323 return 0;
324 }
325 return internal_socket_->sendto(msg, len, flags, to);
326 }
327
328 destroy_stale_port_mappings();
329
330 if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
331 return nat_->error_code_for_drop_;
332 }
333
334 // Choose our port mapping based on our most selective criteria
335 PortMapping* port_mapping = get_port_mapping(
336 *to, std::max(nat_->filtering_type_, nat_->mapping_type_));
337
338 if (!port_mapping) {
339 // See if we have already made the external socket we need to use.
340 PortMapping* similar_port_mapping =
341 get_port_mapping(*to, nat_->mapping_type_);
342 RefPtr<NrSocketBase> external_socket;
343
344 if (similar_port_mapping) {
345 external_socket = similar_port_mapping->external_socket_;
346 } else {
347 external_socket = create_external_socket(*to);
348 if (!external_socket) {
349 MOZ_ASSERT(false);
350 return R_INTERNAL;
351 }
352 }
353
354 port_mapping = create_port_mapping(*to, external_socket);
355 port_mappings_.push_back(port_mapping);
356
357 if (poll_flags() & PR_POLL_READ) {
358 // Make sure the new port mapping is ready to receive traffic if the
359 // TestNrSocket is already waiting.
360 port_mapping->async_wait(NR_ASYNC_WAIT_READ, socket_readable_callback,
361 this, (char*)__FUNCTION__, __LINE__);
362 }
363 }
364
365 // We probably don't want to propagate the flags, since this is a simulated
366 // external IP address.
367 return port_mapping->sendto(msg, len, *to);
368 }
369
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)370 int TestNrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
371 nr_transport_addr* from) {
372 MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
373
374 if (!read_buffer_.empty()) {
375 UdpPacket& packet = read_buffer_.front();
376 *len = std::min(maxlen, packet.buffer_->len());
377 memcpy(buf, packet.buffer_->data(), *len);
378 nr_transport_addr_copy(from, &packet.remote_address_);
379 read_buffer_.pop_front();
380 return 0;
381 }
382
383 int r;
384 bool ingress_allowed = false;
385
386 if (readable_socket_) {
387 // If any of the external sockets got data, see if it will be passed through
388 r = readable_socket_->recvfrom(buf, maxlen, len, 0, from);
389 readable_socket_ = nullptr;
390 if (!r) {
391 PortMapping* port_mapping_used;
392 ingress_allowed = allow_ingress(*from, &port_mapping_used);
393 if (ingress_allowed) {
394 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s",
395 internal_socket_->my_addr().as_string, from->as_string,
396 port_mapping_used->external_socket_->my_addr().as_string);
397 if (nat_->refresh_on_ingress_) {
398 port_mapping_used->last_used_ = PR_IntervalNow();
399 }
400 }
401 }
402 } else {
403 // If no external socket has data, see if there's any data that was sent
404 // directly to the TestNrSocket, and eat it if it isn't supposed to get
405 // through.
406 r = internal_socket_->recvfrom(buf, maxlen, len, flags, from);
407 if (!r) {
408 // We do not use allow_ingress() here because that only handles traffic
409 // landing on an external port.
410 ingress_allowed = (!nat_->enabled_ || nat_->is_an_internal_tuple(*from));
411 if (!ingress_allowed) {
412 r_log(LOG_GENERIC, LOG_INFO,
413 "TestNrSocket %s denying ingress from %s: "
414 "Not behind the same NAT",
415 internal_socket_->my_addr().as_string, from->as_string);
416 } else {
417 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s",
418 internal_socket_->my_addr().as_string, from->as_string);
419 }
420 }
421 }
422
423 // Kinda bad that we are forced to give the app a readable callback and then
424 // say "Oh, never mind...", but the alternative is to totally decouple the
425 // callbacks from STS and the callbacks the app sets. On the bright side, this
426 // speeds up unit tests where we are verifying that ingress is forbidden,
427 // since they'll get a readable callback and then an error, instead of having
428 // to wait for a timeout.
429 if (!ingress_allowed) {
430 *len = 0;
431 r = R_WOULDBLOCK;
432 }
433
434 return r;
435 }
436
allow_ingress(const nr_transport_addr & from,PortMapping ** port_mapping_used) const437 bool TestNrSocket::allow_ingress(const nr_transport_addr& from,
438 PortMapping** port_mapping_used) const {
439 // This is only called for traffic arriving at a port mapping
440 MOZ_ASSERT(nat_->enabled_);
441 MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
442
443 *port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
444 if (!(*port_mapping_used)) {
445 r_log(LOG_GENERIC, LOG_INFO,
446 "TestNrSocket %s denying ingress from %s: "
447 "Filtered",
448 internal_socket_->my_addr().as_string, from.as_string);
449 return false;
450 }
451
452 if (is_port_mapping_stale(**port_mapping_used)) {
453 r_log(LOG_GENERIC, LOG_INFO,
454 "TestNrSocket %s denying ingress from %s: "
455 "Stale port mapping",
456 internal_socket_->my_addr().as_string, from.as_string);
457 return false;
458 }
459
460 if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
461 r_log(LOG_GENERIC, LOG_INFO,
462 "TestNrSocket %s denying ingress from %s: "
463 "Hairpinning disallowed",
464 internal_socket_->my_addr().as_string, from.as_string);
465 return false;
466 }
467
468 return true;
469 }
470
connect(const nr_transport_addr * addr)471 int TestNrSocket::connect(const nr_transport_addr* addr) {
472 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s connecting to %s", this,
473 internal_socket_->my_addr().as_string, addr->as_string);
474
475 if (connect_invoked_ || !port_mappings_.empty()) {
476 MOZ_CRASH("TestNrSocket::connect() called more than once!");
477 return R_INTERNAL;
478 }
479
480 if (maybe_get_redirect_targets(addr).isSome()) {
481 // If we are simulating STUN redirects for |addr|, we need to pretend that
482 // the TCP connection worked, since |addr| probably does not actually point
483 // at something that exists.
484 connect_fake_stun_address_.reset(new nr_transport_addr);
485 nr_transport_addr_copy(connect_fake_stun_address_.get(), addr);
486
487 // We dispatch this, otherwise nICEr can trip over its shoelaces
488 GetCurrentSerialEventTarget()->Dispatch(
489 NS_NewRunnableFunction("Async writeable callback for TestNrSocket",
490 [this, self = RefPtr<TestNrSocket>(this)] {
491 if (poll_flags() & PR_POLL_WRITE) {
492 fire_callback(NR_ASYNC_WAIT_WRITE);
493 }
494 }));
495
496 return R_WOULDBLOCK;
497 }
498
499 if (!nat_->enabled_ ||
500 addr->protocol == IPPROTO_UDP // Horrible hack to allow default address
501 // discovery to work. Only works because
502 // we don't normally connect on UDP.
503 || nat_->is_an_internal_tuple(*addr)) {
504 // This will set connect_invoked_
505 return internal_socket_->connect(addr);
506 }
507
508 RefPtr<NrSocketBase> external_socket(create_external_socket(*addr));
509 if (!external_socket) {
510 return R_INTERNAL;
511 }
512
513 PortMapping* port_mapping = create_port_mapping(*addr, external_socket);
514 port_mappings_.push_back(port_mapping);
515 int r = port_mapping->external_socket_->connect(addr);
516 if (r && r != R_WOULDBLOCK) {
517 return r;
518 }
519
520 port_mapping->last_used_ = PR_IntervalNow();
521
522 if (poll_flags() & PR_POLL_READ) {
523 port_mapping->async_wait(NR_ASYNC_WAIT_READ,
524 port_mapping_tcp_passthrough_callback, this,
525 (char*)__FUNCTION__, __LINE__);
526 }
527
528 return r;
529 }
530
write(const void * msg,size_t len,size_t * written)531 int TestNrSocket::write(const void* msg, size_t len, size_t* written) {
532 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s writing", this,
533 internal_socket_->my_addr().as_string);
534
535 UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
536
537 if (nat_->nat_delegate_ &&
538 nat_->nat_delegate_->on_write(nat_, msg, len, written)) {
539 return R_INTERNAL;
540 }
541
542 if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
543 // Should cause this socket to be abandoned
544 r_log(LOG_GENERIC, LOG_DEBUG,
545 "TestNrSocket %s dropping outgoing TCP "
546 "because it is configured to drop STUN",
547 my_addr().as_string);
548 return R_INTERNAL;
549 }
550
551 if (nr_is_stun_request_message(buf, len) && connect_fake_stun_address_ &&
552 maybe_send_fake_response(buf, len, connect_fake_stun_address_.get())) {
553 return 0;
554 }
555
556 if (nat_->block_tcp_ && !tls_) {
557 // Should cause this socket to be abandoned
558 r_log(LOG_GENERIC, LOG_DEBUG,
559 "TestNrSocket %s dropping outgoing TCP "
560 "because it is configured to drop TCP",
561 my_addr().as_string);
562 return R_INTERNAL;
563 }
564
565 if (port_mappings_.empty()) {
566 // The no-nat case, just pass call through.
567 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
568 my_addr().as_string);
569
570 return internal_socket_->write(msg, len, written);
571 }
572 destroy_stale_port_mappings();
573 if (port_mappings_.empty()) {
574 r_log(LOG_GENERIC, LOG_DEBUG,
575 "TestNrSocket %s dropping outgoing TCP "
576 "because the port mapping was stale",
577 my_addr().as_string);
578 return R_INTERNAL;
579 }
580 // This is TCP only
581 MOZ_ASSERT(port_mappings_.size() == 1);
582 r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s writing",
583 port_mappings_.front()->external_socket_->my_addr().as_string,
584 port_mappings_.front()->remote_address_.as_string);
585 port_mappings_.front()->last_used_ = PR_IntervalNow();
586 return port_mappings_.front()->external_socket_->write(msg, len, written);
587 }
588
read(void * buf,size_t maxlen,size_t * len)589 int TestNrSocket::read(void* buf, size_t maxlen, size_t* len) {
590 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s reading", this,
591 internal_socket_->my_addr().as_string);
592
593 if (!read_buffer_.empty()) {
594 r_log(LOG_GENERIC, LOG_DEBUG,
595 "TestNrSocket %p %s has stuff in read_buffer_", this,
596 internal_socket_->my_addr().as_string);
597 UdpPacket packet(std::move(read_buffer_.front()));
598 read_buffer_.pop_front();
599 *len = std::min(maxlen, packet.buffer_->len());
600 memcpy(buf, packet.buffer_->data(), *len);
601 if (*len != packet.buffer_->len()) {
602 // Put remaining bytes in new packet, at the front.
603 read_buffer_.emplace_front(packet.buffer_->data() + *len,
604 packet.buffer_->len() - *len,
605 packet.remote_address_);
606 }
607 return 0;
608 }
609
610 if (connect_fake_stun_address_) {
611 return R_WOULDBLOCK;
612 }
613
614 int r;
615
616 if (port_mappings_.empty()) {
617 r = internal_socket_->read(buf, maxlen, len);
618 } else {
619 MOZ_ASSERT(port_mappings_.size() == 1);
620 r = port_mappings_.front()->external_socket_->read(buf, maxlen, len);
621 if (!r && nat_->refresh_on_ingress_) {
622 port_mappings_.front()->last_used_ = PR_IntervalNow();
623 }
624 }
625
626 if (r) {
627 return r;
628 }
629
630 if (nat_->nat_delegate_ &&
631 nat_->nat_delegate_->on_read(nat_, buf, maxlen, len)) {
632 return R_INTERNAL;
633 }
634
635 if (nat_->block_tcp_ && !tls_) {
636 // Should cause this socket to be abandoned
637 return R_INTERNAL;
638 }
639
640 UCHAR* cbuf = static_cast<UCHAR*>(const_cast<void*>(buf));
641 if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) {
642 // Should cause this socket to be abandoned
643 return R_INTERNAL;
644 }
645
646 return r;
647 }
648
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)649 int TestNrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg,
650 char* function, int line) {
651 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
652 internal_socket_->my_addr().as_string,
653 how == NR_ASYNC_WAIT_READ ? "read" : "write");
654
655 int r;
656
657 if (how == NR_ASYNC_WAIT_READ) {
658 NrSocketBase::async_wait(how, cb, cb_arg, function, line);
659 if (!read_buffer_.empty()) {
660 fire_readable_callback();
661 return 0;
662 }
663
664 // Make sure we're waiting on the socket for the internal address
665 r = internal_socket_->async_wait(how, socket_readable_callback, this,
666 function, line);
667 } else {
668 if (connect_fake_stun_address_) {
669 // Fake TCP connection case; register the callback on this socket, not
670 // a real one.
671 return NrSocketBase::async_wait(how, cb, cb_arg, function, line);
672 }
673
674 // For write, just use the readiness of the internal socket, since we queue
675 // everything for the port mappings.
676 r = internal_socket_->async_wait(how, cb, cb_arg, function, line);
677 }
678
679 if (r) {
680 r_log(LOG_GENERIC, LOG_ERR,
681 "TestNrSocket %s failed to async_wait for "
682 "internal socket: %d\n",
683 internal_socket_->my_addr().as_string, r);
684 return r;
685 }
686
687 if (is_tcp_connection_behind_nat()) {
688 // Bypass all port-mapping related logic
689 return 0;
690 }
691
692 if (internal_socket_->my_addr().protocol == IPPROTO_TCP) {
693 // For a TCP connection through a simulated NAT, these signals are
694 // just passed through.
695 MOZ_ASSERT(port_mappings_.size() == 1);
696
697 return port_mappings_.front()->async_wait(
698 how, port_mapping_tcp_passthrough_callback, this, function, line);
699 }
700 if (how == NR_ASYNC_WAIT_READ) {
701 // For UDP port mappings, we decouple the writeable callbacks
702 for (PortMapping* port_mapping : port_mappings_) {
703 // Be ready to receive traffic on our port mappings
704 r = port_mapping->async_wait(how, socket_readable_callback, this,
705 function, line);
706 if (r) {
707 r_log(LOG_GENERIC, LOG_ERR,
708 "TestNrSocket %s failed to async_wait for "
709 "port mapping: %d\n",
710 internal_socket_->my_addr().as_string, r);
711 return r;
712 }
713 }
714 }
715
716 return 0;
717 }
718
cancel_port_mapping_async_wait(int how)719 void TestNrSocket::cancel_port_mapping_async_wait(int how) {
720 for (PortMapping* port_mapping : port_mappings_) {
721 port_mapping->cancel(how);
722 }
723 }
724
cancel(int how)725 int TestNrSocket::cancel(int how) {
726 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
727 internal_socket_->my_addr().as_string,
728 how == NR_ASYNC_WAIT_READ ? "read" : "write");
729
730 if (connect_fake_stun_address_) {
731 return NrSocketBase::cancel(how);
732 }
733
734 // Writable callbacks are decoupled except for the TCP case
735 if (how == NR_ASYNC_WAIT_READ ||
736 internal_socket_->my_addr().protocol == IPPROTO_TCP) {
737 cancel_port_mapping_async_wait(how);
738 }
739
740 return internal_socket_->cancel(how);
741 }
742
has_port_mappings() const743 bool TestNrSocket::has_port_mappings() const { return !port_mappings_.empty(); }
744
is_my_external_tuple(const nr_transport_addr & addr) const745 bool TestNrSocket::is_my_external_tuple(const nr_transport_addr& addr) const {
746 for (PortMapping* port_mapping : port_mappings_) {
747 nr_transport_addr port_mapping_addr;
748 if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) {
749 MOZ_CRASH("NrSocket::getaddr failed!");
750 }
751
752 if (!nr_transport_addr_cmp(&addr, &port_mapping_addr,
753 NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
754 return true;
755 }
756 }
757 return false;
758 }
759
is_port_mapping_stale(const PortMapping & port_mapping) const760 bool TestNrSocket::is_port_mapping_stale(
761 const PortMapping& port_mapping) const {
762 PRIntervalTime now = PR_IntervalNow();
763 PRIntervalTime elapsed_ticks = now - port_mapping.last_used_;
764 uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks);
765 return idle_duration > nat_->mapping_timeout_;
766 }
767
destroy_stale_port_mappings()768 void TestNrSocket::destroy_stale_port_mappings() {
769 for (auto i = port_mappings_.begin(); i != port_mappings_.end();) {
770 auto temp = i;
771 ++i;
772 if (is_port_mapping_stale(**temp)) {
773 r_log(LOG_GENERIC, LOG_INFO,
774 "TestNrSocket %s destroying port mapping %s -> %s",
775 internal_socket_->my_addr().as_string,
776 (*temp)->external_socket_->my_addr().as_string,
777 (*temp)->remote_address_.as_string);
778
779 port_mappings_.erase(temp);
780 }
781 }
782 }
783
socket_readable_callback(void * real_sock_v,int how,void * test_sock_v)784 void TestNrSocket::socket_readable_callback(void* real_sock_v, int how,
785 void* test_sock_v) {
786 TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
787 NrSocketBase* real_socket = static_cast<NrSocketBase*>(real_sock_v);
788
789 test_socket->on_socket_readable(real_socket);
790 }
791
on_socket_readable(NrSocketBase * real_socket)792 void TestNrSocket::on_socket_readable(NrSocketBase* real_socket) {
793 if (!readable_socket_ && (real_socket != internal_socket_)) {
794 readable_socket_ = real_socket;
795 }
796
797 fire_readable_callback();
798 }
799
fire_readable_callback()800 void TestNrSocket::fire_readable_callback() {
801 MOZ_ASSERT(poll_flags() & PR_POLL_READ);
802 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s ready for read", this,
803 internal_socket_->my_addr().as_string);
804 fire_callback(NR_ASYNC_WAIT_READ);
805 }
806
port_mapping_writeable_callback(void * ext_sock_v,int how,void * test_sock_v)807 void TestNrSocket::port_mapping_writeable_callback(void* ext_sock_v, int how,
808 void* test_sock_v) {
809 TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
810 NrSocketBase* external_socket = static_cast<NrSocketBase*>(ext_sock_v);
811
812 test_socket->write_to_port_mapping(external_socket);
813 }
814
write_to_port_mapping(NrSocketBase * external_socket)815 void TestNrSocket::write_to_port_mapping(NrSocketBase* external_socket) {
816 MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
817
818 int r = 0;
819 for (PortMapping* port_mapping : port_mappings_) {
820 if (port_mapping->external_socket_ == external_socket) {
821 // If the send succeeds, or if there was nothing to send, we keep going
822 r = port_mapping->send_from_queue();
823 if (r) {
824 break;
825 }
826 }
827 }
828
829 if (r == R_WOULDBLOCK) {
830 // Re-register for writeable callbacks, since we still have stuff to send
831 NR_ASYNC_WAIT(external_socket, NR_ASYNC_WAIT_WRITE,
832 &TestNrSocket::port_mapping_writeable_callback, this);
833 }
834 }
835
port_mapping_tcp_passthrough_callback(void * ext_sock_v,int how,void * test_sock_v)836 void TestNrSocket::port_mapping_tcp_passthrough_callback(void* ext_sock_v,
837 int how,
838 void* test_sock_v) {
839 TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
840 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s firing %s callback",
841 test_socket->internal_socket_->my_addr().as_string,
842 how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
843
844 test_socket->internal_socket_->fire_callback(how);
845 }
846
is_tcp_connection_behind_nat() const847 bool TestNrSocket::is_tcp_connection_behind_nat() const {
848 return internal_socket_->my_addr().protocol == IPPROTO_TCP &&
849 port_mappings_.empty();
850 }
851
get_port_mapping(const nr_transport_addr & remote_address,TestNat::NatBehavior filter) const852 TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
853 const nr_transport_addr& remote_address,
854 TestNat::NatBehavior filter) const {
855 int compare_flags;
856 switch (filter) {
857 case TestNat::ENDPOINT_INDEPENDENT:
858 compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL;
859 break;
860 case TestNat::ADDRESS_DEPENDENT:
861 compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR;
862 break;
863 case TestNat::PORT_DEPENDENT:
864 compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL;
865 break;
866 }
867
868 for (PortMapping* port_mapping : port_mappings_) {
869 if (!nr_transport_addr_cmp(&remote_address, &port_mapping->remote_address_,
870 compare_flags)) {
871 return port_mapping;
872 }
873 }
874 return nullptr;
875 }
876
create_port_mapping(const nr_transport_addr & remote_address,const RefPtr<NrSocketBase> & external_socket) const877 TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
878 const nr_transport_addr& remote_address,
879 const RefPtr<NrSocketBase>& external_socket) const {
880 r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
881 internal_socket_->my_addr().as_string,
882 external_socket->my_addr().as_string, remote_address.as_string);
883
884 return new PortMapping(remote_address, external_socket);
885 }
886
PortMapping(const nr_transport_addr & remote_address,const RefPtr<NrSocketBase> & external_socket)887 TestNrSocket::PortMapping::PortMapping(
888 const nr_transport_addr& remote_address,
889 const RefPtr<NrSocketBase>& external_socket)
890 : external_socket_(external_socket) {
891 nr_transport_addr_copy(&remote_address_, &remote_address);
892 }
893
send_from_queue()894 int TestNrSocket::PortMapping::send_from_queue() {
895 MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
896 int r = 0;
897
898 while (!send_queue_.empty()) {
899 UdpPacket& packet = send_queue_.front();
900 r_log(LOG_GENERIC, LOG_DEBUG,
901 "PortMapping %s -> %s sending from queue to %s",
902 external_socket_->my_addr().as_string, remote_address_.as_string,
903 packet.remote_address_.as_string);
904
905 r = external_socket_->sendto(packet.buffer_->data(), packet.buffer_->len(),
906 0, &packet.remote_address_);
907
908 if (r) {
909 if (r != R_WOULDBLOCK) {
910 r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying",
911 __FUNCTION__, r);
912 send_queue_.clear();
913 } else {
914 r_log(LOG_GENERIC, LOG_DEBUG, "Would block, will retry later");
915 }
916 break;
917 }
918
919 send_queue_.pop_front();
920 }
921
922 return r;
923 }
924
sendto(const void * msg,size_t len,const nr_transport_addr & to)925 int TestNrSocket::PortMapping::sendto(const void* msg, size_t len,
926 const nr_transport_addr& to) {
927 MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
928 r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s sending to %s",
929 external_socket_->my_addr().as_string, remote_address_.as_string,
930 to.as_string);
931
932 last_used_ = PR_IntervalNow();
933 int r = external_socket_->sendto(msg, len, 0, &to);
934
935 if (r == R_WOULDBLOCK) {
936 r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
937 send_queue_.emplace_back(msg, len, to);
938 return 0;
939 }
940 if (r) {
941 r_log(LOG_GENERIC, LOG_ERR, "Error: %d", r);
942 }
943
944 return r;
945 }
946
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)947 int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void* cb_arg,
948 char* function, int line) {
949 r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s waiting for %s",
950 external_socket_->my_addr().as_string, remote_address_.as_string,
951 how == NR_ASYNC_WAIT_READ ? "read" : "write");
952
953 return external_socket_->async_wait(how, cb, cb_arg, function, line);
954 }
955
cancel(int how)956 int TestNrSocket::PortMapping::cancel(int how) {
957 r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s stop waiting for %s",
958 external_socket_->my_addr().as_string, remote_address_.as_string,
959 how == NR_ASYNC_WAIT_READ ? "read" : "write");
960
961 return external_socket_->cancel(how);
962 }
963
964 class nr_stun_message_deleter {
965 public:
966 nr_stun_message_deleter() = default;
operator ()(nr_stun_message * msg) const967 void operator()(nr_stun_message* msg) const { nr_stun_message_destroy(&msg); }
968 };
969
maybe_send_fake_response(const void * msg,size_t len,const nr_transport_addr * to)970 bool TestNrSocket::maybe_send_fake_response(const void* msg, size_t len,
971 const nr_transport_addr* to) {
972 Maybe<nsTArray<nsCString>> redirect_targets = maybe_get_redirect_targets(to);
973 if (!redirect_targets.isSome()) {
974 return false;
975 }
976
977 std::unique_ptr<nr_stun_message, nr_stun_message_deleter> request;
978 {
979 nr_stun_message* temp = nullptr;
980 if (NS_WARN_IF(nr_stun_message_create2(&temp, (unsigned char*)msg, len))) {
981 return false;
982 }
983 request.reset(temp);
984 }
985
986 if (NS_WARN_IF(nr_stun_decode_message(request.get(), nullptr, nullptr))) {
987 return false;
988 }
989
990 std::unique_ptr<nr_stun_message, nr_stun_message_deleter> response;
991 {
992 nr_stun_message* temp = nullptr;
993 if (nr_stun_message_create(&temp)) {
994 MOZ_CRASH("nr_stun_message_create failed!");
995 }
996 response.reset(temp);
997 }
998
999 nr_stun_form_error_response(request.get(), response.get(), 300,
1000 (char*)"Try alternate");
1001
1002 int port = 0;
1003 if (nr_transport_addr_get_port(to, &port)) {
1004 MOZ_CRASH();
1005 }
1006
1007 for (const nsCString& address : *redirect_targets) {
1008 r_log(LOG_GENERIC, LOG_DEBUG,
1009 "TestNrSocket attempting to add alternate server %s", address.Data());
1010 nr_transport_addr addr;
1011 if (NS_WARN_IF(nr_str_port_to_transport_addr(address.Data(), port,
1012 IPPROTO_UDP, &addr))) {
1013 continue;
1014 }
1015 if (nr_stun_message_add_alternate_server_attribute(response.get(), &addr)) {
1016 MOZ_CRASH("nr_stun_message_add_alternate_server_attribute failed!");
1017 }
1018 }
1019
1020 if (nr_stun_encode_message(response.get())) {
1021 MOZ_CRASH("nr_stun_encode_message failed!");
1022 }
1023
1024 nr_transport_addr response_from;
1025 if (nr_transport_addr_is_wildcard(to)) {
1026 // |to| points to an FQDN, and nICEr is delegating DNS lookup to us; we
1027 // aren't _actually_ going to do that though, so we select a bogus address
1028 // for the response to come from. TEST-NET is a fairly reasonable thing to
1029 // use for this.
1030 int port = 0;
1031 if (nr_transport_addr_get_port(to, &port)) {
1032 MOZ_CRASH();
1033 }
1034 switch (to->ip_version) {
1035 case NR_IPV4:
1036 if (nr_str_port_to_transport_addr("198.51.100.1", port, to->protocol,
1037 &response_from)) {
1038 MOZ_CRASH();
1039 }
1040 break;
1041 case NR_IPV6:
1042 if (nr_str_port_to_transport_addr("::ffff:198.51.100.1", port,
1043 to->protocol, &response_from)) {
1044 MOZ_CRASH();
1045 }
1046 break;
1047 default:
1048 MOZ_CRASH();
1049 }
1050 } else {
1051 nr_transport_addr_copy(&response_from, to);
1052 }
1053
1054 read_buffer_.emplace_back(response->buffer, response->length, response_from);
1055
1056 // We dispatch this, otherwise nICEr can trip over its shoelaces
1057 r_log(LOG_GENERIC, LOG_DEBUG,
1058 "TestNrSocket %p scheduling callback for redirect response", this);
1059 GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
1060 "Async readable callback for TestNrSocket",
1061 [this, self = RefPtr<TestNrSocket>(this)] {
1062 if (poll_flags() & PR_POLL_READ) {
1063 fire_readable_callback();
1064 } else {
1065 r_log(LOG_GENERIC, LOG_DEBUG,
1066 "TestNrSocket %p deferring callback for redirect response",
1067 this);
1068 }
1069 }));
1070
1071 return true;
1072 }
1073
maybe_get_redirect_targets(const nr_transport_addr * to) const1074 Maybe<nsTArray<nsCString>> TestNrSocket::maybe_get_redirect_targets(
1075 const nr_transport_addr* to) const {
1076 Maybe<nsTArray<nsCString>> result;
1077
1078 // 256 is overkill, but it hardly matters
1079 char addrstring[256];
1080 if (nr_transport_addr_get_addrstring(to, addrstring, 256)) {
1081 MOZ_CRASH("nr_transport_addr_get_addrstring failed!");
1082 }
1083
1084 r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket checking redirect rules for %s",
1085 addrstring);
1086 auto it = nat_->stun_redirect_map_.find(nsCString(addrstring));
1087 if (it != nat_->stun_redirect_map_.end()) {
1088 result = Some(it->second);
1089 }
1090
1091 return result;
1092 }
1093
1094 } // namespace mozilla
1095