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