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