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 #ifndef test_nr_socket__ 84 #define test_nr_socket__ 85 86 extern "C" { 87 #include "transport_addr.h" 88 } 89 90 #include "nr_socket_prsock.h" 91 92 extern "C" { 93 #include "nr_socket.h" 94 } 95 96 #include <set> 97 #include <vector> 98 #include <map> 99 #include <list> 100 #include <string> 101 102 #include "mozilla/UniquePtr.h" 103 #include "prinrval.h" 104 #include "mediapacket.h" 105 106 namespace mozilla { 107 108 class TestNrSocket; 109 class NrSocketProxyConfig; 110 111 /** 112 * A group of TestNrSockets that behave as if they were behind the same NAT. 113 * @note We deliberately avoid addref/release of TestNrSocket here to avoid 114 * masking lifetime errors elsewhere. 115 */ 116 class TestNat { 117 public: 118 /** 119 * This allows TestNat traffic to be passively inspected. 120 * If a non-zero (error) value is returned, the packet will be dropped, 121 * allowing for tests to extend how packet manipulation is done by 122 * TestNat with having to modify TestNat itself. 123 */ 124 class NatDelegate { 125 public: 126 virtual int on_read(TestNat* nat, void* buf, size_t maxlen, 127 size_t* len) = 0; 128 virtual int on_sendto(TestNat* nat, const void* msg, size_t len, int flags, 129 const nr_transport_addr* to) = 0; 130 virtual int on_write(TestNat* nat, const void* msg, size_t len, 131 size_t* written) = 0; 132 }; 133 134 typedef enum { 135 /** For mapping, one port is used for all destinations. 136 * For filtering, allow any external address/port. */ 137 ENDPOINT_INDEPENDENT, 138 139 /** For mapping, one port for each destination address (for any port). 140 * For filtering, allow incoming traffic from addresses that outgoing 141 * traffic has been sent to. */ 142 ADDRESS_DEPENDENT, 143 144 /** For mapping, one port for each destination address/port. 145 * For filtering, allow incoming traffic only from addresses/ports that 146 * outgoing traffic has been sent to. */ 147 PORT_DEPENDENT, 148 } NatBehavior; 149 TestNat()150 TestNat() 151 : enabled_(false), 152 filtering_type_(ENDPOINT_INDEPENDENT), 153 mapping_type_(ENDPOINT_INDEPENDENT), 154 mapping_timeout_(30000), 155 allow_hairpinning_(false), 156 refresh_on_ingress_(false), 157 block_udp_(false), 158 block_stun_(false), 159 block_tcp_(false), 160 block_tls_(false), 161 error_code_for_drop_(0), 162 delay_stun_resp_ms_(0), 163 nat_delegate_(nullptr), 164 sockets_() {} 165 166 bool has_port_mappings() const; 167 168 // Helps determine whether we're hairpinning 169 bool is_my_external_tuple(const nr_transport_addr& addr) const; 170 bool is_an_internal_tuple(const nr_transport_addr& addr) const; 171 172 int create_socket_factory(nr_socket_factory** factorypp); 173 insert_socket(TestNrSocket * socket)174 void insert_socket(TestNrSocket* socket) { sockets_.insert(socket); } 175 erase_socket(TestNrSocket * socket)176 void erase_socket(TestNrSocket* socket) { sockets_.erase(socket); } 177 178 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNat); 179 180 static NatBehavior ToNatBehavior(const std::string& type); 181 182 void set_proxy_config(std::shared_ptr<NrSocketProxyConfig> aProxyConfig); 183 184 bool enabled_; 185 TestNat::NatBehavior filtering_type_; 186 TestNat::NatBehavior mapping_type_; 187 uint32_t mapping_timeout_; 188 bool allow_hairpinning_; 189 bool refresh_on_ingress_; 190 bool block_udp_; 191 bool block_stun_; 192 bool block_tcp_; 193 bool block_tls_; 194 bool error_code_for_drop_; 195 /* Note: this can only delay a single response so far (bug 1253657) */ 196 uint32_t delay_stun_resp_ms_; 197 198 // When we see an outgoing STUN request with a destination address or 199 // destination FQDN that matches a key in this map, we respond with a STUN/300 200 // with a list of ALTERNATE-SERVER fields based on the value in this map. 201 std::map<nsCString, CopyableTArray<nsCString>> stun_redirect_map_; 202 203 NatDelegate* nat_delegate_; 204 std::shared_ptr<NrSocketProxyConfig> proxy_config_; 205 206 private: 207 std::set<TestNrSocket*> sockets_; 208 209 ~TestNat() = default; 210 }; 211 212 /** 213 * Subclass of NrSocketBase that can simulate things like being behind a NAT, 214 * packet loss, latency, packet rewriting, etc. Also exposes some stuff that 215 * assists in diagnostics. 216 * This is accomplished by wrapping an "internal" socket (that handles traffic 217 * behind the NAT), and a collection of "external" sockets (that handle traffic 218 * into/out of the NAT) 219 */ 220 class TestNrSocket : public NrSocketBase { 221 public: 222 explicit TestNrSocket(TestNat* nat); 223 224 bool has_port_mappings() const; 225 bool is_my_external_tuple(const nr_transport_addr& addr) const; 226 227 // Overrides of NrSocketBase 228 int create(nr_transport_addr* addr) override; 229 int sendto(const void* msg, size_t len, int flags, 230 const nr_transport_addr* to) override; 231 int recvfrom(void* buf, size_t maxlen, size_t* len, int flags, 232 nr_transport_addr* from) override; 233 int getaddr(nr_transport_addr* addrp) override; 234 void close() override; 235 int connect(const nr_transport_addr* addr) override; 236 int write(const void* msg, size_t len, size_t* written) override; 237 int read(void* buf, size_t maxlen, size_t* len) override; 238 239 int listen(int backlog) override; 240 int accept(nr_transport_addr* addrp, nr_socket** sockp) override; 241 int async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, 242 int line) override; 243 int cancel(int how) override; 244 245 // Need override since this is virtual in NrSocketBase 246 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNrSocket, override) 247 248 private: 249 virtual ~TestNrSocket(); 250 251 class UdpPacket { 252 public: UdpPacket(const void * msg,size_t len,const nr_transport_addr & addr)253 UdpPacket(const void* msg, size_t len, const nr_transport_addr& addr) 254 : buffer_(new MediaPacket) { 255 buffer_->Copy(static_cast<const uint8_t*>(msg), len); 256 nr_transport_addr_copy(&remote_address_, &addr); 257 } 258 259 UdpPacket(UdpPacket&& aOrig) = default; 260 261 ~UdpPacket() = default; 262 263 nr_transport_addr remote_address_; 264 UniquePtr<MediaPacket> buffer_; 265 }; 266 267 class PortMapping { 268 public: 269 PortMapping(const nr_transport_addr& remote_address, 270 const RefPtr<NrSocketBase>& external_socket); 271 272 int sendto(const void* msg, size_t len, const nr_transport_addr& to); 273 int async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, 274 int line); 275 int cancel(int how); 276 int send_from_queue(); 277 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping); 278 279 PRIntervalTime last_used_; 280 RefPtr<NrSocketBase> external_socket_; 281 // For non-symmetric, most of the data here doesn't matter 282 nr_transport_addr remote_address_; 283 284 private: ~PortMapping()285 ~PortMapping() { external_socket_->close(); } 286 287 // If external_socket_ returns E_WOULDBLOCK, we don't want to propagate 288 // that to the code using the TestNrSocket. We can also perhaps use this 289 // to help simulate things like latency. 290 std::list<UdpPacket> send_queue_; 291 }; 292 293 struct DeferredPacket { DeferredPacketDeferredPacket294 DeferredPacket(TestNrSocket* sock, const void* data, size_t len, int flags, 295 const nr_transport_addr* addr, 296 RefPtr<NrSocketBase> internal_socket) 297 : socket_(sock), 298 buffer_(), 299 flags_(flags), 300 internal_socket_(internal_socket) { 301 buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len); 302 nr_transport_addr_copy(&to_, addr); 303 } 304 305 TestNrSocket* socket_; 306 MediaPacket buffer_; 307 int flags_; 308 nr_transport_addr to_; 309 RefPtr<NrSocketBase> internal_socket_; 310 }; 311 312 bool is_port_mapping_stale(const PortMapping& port_mapping) const; 313 bool allow_ingress(const nr_transport_addr& to, const nr_transport_addr& from, 314 PortMapping** port_mapping_used) const; 315 void destroy_stale_port_mappings(); 316 317 static void socket_readable_callback(void* real_sock_v, int how, 318 void* test_sock_v); 319 void on_socket_readable(NrSocketBase* external_or_internal_socket); 320 void fire_readable_callback(); 321 322 static void port_mapping_tcp_passthrough_callback(void* ext_sock_v, int how, 323 void* test_sock_v); 324 void cancel_port_mapping_async_wait(int how); 325 326 static void port_mapping_writeable_callback(void* ext_sock_v, int how, 327 void* test_sock_v); 328 void write_to_port_mapping(NrSocketBase* external_socket); 329 bool is_tcp_connection_behind_nat() const; 330 331 PortMapping* get_port_mapping(const nr_transport_addr& remote_addr, 332 TestNat::NatBehavior filter) const; 333 static bool port_mapping_matches(const PortMapping& port_mapping, 334 const nr_transport_addr& remote_addr, 335 TestNat::NatBehavior filter); 336 PortMapping* create_port_mapping( 337 const nr_transport_addr& remote_addr, 338 const RefPtr<NrSocketBase>& external_socket) const; 339 RefPtr<NrSocketBase> create_external_socket( 340 const nr_transport_addr& remote_addr) const; 341 342 static void process_delayed_cb(NR_SOCKET s, int how, void* cb_arg); 343 344 bool maybe_send_fake_response(const void* msg, size_t len, 345 const nr_transport_addr* to); 346 Maybe<nsTArray<nsCString>> maybe_get_redirect_targets( 347 const nr_transport_addr* to) const; 348 349 RefPtr<NrSocketBase> readable_socket_; 350 // The socket for the "internal" address; used to talk to stuff behind the 351 // same nat. 352 RefPtr<NrSocketBase> internal_socket_; 353 RefPtr<TestNat> nat_; 354 bool tls_; 355 // Since our comparison logic is different depending on what kind of NAT 356 // we simulate, and the STL does not make it very easy to switch out the 357 // comparison function at runtime, and these lists are going to be very 358 // small anyway, we just brute-force it. 359 std::list<RefPtr<PortMapping>> port_mappings_; 360 361 void* timer_handle_; 362 363 // Just used for fake stun responses right now. Not _necessarily_ just UDP 364 // stuff, UdpPacket just has what we need to make this work for UDP. 365 std::list<UdpPacket> read_buffer_; 366 std::unique_ptr<nr_transport_addr> connect_fake_stun_address_; 367 }; 368 369 } // namespace mozilla 370 371 #endif // test_nr_socket__ 372