multi_tcp_socket_unittest.cpp (16488B)
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 #include <iostream> 8 #include <vector> 9 10 #include "mozilla/Atomics.h" 11 #include "pk11pub.h" 12 #include "runnable_utils.h" 13 14 extern "C" { 15 // clang-format off 16 #include "nr_api.h" 17 #include "nr_socket.h" 18 #include "transport_addr.h" 19 #include "nr_socket_multi_tcp.h" 20 // clang-format on 21 } 22 23 #include "nricectx.h" 24 #include "stunserver.h" 25 26 #define GTEST_HAS_RTTI 0 27 #include "gtest/gtest.h" 28 #include "gtest_utils.h" 29 30 using namespace mozilla; 31 32 namespace { 33 34 class MultiTcpSocketTest : public MtransportTest { 35 public: 36 MultiTcpSocketTest() : socks(3, nullptr), readable(false) {} 37 38 void SetUp() { 39 MtransportTest::SetUp(); 40 41 test_utils_->SyncDispatchToSTS( 42 WrapRunnable(this, &MultiTcpSocketTest::SetUp_s)); 43 } 44 45 void SetUp_s() { 46 NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); 47 ice_ctx_ = NrIceCtx::Create("stun"); 48 TestStunTcpServer::GetInstance(AF_INET); 49 TestStunTcpServer::GetInstance(AF_INET6); 50 } 51 52 void TearDown() { 53 test_utils_->SyncDispatchToSTS( 54 WrapRunnable(this, &MultiTcpSocketTest::Shutdown_s)); 55 56 MtransportTest::TearDown(); 57 } 58 59 DISALLOW_COPY_ASSIGN(MultiTcpSocketTest); 60 61 static void SockReadable(NR_SOCKET s, int how, void* arg) { 62 MultiTcpSocketTest* obj = static_cast<MultiTcpSocketTest*>(arg); 63 obj->SetReadable(true); 64 } 65 66 void Shutdown_s() { 67 ice_ctx_ = nullptr; 68 for (auto& sock : socks) { 69 nr_socket_destroy(&sock); 70 } 71 } 72 73 static uint16_t GetRandomPort() { 74 uint16_t result; 75 if (PK11_GenerateRandom((unsigned char*)&result, 2) != SECSuccess) { 76 MOZ_ASSERT(false); 77 return 0; 78 } 79 return result; 80 } 81 82 static uint16_t EnsureEphemeral(uint16_t port) { 83 // IANA ephemeral port range (49152 to 65535) 84 return port | 49152; 85 } 86 87 void Create_s(nr_socket_tcp_type tcp_type, std::string stun_server_addr, 88 uint16_t stun_server_port, nr_socket** sock) { 89 nr_transport_addr local; 90 // Get start of port range for test 91 static unsigned short port_s = GetRandomPort(); 92 int r; 93 94 if (!stun_server_addr.empty()) { 95 std::vector<NrIceStunServer> stun_servers; 96 UniquePtr<NrIceStunServer> server(NrIceStunServer::Create( 97 stun_server_addr, stun_server_port, kNrIceTransportTcp)); 98 stun_servers.push_back(*server); 99 100 ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers))); 101 } 102 103 r = 1; 104 for (int tries = 10; tries && r; --tries) { 105 r = nr_str_port_to_transport_addr( 106 (char*)"127.0.0.1", EnsureEphemeral(port_s++), IPPROTO_TCP, &local); 107 ASSERT_EQ(0, r); 108 109 r = nr_socket_multi_tcp_create(ice_ctx_->ctx(), nullptr, &local, tcp_type, 110 1, 2048, sock); 111 } 112 113 ASSERT_EQ(0, r); 114 printf("Creating socket on %s\n", local.as_string); 115 r = nr_socket_multi_tcp_set_readable_cb( 116 *sock, &MultiTcpSocketTest::SockReadable, this); 117 ASSERT_EQ(0, r); 118 } 119 120 nr_socket* Create(nr_socket_tcp_type tcp_type, 121 std::string stun_server_addr = "", 122 uint16_t stun_server_port = 0) { 123 nr_socket* sock = nullptr; 124 test_utils_->SyncDispatchToSTS( 125 WrapRunnable(this, &MultiTcpSocketTest::Create_s, tcp_type, 126 stun_server_addr, stun_server_port, &sock)); 127 return sock; 128 } 129 130 void Listen_s(nr_socket* sock) { 131 nr_transport_addr addr; 132 int r = nr_socket_getaddr(sock, &addr); 133 ASSERT_EQ(0, r); 134 printf("Listening on %s\n", addr.as_string); 135 r = nr_socket_listen(sock, 5); 136 ASSERT_EQ(0, r); 137 } 138 139 void Listen(nr_socket* sock) { 140 test_utils_->SyncDispatchToSTS( 141 WrapRunnable(this, &MultiTcpSocketTest::Listen_s, sock)); 142 } 143 144 void Destroy_s(nr_socket* sock) { 145 int r = nr_socket_destroy(&sock); 146 ASSERT_EQ(0, r); 147 } 148 149 void Destroy(nr_socket* sock) { 150 test_utils_->SyncDispatchToSTS( 151 WrapRunnable(this, &MultiTcpSocketTest::Destroy_s, sock)); 152 } 153 154 void Connect_s(nr_socket* from, nr_socket* to) { 155 nr_transport_addr addr_to; 156 nr_transport_addr addr_from; 157 int r = nr_socket_getaddr(to, &addr_to); 158 ASSERT_EQ(0, r); 159 r = nr_socket_getaddr(from, &addr_from); 160 ASSERT_EQ(0, r); 161 printf("Connecting from %s to %s\n", addr_from.as_string, 162 addr_to.as_string); 163 r = nr_socket_connect(from, &addr_to); 164 ASSERT_EQ(0, r); 165 } 166 167 void Connect(nr_socket* from, nr_socket* to) { 168 test_utils_->SyncDispatchToSTS( 169 WrapRunnable(this, &MultiTcpSocketTest::Connect_s, from, to)); 170 } 171 172 void ConnectSo_s(nr_socket* so1, nr_socket* so2) { 173 nr_transport_addr addr_so1; 174 nr_transport_addr addr_so2; 175 int r = nr_socket_getaddr(so1, &addr_so1); 176 ASSERT_EQ(0, r); 177 r = nr_socket_getaddr(so2, &addr_so2); 178 ASSERT_EQ(0, r); 179 printf("Connecting SO %s <-> %s\n", addr_so1.as_string, addr_so2.as_string); 180 r = nr_socket_connect(so1, &addr_so2); 181 ASSERT_EQ(0, r); 182 r = nr_socket_connect(so2, &addr_so1); 183 ASSERT_EQ(0, r); 184 } 185 186 void ConnectSo(nr_socket* from, nr_socket* to) { 187 test_utils_->SyncDispatchToSTS( 188 WrapRunnable(this, &MultiTcpSocketTest::ConnectSo_s, from, to)); 189 } 190 191 void SendDataToAddress_s(nr_socket* from, nr_transport_addr* to, 192 const char* data, size_t len) { 193 nr_transport_addr addr_from; 194 195 int r = nr_socket_getaddr(from, &addr_from); 196 ASSERT_EQ(0, r); 197 printf("Sending %lu bytes %s -> %s\n", (unsigned long)len, 198 addr_from.as_string, to->as_string); 199 r = nr_socket_sendto(from, data, len, 0, to); 200 ASSERT_EQ(0, r); 201 } 202 203 void SendData(nr_socket* from, nr_transport_addr* to, const char* data, 204 size_t len) { 205 test_utils_->SyncDispatchToSTS(WrapRunnable( 206 this, &MultiTcpSocketTest::SendDataToAddress_s, from, to, data, len)); 207 } 208 209 void SendDataToSocket_s(nr_socket* from, nr_socket* to, const char* data, 210 size_t len) { 211 nr_transport_addr addr_to; 212 213 int r = nr_socket_getaddr(to, &addr_to); 214 ASSERT_EQ(0, r); 215 SendDataToAddress_s(from, &addr_to, data, len); 216 } 217 218 void SendData(nr_socket* from, nr_socket* to, const char* data, size_t len) { 219 test_utils_->SyncDispatchToSTS(WrapRunnable( 220 this, &MultiTcpSocketTest::SendDataToSocket_s, from, to, data, len)); 221 } 222 223 void RecvDataFromAddress_s(nr_transport_addr* expected_from, 224 nr_socket* sent_to, const char* expected_data, 225 size_t expected_len) { 226 SetReadable(false); 227 size_t buflen = expected_len ? expected_len + 1 : 100; 228 char received_data[buflen]; 229 nr_transport_addr addr_to; 230 nr_transport_addr retaddr; 231 size_t retlen; 232 233 int r = nr_socket_getaddr(sent_to, &addr_to); 234 ASSERT_EQ(0, r); 235 printf("Receiving %lu bytes %s <- %s\n", (unsigned long)expected_len, 236 addr_to.as_string, expected_from->as_string); 237 r = nr_socket_recvfrom(sent_to, received_data, buflen, &retlen, 0, 238 &retaddr); 239 ASSERT_EQ(0, r); 240 r = nr_transport_addr_cmp(&retaddr, expected_from, 241 NR_TRANSPORT_ADDR_CMP_MODE_ALL); 242 ASSERT_EQ(0, r); 243 // expected_len == 0 means we just expected some data 244 if (expected_len == 0) { 245 ASSERT_GT(retlen, 0U); 246 } else { 247 ASSERT_EQ(expected_len, retlen); 248 r = memcmp(expected_data, received_data, retlen); 249 ASSERT_EQ(0, r); 250 } 251 } 252 253 void RecvData(nr_transport_addr* expected_from, nr_socket* sent_to, 254 const char* expected_data = nullptr, size_t expected_len = 0) { 255 ASSERT_TRUE_WAIT(IsReadable(), 1000); 256 test_utils_->SyncDispatchToSTS( 257 WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromAddress_s, 258 expected_from, sent_to, expected_data, expected_len)); 259 } 260 261 void RecvDataFromSocket_s(nr_socket* expected_from, nr_socket* sent_to, 262 const char* expected_data, size_t expected_len) { 263 nr_transport_addr addr_from; 264 265 int r = nr_socket_getaddr(expected_from, &addr_from); 266 ASSERT_EQ(0, r); 267 268 RecvDataFromAddress_s(&addr_from, sent_to, expected_data, expected_len); 269 } 270 271 void RecvData(nr_socket* expected_from, nr_socket* sent_to, 272 const char* expected_data, size_t expected_len) { 273 ASSERT_TRUE_WAIT(IsReadable(), 1000); 274 test_utils_->SyncDispatchToSTS( 275 WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromSocket_s, 276 expected_from, sent_to, expected_data, expected_len)); 277 } 278 279 void RecvDataFailed_s(nr_socket* sent_to, size_t expected_len, 280 int expected_err) { 281 SetReadable(false); 282 char received_data[expected_len + 1]; 283 nr_transport_addr addr_to; 284 nr_transport_addr retaddr; 285 size_t retlen; 286 287 int r = nr_socket_getaddr(sent_to, &addr_to); 288 ASSERT_EQ(0, r); 289 r = nr_socket_recvfrom(sent_to, received_data, expected_len + 1, &retlen, 0, 290 &retaddr); 291 ASSERT_EQ(expected_err, r) << "Expecting receive failure " << expected_err 292 << " on " << addr_to.as_string; 293 } 294 295 void RecvDataFailed(nr_socket* sent_to, size_t expected_len, 296 int expected_err) { 297 ASSERT_TRUE_WAIT(IsReadable(), 1000); 298 test_utils_->SyncDispatchToSTS( 299 WrapRunnable(this, &MultiTcpSocketTest::RecvDataFailed_s, sent_to, 300 expected_len, expected_err)); 301 } 302 303 void TransferData(nr_socket* from, nr_socket* to, const char* data, 304 size_t len) { 305 SendData(from, to, data, len); 306 RecvData(from, to, data, len); 307 } 308 309 protected: 310 bool IsReadable() const { return readable; } 311 void SetReadable(bool r) { readable = r; } 312 std::vector<nr_socket*> socks; 313 Atomic<bool> readable; 314 RefPtr<NrIceCtx> ice_ctx_; 315 }; 316 } // namespace 317 318 TEST_F(MultiTcpSocketTest, TestListen) { 319 socks[0] = Create(TCP_TYPE_PASSIVE); 320 Listen(socks[0]); 321 } 322 323 TEST_F(MultiTcpSocketTest, TestConnect) { 324 socks[0] = Create(TCP_TYPE_PASSIVE); 325 socks[1] = Create(TCP_TYPE_ACTIVE); 326 socks[2] = Create(TCP_TYPE_ACTIVE); 327 Listen(socks[0]); 328 Connect(socks[1], socks[0]); 329 Connect(socks[2], socks[0]); 330 } 331 332 TEST_F(MultiTcpSocketTest, TestTransmit) { 333 const char data[] = "TestTransmit"; 334 socks[0] = Create(TCP_TYPE_ACTIVE); 335 socks[1] = Create(TCP_TYPE_PASSIVE); 336 Listen(socks[1]); 337 Connect(socks[0], socks[1]); 338 339 TransferData(socks[0], socks[1], data, sizeof(data)); 340 TransferData(socks[1], socks[0], data, sizeof(data)); 341 } 342 343 TEST_F(MultiTcpSocketTest, TestClosePassive) { 344 const char data[] = "TestClosePassive"; 345 socks[0] = Create(TCP_TYPE_ACTIVE); 346 socks[1] = Create(TCP_TYPE_PASSIVE); 347 Listen(socks[1]); 348 Connect(socks[0], socks[1]); 349 350 TransferData(socks[0], socks[1], data, sizeof(data)); 351 TransferData(socks[1], socks[0], data, sizeof(data)); 352 353 /* We have to destroy as only that calls PR_Close() */ 354 std::cerr << "Destructing socket" << std::endl; 355 Destroy(socks[1]); 356 357 RecvDataFailed(socks[0], sizeof(data), R_EOD); 358 359 socks[1] = nullptr; 360 } 361 362 TEST_F(MultiTcpSocketTest, TestCloseActive) { 363 const char data[] = "TestCloseActive"; 364 socks[0] = Create(TCP_TYPE_ACTIVE); 365 socks[1] = Create(TCP_TYPE_PASSIVE); 366 Listen(socks[1]); 367 Connect(socks[0], socks[1]); 368 369 TransferData(socks[0], socks[1], data, sizeof(data)); 370 TransferData(socks[1], socks[0], data, sizeof(data)); 371 372 /* We have to destroy as only that calls PR_Close() */ 373 std::cerr << "Destructing socket" << std::endl; 374 Destroy(socks[0]); 375 376 RecvDataFailed(socks[1], sizeof(data), R_EOD); 377 378 socks[0] = nullptr; 379 } 380 381 TEST_F(MultiTcpSocketTest, TestTwoSendsBeforeReceives) { 382 const char data1[] = "TestTwoSendsBeforeReceives"; 383 const char data2[] = "2nd data"; 384 socks[0] = Create(TCP_TYPE_ACTIVE); 385 socks[1] = Create(TCP_TYPE_PASSIVE); 386 Listen(socks[1]); 387 Connect(socks[0], socks[1]); 388 389 SendData(socks[0], socks[1], data1, sizeof(data1)); 390 SendData(socks[0], socks[1], data2, sizeof(data2)); 391 RecvData(socks[0], socks[1], data1, sizeof(data1)); 392 /* ICE TCP framing turns TCP effectively into datagram mode */ 393 RecvData(socks[0], socks[1], data2, sizeof(data2)); 394 } 395 396 TEST_F(MultiTcpSocketTest, TestTwoActiveBidirectionalTransmit) { 397 const char data1[] = "TestTwoActiveBidirectionalTransmit"; 398 const char data2[] = "ReplyToTheFirstSocket"; 399 const char data3[] = "TestMessageFromTheSecondSocket"; 400 const char data4[] = "ThisIsAReplyToTheSecondSocket"; 401 socks[0] = Create(TCP_TYPE_PASSIVE); 402 socks[1] = Create(TCP_TYPE_ACTIVE); 403 socks[2] = Create(TCP_TYPE_ACTIVE); 404 Listen(socks[0]); 405 Connect(socks[1], socks[0]); 406 Connect(socks[2], socks[0]); 407 408 TransferData(socks[1], socks[0], data1, sizeof(data1)); 409 TransferData(socks[0], socks[1], data2, sizeof(data2)); 410 TransferData(socks[2], socks[0], data3, sizeof(data3)); 411 TransferData(socks[0], socks[2], data4, sizeof(data4)); 412 } 413 414 TEST_F(MultiTcpSocketTest, TestTwoPassiveBidirectionalTransmit) { 415 const char data1[] = "TestTwoPassiveBidirectionalTransmit"; 416 const char data2[] = "FirstReply"; 417 const char data3[] = "TestTwoPassiveBidirectionalTransmitToTheSecondSock"; 418 const char data4[] = "SecondReply"; 419 socks[0] = Create(TCP_TYPE_PASSIVE); 420 socks[1] = Create(TCP_TYPE_PASSIVE); 421 socks[2] = Create(TCP_TYPE_ACTIVE); 422 Listen(socks[0]); 423 Listen(socks[1]); 424 Connect(socks[2], socks[0]); 425 Connect(socks[2], socks[1]); 426 427 TransferData(socks[2], socks[0], data1, sizeof(data1)); 428 TransferData(socks[0], socks[2], data2, sizeof(data2)); 429 TransferData(socks[2], socks[1], data3, sizeof(data3)); 430 TransferData(socks[1], socks[2], data4, sizeof(data4)); 431 } 432 433 TEST_F(MultiTcpSocketTest, TestActivePassiveWithStunServerMockup) { 434 /* Fake STUN message able to pass the nr_is_stun_msg check 435 used in nr_socket_buffered_stun */ 436 const char stunMessage[] = {'\x00', '\x01', '\x00', '\x04', '\x21', '\x12', 437 '\xa4', '\x42', '\x00', '\x00', '\x00', '\x00', 438 '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', 439 '\x00', '\x00', '\x1c', '\xed', '\xca', '\xfe'}; 440 const char data[] = "TestActivePassiveWithStunServerMockup"; 441 442 nr_transport_addr stun_srv_addr; 443 std::string stun_addr; 444 uint16_t stun_port; 445 stun_addr = TestStunTcpServer::GetInstance(AF_INET)->addr(); 446 stun_port = TestStunTcpServer::GetInstance(AF_INET)->port(); 447 int r = nr_str_port_to_transport_addr(stun_addr.c_str(), stun_port, 448 IPPROTO_TCP, &stun_srv_addr); 449 ASSERT_EQ(0, r); 450 451 socks[0] = Create(TCP_TYPE_PASSIVE, stun_addr, stun_port); 452 Listen(socks[0]); 453 socks[1] = Create(TCP_TYPE_ACTIVE, stun_addr, stun_port); 454 455 /* Send a fake STUN request and expect a STUN error response */ 456 SendData(socks[0], &stun_srv_addr, stunMessage, sizeof(stunMessage)); 457 RecvData(&stun_srv_addr, socks[0]); 458 459 Connect(socks[1], socks[0]); 460 TransferData(socks[1], socks[0], data, sizeof(data)); 461 TransferData(socks[0], socks[1], data, sizeof(data)); 462 } 463 464 TEST_F(MultiTcpSocketTest, TestConnectTwoSo) { 465 socks[0] = Create(TCP_TYPE_SO); 466 socks[1] = Create(TCP_TYPE_SO); 467 ConnectSo(socks[0], socks[1]); 468 } 469 470 // test works on localhost only with delay applied: 471 // tc qdisc add dev lo root netem delay 5ms 472 TEST_F(MultiTcpSocketTest, DISABLED_TestTwoSoBidirectionalTransmit) { 473 const char data[] = "TestTwoSoBidirectionalTransmit"; 474 socks[0] = Create(TCP_TYPE_SO); 475 socks[1] = Create(TCP_TYPE_SO); 476 ConnectSo(socks[0], socks[1]); 477 TransferData(socks[0], socks[1], data, sizeof(data)); 478 TransferData(socks[1], socks[0], data, sizeof(data)); 479 } 480 481 TEST_F(MultiTcpSocketTest, TestBigData) { 482 char buf1[2048]; 483 char buf2[1024]; 484 485 for (unsigned i = 0; i < sizeof(buf1); ++i) { 486 buf1[i] = i & 0xff; 487 } 488 for (unsigned i = 0; i < sizeof(buf2); ++i) { 489 buf2[i] = (i + 0x80) & 0xff; 490 } 491 socks[0] = Create(TCP_TYPE_ACTIVE); 492 socks[1] = Create(TCP_TYPE_PASSIVE); 493 Listen(socks[1]); 494 Connect(socks[0], socks[1]); 495 496 TransferData(socks[0], socks[1], buf1, sizeof(buf1)); 497 TransferData(socks[0], socks[1], buf2, sizeof(buf2)); 498 // opposite dir 499 SendData(socks[1], socks[0], buf2, sizeof(buf2)); 500 SendData(socks[1], socks[0], buf1, sizeof(buf1)); 501 RecvData(socks[1], socks[0], buf2, sizeof(buf2)); 502 RecvData(socks[1], socks[0], buf1, sizeof(buf1)); 503 }