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.empty() || !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 
set_proxy_config(std::shared_ptr<NrSocketProxyConfig> aProxyConfig)195 void TestNat::set_proxy_config(
196     std::shared_ptr<NrSocketProxyConfig> aProxyConfig) {
197   proxy_config_ = std::move(aProxyConfig);
198 }
199 
TestNrSocket(TestNat * nat)200 TestNrSocket::TestNrSocket(TestNat* nat)
201     : nat_(nat), tls_(false), timer_handle_(nullptr) {
202   nat_->insert_socket(this);
203 }
204 
~TestNrSocket()205 TestNrSocket::~TestNrSocket() { nat_->erase_socket(this); }
206 
create_external_socket(const nr_transport_addr & dest_addr) const207 RefPtr<NrSocketBase> TestNrSocket::create_external_socket(
208     const nr_transport_addr& dest_addr) const {
209   MOZ_ASSERT(nat_->enabled_);
210   MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
211 
212   int r;
213   nr_transport_addr nat_external_addr;
214 
215   // Open the socket on an arbitrary port, on the same address.
216   if ((r = nr_transport_addr_copy(&nat_external_addr,
217                                   &internal_socket_->my_addr()))) {
218     r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
219           __FUNCTION__, r);
220     return nullptr;
221   }
222 
223   if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) {
224     r_log(LOG_GENERIC, LOG_CRIT,
225           "%s: Failure in nr_transport_addr_set_port: %d", __FUNCTION__, r);
226     return nullptr;
227   }
228 
229   RefPtr<NrSocketBase> external_socket;
230   r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket,
231                                  nat_->proxy_config_);
232 
233   if (r) {
234     r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in NrSocket::create: %d",
235           __FUNCTION__, r);
236     return nullptr;
237   }
238 
239   return external_socket;
240 }
241 
create(nr_transport_addr * addr)242 int TestNrSocket::create(nr_transport_addr* addr) {
243   tls_ = addr->tls;
244 
245   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p create %s", this,
246         addr->as_string);
247   return NrSocketBase::CreateSocket(addr, &internal_socket_, nullptr);
248 }
249 
getaddr(nr_transport_addr * addrp)250 int TestNrSocket::getaddr(nr_transport_addr* addrp) {
251   return internal_socket_->getaddr(addrp);
252 }
253 
close()254 void TestNrSocket::close() {
255   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s closing", this,
256         internal_socket_->my_addr().as_string);
257   if (timer_handle_) {
258     NR_async_timer_cancel(timer_handle_);
259     timer_handle_ = nullptr;
260   }
261   internal_socket_->close();
262   for (RefPtr<PortMapping>& port_mapping : port_mappings_) {
263     port_mapping->external_socket_->close();
264   }
265 }
266 
listen(int backlog)267 int TestNrSocket::listen(int backlog) {
268   MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
269   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s listening", this,
270         internal_socket_->my_addr().as_string);
271 
272   return internal_socket_->listen(backlog);
273 }
274 
accept(nr_transport_addr * addrp,nr_socket ** sockp)275 int TestNrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) {
276   MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
277   int r = internal_socket_->accept(addrp, sockp);
278   if (r) {
279     return r;
280   }
281 
282   if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) {
283     nr_socket_destroy(sockp);
284     return R_IO_ERROR;
285   }
286 
287   return 0;
288 }
289 
process_delayed_cb(NR_SOCKET s,int how,void * cb_arg)290 void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void* cb_arg) {
291   DeferredPacket* op = static_cast<DeferredPacket*>(cb_arg);
292   op->socket_->timer_handle_ = nullptr;
293   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s sending delayed STUN response",
294         op->internal_socket_->my_addr().as_string);
295   op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(),
296                                op->flags_, &op->to_);
297 
298   delete op;
299 }
300 
sendto(const void * msg,size_t len,int flags,const nr_transport_addr * to)301 int TestNrSocket::sendto(const void* msg, size_t len, int flags,
302                          const nr_transport_addr* to) {
303   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
304   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s %s", this, __FUNCTION__,
305         to->as_string);
306 
307   if (nat_->nat_delegate_ &&
308       nat_->nat_delegate_->on_sendto(nat_, msg, len, flags, to)) {
309     return nat_->error_code_for_drop_;
310   }
311 
312   UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
313   if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
314     return nat_->error_code_for_drop_;
315   }
316 
317   if (nr_is_stun_request_message(buf, len) &&
318       maybe_send_fake_response(buf, len, to)) {
319     return 0;
320   }
321 
322   /* TODO: improve the functionality of this in bug 1253657 */
323   if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
324     if (nat_->delay_stun_resp_ms_ && nr_is_stun_response_message(buf, len)) {
325       NR_ASYNC_TIMER_SET(
326           nat_->delay_stun_resp_ms_, process_delayed_cb,
327           new DeferredPacket(this, msg, len, flags, to, internal_socket_),
328           &timer_handle_);
329       return 0;
330     }
331     return internal_socket_->sendto(msg, len, flags, to);
332   }
333 
334   destroy_stale_port_mappings();
335 
336   if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
337     return nat_->error_code_for_drop_;
338   }
339 
340   // Choose our port mapping based on our most selective criteria
341   PortMapping* port_mapping = get_port_mapping(
342       *to, std::max(nat_->filtering_type_, nat_->mapping_type_));
343 
344   if (!port_mapping) {
345     // See if we have already made the external socket we need to use.
346     PortMapping* similar_port_mapping =
347         get_port_mapping(*to, nat_->mapping_type_);
348     RefPtr<NrSocketBase> external_socket;
349 
350     if (similar_port_mapping) {
351       external_socket = similar_port_mapping->external_socket_;
352     } else {
353       external_socket = create_external_socket(*to);
354       if (!external_socket) {
355         MOZ_ASSERT(false);
356         return R_INTERNAL;
357       }
358     }
359 
360     port_mapping = create_port_mapping(*to, external_socket);
361     port_mappings_.push_back(port_mapping);
362 
363     if (poll_flags() & PR_POLL_READ) {
364       // Make sure the new port mapping is ready to receive traffic if the
365       // TestNrSocket is already waiting.
366       port_mapping->async_wait(NR_ASYNC_WAIT_READ, socket_readable_callback,
367                                this, (char*)__FUNCTION__, __LINE__);
368     }
369   }
370 
371   // We probably don't want to propagate the flags, since this is a simulated
372   // external IP address.
373   return port_mapping->sendto(msg, len, *to);
374 }
375 
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)376 int TestNrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
377                            nr_transport_addr* from) {
378   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
379 
380   if (!read_buffer_.empty()) {
381     UdpPacket& packet = read_buffer_.front();
382     *len = std::min(maxlen, packet.buffer_->len());
383     memcpy(buf, packet.buffer_->data(), *len);
384     nr_transport_addr_copy(from, &packet.remote_address_);
385     read_buffer_.pop_front();
386     return 0;
387   }
388 
389   int r;
390   bool ingress_allowed = false;
391 
392   if (readable_socket_) {
393     // If any of the external sockets got data, see if it will be passed through
394     r = readable_socket_->recvfrom(buf, maxlen, len, 0, from);
395     const nr_transport_addr to = readable_socket_->my_addr();
396     readable_socket_ = nullptr;
397     if (!r) {
398       PortMapping* port_mapping_used;
399       ingress_allowed = allow_ingress(to, *from, &port_mapping_used);
400       if (ingress_allowed) {
401         r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s",
402               internal_socket_->my_addr().as_string, from->as_string,
403               port_mapping_used->external_socket_->my_addr().as_string);
404         if (nat_->refresh_on_ingress_) {
405           port_mapping_used->last_used_ = PR_IntervalNow();
406         }
407       }
408     }
409   } else {
410     // If no external socket has data, see if there's any data that was sent
411     // directly to the TestNrSocket, and eat it if it isn't supposed to get
412     // through.
413     r = internal_socket_->recvfrom(buf, maxlen, len, flags, from);
414     if (!r) {
415       // We do not use allow_ingress() here because that only handles traffic
416       // landing on an external port.
417       ingress_allowed = (!nat_->enabled_ || nat_->is_an_internal_tuple(*from));
418       if (!ingress_allowed) {
419         r_log(LOG_GENERIC, LOG_INFO,
420               "TestNrSocket %s denying ingress from %s: "
421               "Not behind the same NAT",
422               internal_socket_->my_addr().as_string, from->as_string);
423       } else {
424         r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s",
425               internal_socket_->my_addr().as_string, from->as_string);
426       }
427     }
428   }
429 
430   // Kinda bad that we are forced to give the app a readable callback and then
431   // say "Oh, never mind...", but the alternative is to totally decouple the
432   // callbacks from STS and the callbacks the app sets. On the bright side, this
433   // speeds up unit tests where we are verifying that ingress is forbidden,
434   // since they'll get a readable callback and then an error, instead of having
435   // to wait for a timeout.
436   if (!ingress_allowed) {
437     *len = 0;
438     r = R_WOULDBLOCK;
439   }
440 
441   return r;
442 }
443 
allow_ingress(const nr_transport_addr & to,const nr_transport_addr & from,PortMapping ** port_mapping_used) const444 bool TestNrSocket::allow_ingress(const nr_transport_addr& to,
445                                  const nr_transport_addr& from,
446                                  PortMapping** port_mapping_used) const {
447   // This is only called for traffic arriving at a port mapping
448   MOZ_ASSERT(nat_->enabled_);
449   MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
450 
451   // Find the port mapping (if any) that this packet landed on
452   for (PortMapping* port_mapping : port_mappings_) {
453     if (!nr_transport_addr_cmp(&to, &port_mapping->external_socket_->my_addr(),
454                                NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
455       *port_mapping_used = port_mapping;
456     }
457   }
458 
459   if (NS_WARN_IF(!(*port_mapping_used))) {
460     MOZ_ASSERT(false);
461     r_log(LOG_GENERIC, LOG_INFO,
462           "TestNrSocket %s denying ingress from %s: "
463           "No port mapping for this local port! What?",
464           internal_socket_->my_addr().as_string, from.as_string);
465     return false;
466   }
467 
468   if (!port_mapping_matches(**port_mapping_used, from, nat_->filtering_type_)) {
469     r_log(LOG_GENERIC, LOG_INFO,
470           "TestNrSocket %s denying ingress from %s: "
471           "Filtered (no port mapping for source)",
472           internal_socket_->my_addr().as_string, from.as_string);
473     return false;
474   }
475 
476   if (is_port_mapping_stale(**port_mapping_used)) {
477     r_log(LOG_GENERIC, LOG_INFO,
478           "TestNrSocket %s denying ingress from %s: "
479           "Stale port mapping",
480           internal_socket_->my_addr().as_string, from.as_string);
481     return false;
482   }
483 
484   if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
485     r_log(LOG_GENERIC, LOG_INFO,
486           "TestNrSocket %s denying ingress from %s: "
487           "Hairpinning disallowed",
488           internal_socket_->my_addr().as_string, from.as_string);
489     return false;
490   }
491 
492   return true;
493 }
494 
connect(const nr_transport_addr * addr)495 int TestNrSocket::connect(const nr_transport_addr* addr) {
496   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s connecting to %s", this,
497         internal_socket_->my_addr().as_string, addr->as_string);
498 
499   if (connect_invoked_ || !port_mappings_.empty()) {
500     MOZ_CRASH("TestNrSocket::connect() called more than once!");
501     return R_INTERNAL;
502   }
503 
504   if (maybe_get_redirect_targets(addr).isSome()) {
505     // If we are simulating STUN redirects for |addr|, we need to pretend that
506     // the TCP connection worked, since |addr| probably does not actually point
507     // at something that exists.
508     connect_fake_stun_address_.reset(new nr_transport_addr);
509     nr_transport_addr_copy(connect_fake_stun_address_.get(), addr);
510 
511     // We dispatch this, otherwise nICEr can trip over its shoelaces
512     GetCurrentSerialEventTarget()->Dispatch(
513         NS_NewRunnableFunction("Async writeable callback for TestNrSocket",
514                                [this, self = RefPtr<TestNrSocket>(this)] {
515                                  if (poll_flags() & PR_POLL_WRITE) {
516                                    fire_callback(NR_ASYNC_WAIT_WRITE);
517                                  }
518                                }));
519 
520     return R_WOULDBLOCK;
521   }
522 
523   if (!nat_->enabled_ ||
524       addr->protocol == IPPROTO_UDP  // Horrible hack to allow default address
525                                      // discovery to work. Only works because
526                                      // we don't normally connect on UDP.
527       || nat_->is_an_internal_tuple(*addr)) {
528     // This will set connect_invoked_
529     return internal_socket_->connect(addr);
530   }
531 
532   RefPtr<NrSocketBase> external_socket(create_external_socket(*addr));
533   if (!external_socket) {
534     return R_INTERNAL;
535   }
536 
537   PortMapping* port_mapping = create_port_mapping(*addr, external_socket);
538   port_mappings_.push_back(port_mapping);
539   int r = port_mapping->external_socket_->connect(addr);
540   if (r && r != R_WOULDBLOCK) {
541     return r;
542   }
543 
544   port_mapping->last_used_ = PR_IntervalNow();
545 
546   if (poll_flags() & PR_POLL_READ) {
547     port_mapping->async_wait(NR_ASYNC_WAIT_READ,
548                              port_mapping_tcp_passthrough_callback, this,
549                              (char*)__FUNCTION__, __LINE__);
550   }
551 
552   return r;
553 }
554 
write(const void * msg,size_t len,size_t * written)555 int TestNrSocket::write(const void* msg, size_t len, size_t* written) {
556   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s writing", this,
557         internal_socket_->my_addr().as_string);
558 
559   UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
560 
561   if (nat_->nat_delegate_ &&
562       nat_->nat_delegate_->on_write(nat_, msg, len, written)) {
563     return R_INTERNAL;
564   }
565 
566   if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
567     // Should cause this socket to be abandoned
568     r_log(LOG_GENERIC, LOG_DEBUG,
569           "TestNrSocket %s dropping outgoing TCP "
570           "because it is configured to drop STUN",
571           my_addr().as_string);
572     return R_INTERNAL;
573   }
574 
575   if (nr_is_stun_request_message(buf, len) && connect_fake_stun_address_ &&
576       maybe_send_fake_response(buf, len, connect_fake_stun_address_.get())) {
577     return 0;
578   }
579 
580   if (nat_->block_tcp_ && !tls_) {
581     // Should cause this socket to be abandoned
582     r_log(LOG_GENERIC, LOG_DEBUG,
583           "TestNrSocket %s dropping outgoing TCP "
584           "because it is configured to drop TCP",
585           my_addr().as_string);
586     return R_INTERNAL;
587   }
588 
589   if (nat_->block_tls_ && tls_) {
590     // Should cause this socket to be abandoned
591     r_log(LOG_GENERIC, LOG_DEBUG,
592           "TestNrSocket %s dropping outgoing TLS "
593           "because it is configured to drop TLS",
594           my_addr().as_string);
595     return R_INTERNAL;
596   }
597 
598   if (port_mappings_.empty()) {
599     // The no-nat case, just pass call through.
600     r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
601           my_addr().as_string);
602 
603     return internal_socket_->write(msg, len, written);
604   }
605   destroy_stale_port_mappings();
606   if (port_mappings_.empty()) {
607     r_log(LOG_GENERIC, LOG_DEBUG,
608           "TestNrSocket %s dropping outgoing TCP "
609           "because the port mapping was stale",
610           my_addr().as_string);
611     return R_INTERNAL;
612   }
613   // This is TCP only
614   MOZ_ASSERT(port_mappings_.size() == 1);
615   r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s writing",
616         port_mappings_.front()->external_socket_->my_addr().as_string,
617         port_mappings_.front()->remote_address_.as_string);
618   port_mappings_.front()->last_used_ = PR_IntervalNow();
619   return port_mappings_.front()->external_socket_->write(msg, len, written);
620 }
621 
read(void * buf,size_t maxlen,size_t * len)622 int TestNrSocket::read(void* buf, size_t maxlen, size_t* len) {
623   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s reading", this,
624         internal_socket_->my_addr().as_string);
625 
626   if (!read_buffer_.empty()) {
627     r_log(LOG_GENERIC, LOG_DEBUG,
628           "TestNrSocket %p %s has stuff in read_buffer_", this,
629           internal_socket_->my_addr().as_string);
630     UdpPacket packet(std::move(read_buffer_.front()));
631     read_buffer_.pop_front();
632     *len = std::min(maxlen, packet.buffer_->len());
633     memcpy(buf, packet.buffer_->data(), *len);
634     if (*len != packet.buffer_->len()) {
635       // Put remaining bytes in new packet, at the front.
636       read_buffer_.emplace_front(packet.buffer_->data() + *len,
637                                  packet.buffer_->len() - *len,
638                                  packet.remote_address_);
639     }
640     return 0;
641   }
642 
643   if (connect_fake_stun_address_) {
644     return R_WOULDBLOCK;
645   }
646 
647   int r;
648 
649   if (port_mappings_.empty()) {
650     r = internal_socket_->read(buf, maxlen, len);
651   } else {
652     MOZ_ASSERT(port_mappings_.size() == 1);
653     r = port_mappings_.front()->external_socket_->read(buf, maxlen, len);
654     if (!r && nat_->refresh_on_ingress_) {
655       port_mappings_.front()->last_used_ = PR_IntervalNow();
656     }
657   }
658 
659   if (r) {
660     return r;
661   }
662 
663   if (nat_->nat_delegate_ &&
664       nat_->nat_delegate_->on_read(nat_, buf, maxlen, len)) {
665     return R_INTERNAL;
666   }
667 
668   if (nat_->block_tcp_ && !tls_) {
669     // Should cause this socket to be abandoned
670     return R_INTERNAL;
671   }
672 
673   if (nat_->block_tls_ && tls_) {
674     // Should cause this socket to be abandoned
675     return R_INTERNAL;
676   }
677 
678   UCHAR* cbuf = static_cast<UCHAR*>(const_cast<void*>(buf));
679   if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) {
680     // Should cause this socket to be abandoned
681     return R_INTERNAL;
682   }
683 
684   return r;
685 }
686 
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)687 int TestNrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg,
688                              char* function, int line) {
689   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
690         internal_socket_->my_addr().as_string,
691         how == NR_ASYNC_WAIT_READ ? "read" : "write");
692 
693   int r;
694 
695   if (how == NR_ASYNC_WAIT_READ) {
696     NrSocketBase::async_wait(how, cb, cb_arg, function, line);
697     if (!read_buffer_.empty()) {
698       fire_readable_callback();
699       return 0;
700     }
701 
702     // Make sure we're waiting on the socket for the internal address
703     r = internal_socket_->async_wait(how, socket_readable_callback, this,
704                                      function, line);
705   } else {
706     if (connect_fake_stun_address_) {
707       // Fake TCP connection case; register the callback on this socket, not
708       // a real one.
709       return NrSocketBase::async_wait(how, cb, cb_arg, function, line);
710     }
711 
712     // For write, just use the readiness of the internal socket, since we queue
713     // everything for the port mappings.
714     r = internal_socket_->async_wait(how, cb, cb_arg, function, line);
715   }
716 
717   if (r) {
718     r_log(LOG_GENERIC, LOG_ERR,
719           "TestNrSocket %s failed to async_wait for "
720           "internal socket: %d\n",
721           internal_socket_->my_addr().as_string, r);
722     return r;
723   }
724 
725   if (is_tcp_connection_behind_nat()) {
726     // Bypass all port-mapping related logic
727     return 0;
728   }
729 
730   if (internal_socket_->my_addr().protocol == IPPROTO_TCP) {
731     // For a TCP connection through a simulated NAT, these signals are
732     // just passed through.
733     MOZ_ASSERT(port_mappings_.size() == 1);
734 
735     return port_mappings_.front()->async_wait(
736         how, port_mapping_tcp_passthrough_callback, this, function, line);
737   }
738   if (how == NR_ASYNC_WAIT_READ) {
739     // For UDP port mappings, we decouple the writeable callbacks
740     for (PortMapping* port_mapping : port_mappings_) {
741       // Be ready to receive traffic on our port mappings
742       r = port_mapping->async_wait(how, socket_readable_callback, this,
743                                    function, line);
744       if (r) {
745         r_log(LOG_GENERIC, LOG_ERR,
746               "TestNrSocket %s failed to async_wait for "
747               "port mapping: %d\n",
748               internal_socket_->my_addr().as_string, r);
749         return r;
750       }
751     }
752   }
753 
754   return 0;
755 }
756 
cancel_port_mapping_async_wait(int how)757 void TestNrSocket::cancel_port_mapping_async_wait(int how) {
758   for (PortMapping* port_mapping : port_mappings_) {
759     port_mapping->cancel(how);
760   }
761 }
762 
cancel(int how)763 int TestNrSocket::cancel(int how) {
764   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
765         internal_socket_->my_addr().as_string,
766         how == NR_ASYNC_WAIT_READ ? "read" : "write");
767 
768   if (connect_fake_stun_address_) {
769     return NrSocketBase::cancel(how);
770   }
771 
772   // Writable callbacks are decoupled except for the TCP case
773   if (how == NR_ASYNC_WAIT_READ ||
774       internal_socket_->my_addr().protocol == IPPROTO_TCP) {
775     cancel_port_mapping_async_wait(how);
776   }
777 
778   return internal_socket_->cancel(how);
779 }
780 
has_port_mappings() const781 bool TestNrSocket::has_port_mappings() const { return !port_mappings_.empty(); }
782 
is_my_external_tuple(const nr_transport_addr & addr) const783 bool TestNrSocket::is_my_external_tuple(const nr_transport_addr& addr) const {
784   for (PortMapping* port_mapping : port_mappings_) {
785     nr_transport_addr port_mapping_addr;
786     if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) {
787       MOZ_CRASH("NrSocket::getaddr failed!");
788     }
789 
790     if (!nr_transport_addr_cmp(&addr, &port_mapping_addr,
791                                NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
792       return true;
793     }
794   }
795   return false;
796 }
797 
is_port_mapping_stale(const PortMapping & port_mapping) const798 bool TestNrSocket::is_port_mapping_stale(
799     const PortMapping& port_mapping) const {
800   PRIntervalTime now = PR_IntervalNow();
801   PRIntervalTime elapsed_ticks = now - port_mapping.last_used_;
802   uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks);
803   return idle_duration > nat_->mapping_timeout_;
804 }
805 
destroy_stale_port_mappings()806 void TestNrSocket::destroy_stale_port_mappings() {
807   for (auto i = port_mappings_.begin(); i != port_mappings_.end();) {
808     auto temp = i;
809     ++i;
810     if (is_port_mapping_stale(**temp)) {
811       r_log(LOG_GENERIC, LOG_INFO,
812             "TestNrSocket %s destroying port mapping %s -> %s",
813             internal_socket_->my_addr().as_string,
814             (*temp)->external_socket_->my_addr().as_string,
815             (*temp)->remote_address_.as_string);
816 
817       port_mappings_.erase(temp);
818     }
819   }
820 }
821 
socket_readable_callback(void * real_sock_v,int how,void * test_sock_v)822 void TestNrSocket::socket_readable_callback(void* real_sock_v, int how,
823                                             void* test_sock_v) {
824   TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
825   NrSocketBase* real_socket = static_cast<NrSocketBase*>(real_sock_v);
826 
827   test_socket->on_socket_readable(real_socket);
828 }
829 
on_socket_readable(NrSocketBase * real_socket)830 void TestNrSocket::on_socket_readable(NrSocketBase* real_socket) {
831   if (!readable_socket_ && (real_socket != internal_socket_)) {
832     readable_socket_ = real_socket;
833   }
834 
835   fire_readable_callback();
836 }
837 
fire_readable_callback()838 void TestNrSocket::fire_readable_callback() {
839   MOZ_ASSERT(poll_flags() & PR_POLL_READ);
840   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s ready for read", this,
841         internal_socket_->my_addr().as_string);
842   fire_callback(NR_ASYNC_WAIT_READ);
843 }
844 
port_mapping_writeable_callback(void * ext_sock_v,int how,void * test_sock_v)845 void TestNrSocket::port_mapping_writeable_callback(void* ext_sock_v, int how,
846                                                    void* test_sock_v) {
847   TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
848   NrSocketBase* external_socket = static_cast<NrSocketBase*>(ext_sock_v);
849 
850   test_socket->write_to_port_mapping(external_socket);
851 }
852 
write_to_port_mapping(NrSocketBase * external_socket)853 void TestNrSocket::write_to_port_mapping(NrSocketBase* external_socket) {
854   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
855 
856   int r = 0;
857   for (PortMapping* port_mapping : port_mappings_) {
858     if (port_mapping->external_socket_ == external_socket) {
859       // If the send succeeds, or if there was nothing to send, we keep going
860       r = port_mapping->send_from_queue();
861       if (r) {
862         break;
863       }
864     }
865   }
866 
867   if (r == R_WOULDBLOCK) {
868     // Re-register for writeable callbacks, since we still have stuff to send
869     NR_ASYNC_WAIT(external_socket, NR_ASYNC_WAIT_WRITE,
870                   &TestNrSocket::port_mapping_writeable_callback, this);
871   }
872 }
873 
port_mapping_tcp_passthrough_callback(void * ext_sock_v,int how,void * test_sock_v)874 void TestNrSocket::port_mapping_tcp_passthrough_callback(void* ext_sock_v,
875                                                          int how,
876                                                          void* test_sock_v) {
877   TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v);
878   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s firing %s callback",
879         test_socket->internal_socket_->my_addr().as_string,
880         how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
881 
882   test_socket->internal_socket_->fire_callback(how);
883 }
884 
is_tcp_connection_behind_nat() const885 bool TestNrSocket::is_tcp_connection_behind_nat() const {
886   return internal_socket_->my_addr().protocol == IPPROTO_TCP &&
887          port_mappings_.empty();
888 }
889 
get_port_mapping(const nr_transport_addr & remote_address,TestNat::NatBehavior filter) const890 TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
891     const nr_transport_addr& remote_address,
892     TestNat::NatBehavior filter) const {
893   for (PortMapping* port_mapping : port_mappings_) {
894     if (port_mapping_matches(*port_mapping, remote_address, filter)) {
895       return port_mapping;
896     }
897   }
898   return nullptr;
899 }
900 
901 /* static */
port_mapping_matches(const PortMapping & port_mapping,const nr_transport_addr & remote_addr,TestNat::NatBehavior filter)902 bool TestNrSocket::port_mapping_matches(const PortMapping& port_mapping,
903                                         const nr_transport_addr& remote_addr,
904                                         TestNat::NatBehavior filter) {
905   int compare_flags;
906   switch (filter) {
907     case TestNat::ENDPOINT_INDEPENDENT:
908       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL;
909       break;
910     case TestNat::ADDRESS_DEPENDENT:
911       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR;
912       break;
913     case TestNat::PORT_DEPENDENT:
914       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL;
915       break;
916   }
917 
918   return !nr_transport_addr_cmp(&remote_addr, &port_mapping.remote_address_,
919                                 compare_flags);
920 }
921 
create_port_mapping(const nr_transport_addr & remote_address,const RefPtr<NrSocketBase> & external_socket) const922 TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
923     const nr_transport_addr& remote_address,
924     const RefPtr<NrSocketBase>& external_socket) const {
925   r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
926         internal_socket_->my_addr().as_string,
927         external_socket->my_addr().as_string, remote_address.as_string);
928 
929   return new PortMapping(remote_address, external_socket);
930 }
931 
PortMapping(const nr_transport_addr & remote_address,const RefPtr<NrSocketBase> & external_socket)932 TestNrSocket::PortMapping::PortMapping(
933     const nr_transport_addr& remote_address,
934     const RefPtr<NrSocketBase>& external_socket)
935     : external_socket_(external_socket) {
936   nr_transport_addr_copy(&remote_address_, &remote_address);
937 }
938 
send_from_queue()939 int TestNrSocket::PortMapping::send_from_queue() {
940   MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
941   int r = 0;
942 
943   while (!send_queue_.empty()) {
944     UdpPacket& packet = send_queue_.front();
945     r_log(LOG_GENERIC, LOG_DEBUG,
946           "PortMapping %s -> %s sending from queue to %s",
947           external_socket_->my_addr().as_string, remote_address_.as_string,
948           packet.remote_address_.as_string);
949 
950     r = external_socket_->sendto(packet.buffer_->data(), packet.buffer_->len(),
951                                  0, &packet.remote_address_);
952 
953     if (r) {
954       if (r != R_WOULDBLOCK) {
955         r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying",
956               __FUNCTION__, r);
957         send_queue_.clear();
958       } else {
959         r_log(LOG_GENERIC, LOG_DEBUG, "Would block, will retry later");
960       }
961       break;
962     }
963 
964     send_queue_.pop_front();
965   }
966 
967   return r;
968 }
969 
sendto(const void * msg,size_t len,const nr_transport_addr & to)970 int TestNrSocket::PortMapping::sendto(const void* msg, size_t len,
971                                       const nr_transport_addr& to) {
972   MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
973   r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s sending to %s",
974         external_socket_->my_addr().as_string, remote_address_.as_string,
975         to.as_string);
976 
977   last_used_ = PR_IntervalNow();
978   int r = external_socket_->sendto(msg, len, 0, &to);
979 
980   if (r == R_WOULDBLOCK) {
981     r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
982     send_queue_.emplace_back(msg, len, to);
983     return 0;
984   }
985   if (r) {
986     r_log(LOG_GENERIC, LOG_ERR, "Error: %d", r);
987   }
988 
989   return r;
990 }
991 
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)992 int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void* cb_arg,
993                                           char* function, int line) {
994   r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s waiting for %s",
995         external_socket_->my_addr().as_string, remote_address_.as_string,
996         how == NR_ASYNC_WAIT_READ ? "read" : "write");
997 
998   return external_socket_->async_wait(how, cb, cb_arg, function, line);
999 }
1000 
cancel(int how)1001 int TestNrSocket::PortMapping::cancel(int how) {
1002   r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s stop waiting for %s",
1003         external_socket_->my_addr().as_string, remote_address_.as_string,
1004         how == NR_ASYNC_WAIT_READ ? "read" : "write");
1005 
1006   return external_socket_->cancel(how);
1007 }
1008 
1009 class nr_stun_message_deleter {
1010  public:
1011   nr_stun_message_deleter() = default;
operator ()(nr_stun_message * msg) const1012   void operator()(nr_stun_message* msg) const { nr_stun_message_destroy(&msg); }
1013 };
1014 
maybe_send_fake_response(const void * msg,size_t len,const nr_transport_addr * to)1015 bool TestNrSocket::maybe_send_fake_response(const void* msg, size_t len,
1016                                             const nr_transport_addr* to) {
1017   Maybe<nsTArray<nsCString>> redirect_targets = maybe_get_redirect_targets(to);
1018   if (!redirect_targets.isSome()) {
1019     return false;
1020   }
1021 
1022   std::unique_ptr<nr_stun_message, nr_stun_message_deleter> request;
1023   {
1024     nr_stun_message* temp = nullptr;
1025     if (NS_WARN_IF(nr_stun_message_create2(&temp, (unsigned char*)msg, len))) {
1026       return false;
1027     }
1028     request.reset(temp);
1029   }
1030 
1031   if (NS_WARN_IF(nr_stun_decode_message(request.get(), nullptr, nullptr))) {
1032     return false;
1033   }
1034 
1035   std::unique_ptr<nr_stun_message, nr_stun_message_deleter> response;
1036   {
1037     nr_stun_message* temp = nullptr;
1038     if (nr_stun_message_create(&temp)) {
1039       MOZ_CRASH("nr_stun_message_create failed!");
1040     }
1041     response.reset(temp);
1042   }
1043 
1044   nr_stun_form_error_response(request.get(), response.get(), 300,
1045                               (char*)"Try alternate");
1046 
1047   int port = 0;
1048   if (nr_transport_addr_get_port(to, &port)) {
1049     MOZ_CRASH();
1050   }
1051 
1052   for (const nsCString& address : *redirect_targets) {
1053     r_log(LOG_GENERIC, LOG_DEBUG,
1054           "TestNrSocket attempting to add alternate server %s", address.Data());
1055     nr_transport_addr addr;
1056     if (NS_WARN_IF(nr_str_port_to_transport_addr(address.Data(), port,
1057                                                  IPPROTO_UDP, &addr))) {
1058       continue;
1059     }
1060     if (nr_stun_message_add_alternate_server_attribute(response.get(), &addr)) {
1061       MOZ_CRASH("nr_stun_message_add_alternate_server_attribute failed!");
1062     }
1063   }
1064 
1065   if (nr_stun_encode_message(response.get())) {
1066     MOZ_CRASH("nr_stun_encode_message failed!");
1067   }
1068 
1069   nr_transport_addr response_from;
1070   if (nr_transport_addr_is_wildcard(to)) {
1071     // |to| points to an FQDN, and nICEr is delegating DNS lookup to us; we
1072     // aren't _actually_ going to do that though, so we select a bogus address
1073     // for the response to come from. TEST-NET is a fairly reasonable thing to
1074     // use for this.
1075     int port = 0;
1076     if (nr_transport_addr_get_port(to, &port)) {
1077       MOZ_CRASH();
1078     }
1079     switch (to->ip_version) {
1080       case NR_IPV4:
1081         if (nr_str_port_to_transport_addr("198.51.100.1", port, to->protocol,
1082                                           &response_from)) {
1083           MOZ_CRASH();
1084         }
1085         break;
1086       case NR_IPV6:
1087         if (nr_str_port_to_transport_addr("::ffff:198.51.100.1", port,
1088                                           to->protocol, &response_from)) {
1089           MOZ_CRASH();
1090         }
1091         break;
1092       default:
1093         MOZ_CRASH();
1094     }
1095   } else {
1096     nr_transport_addr_copy(&response_from, to);
1097   }
1098 
1099   read_buffer_.emplace_back(response->buffer, response->length, response_from);
1100 
1101   // We dispatch this, otherwise nICEr can trip over its shoelaces
1102   r_log(LOG_GENERIC, LOG_DEBUG,
1103         "TestNrSocket %p scheduling callback for redirect response", this);
1104   GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
1105       "Async readable callback for TestNrSocket",
1106       [this, self = RefPtr<TestNrSocket>(this)] {
1107         if (poll_flags() & PR_POLL_READ) {
1108           fire_readable_callback();
1109         } else {
1110           r_log(LOG_GENERIC, LOG_DEBUG,
1111                 "TestNrSocket %p deferring callback for redirect response",
1112                 this);
1113         }
1114       }));
1115 
1116   return true;
1117 }
1118 
maybe_get_redirect_targets(const nr_transport_addr * to) const1119 Maybe<nsTArray<nsCString>> TestNrSocket::maybe_get_redirect_targets(
1120     const nr_transport_addr* to) const {
1121   Maybe<nsTArray<nsCString>> result;
1122 
1123   // 256 is overkill, but it hardly matters
1124   char addrstring[256];
1125   if (nr_transport_addr_get_addrstring(to, addrstring, 256)) {
1126     MOZ_CRASH("nr_transport_addr_get_addrstring failed!");
1127   }
1128 
1129   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket checking redirect rules for %s",
1130         addrstring);
1131   auto it = nat_->stun_redirect_map_.find(nsCString(addrstring));
1132   if (it != nat_->stun_redirect_map_.end()) {
1133     result = Some(it->second);
1134   }
1135 
1136   return result;
1137 }
1138 
1139 }  // namespace mozilla
1140