VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceTcp.cpp@ 68181

Last change on this file since 68181 was 64366, checked in by vboxsync, 8 years ago

ValidationKit: Doxygen

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.7 KB
Line 
1/* $Id: TestExecServiceTcp.cpp 64366 2016-10-22 16:36:39Z vboxsync $ */
2/** @file
3 * TestExecServ - Basic Remote Execution Service, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DEFAULT
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/critsect.h>
35#include <iprt/err.h>
36#include <iprt/log.h>
37#include <iprt/mem.h>
38#include <iprt/message.h>
39#include <iprt/poll.h>
40#include <iprt/string.h>
41#include <iprt/tcp.h>
42#include <iprt/thread.h>
43#include <iprt/time.h>
44
45#include "TestExecServiceInternal.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51/** The default server port. */
52#define TXS_TCP_DEF_BIND_PORT 5042
53/** The default client port. */
54#define TXS_TCP_DEF_CONNECT_PORT 5048
55
56/** The default server bind address. */
57#define TXS_TCP_DEF_BIND_ADDRESS ""
58/** The default client connect address (i.e. of the host server). */
59#define TXS_TCP_DEF_CONNECT_ADDRESS "10.0.2.2"
60
61
62/*********************************************************************************************************************************
63* Global Variables *
64*********************************************************************************************************************************/
65/** @name TCP Parameters
66 * @{ */
67static enum { TXSTCPMODE_BOTH, TXSTCPMODE_CLIENT, TXSTCPMODE_SERVER }
68 g_enmTcpMode = TXSTCPMODE_BOTH;
69
70/** The addresses to bind to. Empty string means any. */
71static char g_szTcpBindAddr[256] = TXS_TCP_DEF_BIND_ADDRESS;
72/** The TCP port to listen to. */
73static uint32_t g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;
74/** The addresses to connect to if fRevesedSetupMode is @c true. */
75static char g_szTcpConnectAddr[256] = TXS_TCP_DEF_CONNECT_ADDRESS;
76/** The TCP port to listen to. */
77static uint32_t g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;
78/** @} */
79
80/** Critical section for serializing access to the next few variables. */
81static RTCRITSECT g_TcpCritSect;
82/** Pointer to the TCP server instance. */
83static PRTTCPSERVER g_pTcpServer = NULL;
84/** Thread calling RTTcpServerListen2. */
85static RTTHREAD g_hThreadTcpServer = NIL_RTTHREAD;
86/** Thread calling RTTcpClientConnect. */
87static RTTHREAD g_hThreadTcpConnect = NIL_RTTHREAD;
88/** The main thread handle (for signalling). */
89static RTTHREAD g_hThreadMain = NIL_RTTHREAD;
90/** Stop connecting attempts when set. */
91static bool g_fTcpStopConnecting = false;
92/** Connect cancel cookie. */
93static PRTTCPCLIENTCONNECTCANCEL volatile g_pTcpConnectCancelCookie = NULL;
94
95/** Socket of the current client. */
96static RTSOCKET g_hTcpClient = NIL_RTSOCKET;
97/** Indicates whether g_hTcpClient comes from the server or from a client
98 * connect (relevant when closing it). */
99static bool g_fTcpClientFromServer = false;
100/** The size of the stashed data. */
101static size_t g_cbTcpStashed = 0;
102/** The size of the stashed data allocation. */
103static size_t g_cbTcpStashedAlloced = 0;
104/** The stashed data. */
105static uint8_t *g_pbTcpStashed = NULL;
106
107
108
109/**
110 * Disconnects the current client.
111 */
112static void txsTcpDisconnectClient(void)
113{
114 int rc;
115 if (g_fTcpClientFromServer)
116 rc = RTTcpServerDisconnectClient2(g_hTcpClient);
117 else
118 rc = RTTcpClientClose(g_hTcpClient);
119 AssertRCSuccess(rc);
120 g_hTcpClient = NIL_RTSOCKET;
121}
122
123/**
124 * Sets the current client socket in a safe manner.
125 *
126 * @returns NIL_RTSOCKET if consumed, other wise hTcpClient.
127 * @param hTcpClient The client socket.
128 */
129static RTSOCKET txsTcpSetClient(RTSOCKET hTcpClient)
130{
131 RTCritSectEnter(&g_TcpCritSect);
132 if ( g_hTcpClient == NIL_RTSOCKET
133 && !g_fTcpStopConnecting
134 && g_hThreadMain != NIL_RTTHREAD
135 )
136 {
137 g_fTcpClientFromServer = true;
138 g_hTcpClient = hTcpClient;
139 int rc = RTThreadUserSignal(g_hThreadMain); AssertRC(rc);
140 hTcpClient = NIL_RTSOCKET;
141 }
142 RTCritSectLeave(&g_TcpCritSect);
143 return hTcpClient;
144}
145
146/**
147 * Server mode connection thread.
148 *
149 * @returns iprt status code.
150 * @param hSelf Thread handle. Ignored.
151 * @param pvUser Ignored.
152 */
153static DECLCALLBACK(int) txsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
154{
155 RTSOCKET hTcpClient;
156 int rc = RTTcpServerListen2(g_pTcpServer, &hTcpClient);
157 Log(("txsTcpConnectServerThread: RTTcpServerListen2 -> %Rrc\n", rc));
158 if (RT_SUCCESS(rc))
159 {
160 hTcpClient = txsTcpSetClient(hTcpClient);
161 RTTcpServerDisconnectClient2(hTcpClient);
162 }
163
164 RT_NOREF2(hSelf, pvUser);
165 return rc;
166}
167
168/**
169 * Checks if it's a fatal RTTcpClientConnect return code.
170 *
171 * @returns true / false.
172 * @param rc The IPRT status code.
173 */
174static bool txsTcpIsFatalClientConnectStatus(int rc)
175{
176 return rc != VERR_NET_UNREACHABLE
177 && rc != VERR_NET_HOST_DOWN
178 && rc != VERR_NET_HOST_UNREACHABLE
179 && rc != VERR_NET_CONNECTION_REFUSED
180 && rc != VERR_TIMEOUT
181 && rc != VERR_NET_CONNECTION_TIMED_OUT;
182}
183
184/**
185 * Client mode connection thread.
186 *
187 * @returns iprt status code.
188 * @param hSelf Thread handle. Use to sleep on. The main thread will
189 * signal it to speed up thread shutdown.
190 * @param pvUser Ignored.
191 */
192static DECLCALLBACK(int) txsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser)
193{
194 RT_NOREF1(pvUser);
195
196 for (;;)
197 {
198 /* Stop? */
199 RTCritSectEnter(&g_TcpCritSect);
200 bool fStop = g_fTcpStopConnecting;
201 RTCritSectLeave(&g_TcpCritSect);
202 if (fStop)
203 return VINF_SUCCESS;
204
205 /* Try connect. */ /** @todo make cancelable! */
206 RTSOCKET hTcpClient;
207 Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort));
208 int rc = RTTcpClientConnectEx(g_szTcpConnectAddr, g_uTcpConnectPort, &hTcpClient,
209 RT_SOCKETCONNECT_DEFAULT_WAIT, &g_pTcpConnectCancelCookie);
210 Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc));
211 if (RT_SUCCESS(rc))
212 {
213 hTcpClient = txsTcpSetClient(hTcpClient);
214 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
215 break;
216 }
217
218 if (txsTcpIsFatalClientConnectStatus(rc))
219 return rc;
220
221 /* Delay a wee bit before retrying. */
222 RTThreadUserWait(hSelf, 1536);
223 }
224 return VINF_SUCCESS;
225}
226
227/**
228 * Wait on the threads to complete.
229 *
230 * @returns Thread status (if collected), otherwise VINF_SUCCESS.
231 * @param cMillies The period to wait on each thread.
232 */
233static int txsTcpConnectWaitOnThreads(RTMSINTERVAL cMillies)
234{
235 int rcRet = VINF_SUCCESS;
236
237 if (g_hThreadTcpConnect != NIL_RTTHREAD)
238 {
239 int rcThread;
240 int rc2 = RTThreadWait(g_hThreadTcpConnect, cMillies, &rcThread);
241 if (RT_SUCCESS(rc2))
242 {
243 g_hThreadTcpConnect = NIL_RTTHREAD;
244 rcRet = rcThread;
245 }
246 }
247
248 if (g_hThreadTcpServer != NIL_RTTHREAD)
249 {
250 int rcThread;
251 int rc2 = RTThreadWait(g_hThreadTcpServer, cMillies, &rcThread);
252 if (RT_SUCCESS(rc2))
253 {
254 g_hThreadTcpServer = NIL_RTTHREAD;
255 if (RT_SUCCESS(rc2))
256 rcRet = rcThread;
257 }
258 }
259 return rcRet;
260}
261
262/**
263 * Connects to the peer.
264 *
265 * @returns VBox status code. Updates g_hTcpClient and g_fTcpClientFromServer on
266 * success
267 */
268static int txsTcpConnect(void)
269{
270 int rc;
271 if (g_enmTcpMode == TXSTCPMODE_SERVER)
272 {
273 g_fTcpClientFromServer = true;
274 rc = RTTcpServerListen2(g_pTcpServer, &g_hTcpClient);
275 Log(("txsTcpRecvPkt: RTTcpServerListen2 -> %Rrc\n", rc));
276 }
277 else if (g_enmTcpMode == TXSTCPMODE_CLIENT)
278 {
279 g_fTcpClientFromServer = false;
280 for (;;)
281 {
282 Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort));
283 rc = RTTcpClientConnect(g_szTcpConnectAddr, g_uTcpConnectPort, &g_hTcpClient);
284 Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc));
285 if (RT_SUCCESS(rc) || txsTcpIsFatalClientConnectStatus(rc))
286 break;
287
288 /* Delay a wee bit before retrying. */
289 RTThreadSleep(1536);
290 }
291 }
292 else
293 {
294 Assert(g_enmTcpMode == TXSTCPMODE_BOTH);
295 RTTHREAD hSelf = RTThreadSelf();
296
297 /*
298 * Create client threads.
299 */
300 RTCritSectEnter(&g_TcpCritSect);
301 RTThreadUserReset(hSelf);
302 g_hThreadMain = hSelf;
303 g_fTcpStopConnecting = false;
304 RTCritSectLeave(&g_TcpCritSect);
305
306 txsTcpConnectWaitOnThreads(32);
307
308 rc = VINF_SUCCESS;
309 if (g_hThreadTcpConnect == NIL_RTTHREAD)
310 {
311 g_pTcpConnectCancelCookie = NULL;
312 rc = RTThreadCreate(&g_hThreadTcpConnect, txsTcpClientConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
313 RTTHREADFLAGS_WAITABLE, "tcpconn");
314 }
315 if (g_hThreadTcpServer == NIL_RTTHREAD && RT_SUCCESS(rc))
316 rc = RTThreadCreate(&g_hThreadTcpServer, txsTcpServerConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
317 RTTHREADFLAGS_WAITABLE, "tcpserv");
318
319 RTCritSectEnter(&g_TcpCritSect);
320
321 /*
322 * Wait for connection to be established.
323 */
324 while ( RT_SUCCESS(rc)
325 && g_hTcpClient == NIL_RTSOCKET)
326 {
327 RTCritSectLeave(&g_TcpCritSect);
328 RTThreadUserWait(hSelf, 1536);
329 rc = txsTcpConnectWaitOnThreads(0);
330 RTCritSectEnter(&g_TcpCritSect);
331 }
332
333 /*
334 * Cancel the threads.
335 */
336 g_hThreadMain = NIL_RTTHREAD;
337 g_fTcpStopConnecting = true;
338
339 RTCritSectLeave(&g_TcpCritSect);
340 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
341 }
342
343 AssertMsg(RT_SUCCESS(rc) ? g_hTcpClient != NIL_RTSOCKET : g_hTcpClient == NIL_RTSOCKET, ("%Rrc %p\n", rc, g_hTcpClient));
344 g_cbTcpStashed = 0;
345 return rc;
346}
347
348/**
349 * @interface_method_impl{TXSTRANSPORT,pfnNotifyReboot}
350 */
351static DECLCALLBACK(void) txsTcpNotifyReboot(void)
352{
353 Log(("txsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
354 if (g_pTcpServer)
355 {
356 int rc = RTTcpServerDestroy(g_pTcpServer);
357 if (RT_FAILURE(rc))
358 RTMsgInfo("RTTcpServerDestroy failed in txsTcpNotifyReboot: %Rrc", rc);
359 g_pTcpServer = NULL;
360 }
361}
362
363/**
364 * @interface_method_impl{TXSTRANSPORT,pfnNotifyBye}
365 */
366static DECLCALLBACK(void) txsTcpNotifyBye(void)
367{
368 Log(("txsTcpNotifyBye: txsTcpDisconnectClient %RTsock\n", g_hTcpClient));
369 txsTcpDisconnectClient();
370}
371
372/**
373 * @interface_method_impl{TXSTRANSPORT,pfnNotifyHowdy}
374 */
375static DECLCALLBACK(void) txsTcpNotifyHowdy(void)
376{
377 /* nothing to do here */
378}
379
380/**
381 * @interface_method_impl{TXSTRANSPORT,pfnBabble}
382 */
383static DECLCALLBACK(void) txsTcpBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
384{
385 /*
386 * Quietly ignore already disconnected client.
387 */
388 RTSOCKET hTcpClient = g_hTcpClient;
389 if (hTcpClient == NIL_RTSOCKET)
390 return;
391
392 /*
393 * Try send the babble reply.
394 */
395 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
396 int rc;
397 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
398 do rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
399 while (rc == VERR_INTERRUPTED);
400
401 /*
402 * Disconnect the client.
403 */
404 Log(("txsTcpBabble: txsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", g_hTcpClient, rc));
405 txsTcpDisconnectClient();
406}
407
408/**
409 * @interface_method_impl{TXSTRANSPORT,pfnSendPkt}
410 */
411static DECLCALLBACK(int) txsTcpSendPkt(PCTXSPKTHDR pPktHdr)
412{
413 Assert(pPktHdr->cb >= sizeof(TXSPKTHDR));
414
415 /*
416 * Fail if no client connection.
417 */
418 RTSOCKET hTcpClient = g_hTcpClient;
419 if (hTcpClient == NIL_RTSOCKET)
420 return VERR_NET_NOT_CONNECTED;
421
422 /*
423 * Write it.
424 */
425 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
426 int rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
427 if ( RT_FAILURE(rc)
428 && rc != VERR_INTERRUPTED)
429 {
430 /* assume fatal connection error. */
431 Log(("RTTcpWrite -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
432 txsTcpDisconnectClient();
433 }
434
435 return rc;
436}
437
438/**
439 * @interface_method_impl{TXSTRANSPORT,pfnRecvPkt}
440 */
441static DECLCALLBACK(int) txsTcpRecvPkt(PPTXSPKTHDR ppPktHdr)
442{
443 int rc = VINF_SUCCESS;
444 *ppPktHdr = NULL;
445
446 /*
447 * Do we have to wait for a client to connect?
448 */
449 RTSOCKET hTcpClient = g_hTcpClient;
450 if (hTcpClient == NIL_RTSOCKET)
451 {
452 rc = txsTcpConnect();
453 if (RT_FAILURE(rc))
454 return rc;
455 hTcpClient = g_hTcpClient; Assert(hTcpClient != NIL_RTSOCKET);
456 }
457
458 /*
459 * Read state.
460 */
461 size_t offData = 0;
462 size_t cbData = 0;
463 size_t cbDataAlloced;
464 uint8_t *pbData = NULL;
465
466 /*
467 * Any stashed data?
468 */
469 if (g_cbTcpStashedAlloced)
470 {
471 offData = g_cbTcpStashed;
472 cbDataAlloced = g_cbTcpStashedAlloced;
473 pbData = g_pbTcpStashed;
474
475 g_cbTcpStashed = 0;
476 g_cbTcpStashedAlloced = 0;
477 g_pbTcpStashed = NULL;
478 }
479 else
480 {
481 cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);
482 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
483 if (!pbData)
484 return VERR_NO_MEMORY;
485 }
486
487 /*
488 * Read and valid the length.
489 */
490 while (offData < sizeof(uint32_t))
491 {
492 size_t cbRead;
493 rc = RTTcpRead(hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
494 if (RT_FAILURE(rc))
495 break;
496 if (cbRead == 0)
497 {
498 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
499 rc = VERR_NET_NOT_CONNECTED;
500 break;
501 }
502 offData += cbRead;
503 }
504 if (RT_SUCCESS(rc))
505 {
506 ASMCompilerBarrier(); /* paranoia^3 */
507 cbData = *(uint32_t volatile *)pbData;
508 if (cbData >= sizeof(TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)
509 {
510 /*
511 * Align the length and reallocate the return packet it necessary.
512 */
513 cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);
514 if (cbData > cbDataAlloced)
515 {
516 void *pvNew = RTMemRealloc(pbData, cbData);
517 if (pvNew)
518 {
519 pbData = (uint8_t *)pvNew;
520 cbDataAlloced = cbData;
521 }
522 else
523 rc = VERR_NO_MEMORY;
524 }
525 if (RT_SUCCESS(rc))
526 {
527 /*
528 * Read the remainder of the data.
529 */
530 while (offData < cbData)
531 {
532 size_t cbRead;
533 rc = RTTcpRead(hTcpClient, pbData + offData, cbData - offData, &cbRead);
534 if (RT_FAILURE(rc))
535 break;
536 if (cbRead == 0)
537 {
538 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
539 rc = VERR_NET_NOT_CONNECTED;
540 break;
541 }
542 offData += cbRead;
543 }
544 }
545 }
546 else
547 rc = VERR_NET_PROTOCOL_ERROR;
548 }
549 if (RT_SUCCESS(rc))
550 *ppPktHdr = (PTXSPKTHDR)pbData;
551 else
552 {
553 /*
554 * Deal with errors.
555 */
556 if (rc == VERR_INTERRUPTED)
557 {
558 /* stash it away for the next call. */
559 g_cbTcpStashed = cbData;
560 g_cbTcpStashedAlloced = cbDataAlloced;
561 g_pbTcpStashed = pbData;
562 }
563 else
564 {
565 RTMemFree(pbData);
566
567 /* assume fatal connection error. */
568 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
569 txsTcpDisconnectClient();
570 }
571 }
572
573 return rc;
574}
575
576/**
577 * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd}
578 */
579static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart)
580{
581 return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
582}
583
584/**
585 * @interface_method_impl{TXSTRANSPORT,pfnPollIn}
586 */
587static DECLCALLBACK(bool) txsTcpPollIn(void)
588{
589 RTSOCKET hTcpClient = g_hTcpClient;
590 if (hTcpClient == NIL_RTSOCKET)
591 return false;
592 int rc = RTTcpSelectOne(hTcpClient, 0/*cMillies*/);
593 return RT_SUCCESS(rc);
594}
595
596/**
597 * @interface_method_impl{TXSTRANSPORT,pfnTerm}
598 */
599static DECLCALLBACK(void) txsTcpTerm(void)
600{
601 /* Signal thread */
602 if (RTCritSectIsInitialized(&g_TcpCritSect))
603 {
604 RTCritSectEnter(&g_TcpCritSect);
605 g_fTcpStopConnecting = true;
606 RTCritSectLeave(&g_TcpCritSect);
607 }
608
609 if (g_hThreadTcpConnect != NIL_RTTHREAD)
610 {
611 RTThreadUserSignal(g_hThreadTcpConnect);
612 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
613 }
614
615 /* Shut down the server (will wake up thread). */
616 if (g_pTcpServer)
617 {
618 Log(("txsTcpTerm: Destroying server...\n"));
619 int rc = RTTcpServerDestroy(g_pTcpServer);
620 if (RT_FAILURE(rc))
621 RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc);
622 g_pTcpServer = NULL;
623 }
624
625 /* Shut down client */
626 if (g_hTcpClient != NIL_RTSOCKET)
627 {
628 if (g_fTcpClientFromServer)
629 {
630 Log(("txsTcpTerm: Disconnecting client...\n"));
631 int rc = RTTcpServerDisconnectClient2(g_hTcpClient);
632 if (RT_FAILURE(rc))
633 RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
634 }
635 else
636 {
637 int rc = RTTcpClientClose(g_hTcpClient);
638 if (RT_FAILURE(rc))
639 RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
640 }
641 g_hTcpClient = NIL_RTSOCKET;
642 }
643
644 /* Clean up stashing. */
645 RTMemFree(g_pbTcpStashed);
646 g_pbTcpStashed = NULL;
647 g_cbTcpStashed = 0;
648 g_cbTcpStashedAlloced = 0;
649
650 /* Wait for the thread (they should've had some time to quit by now). */
651 txsTcpConnectWaitOnThreads(15000);
652
653 /* Finally, clean up the critical section. */
654 if (RTCritSectIsInitialized(&g_TcpCritSect))
655 RTCritSectDelete(&g_TcpCritSect);
656
657 Log(("txsTcpTerm: done\n"));
658}
659
660/**
661 * @interface_method_impl{TXSTRANSPORT,pfnInit}
662 */
663static DECLCALLBACK(int) txsTcpInit(void)
664{
665 int rc = RTCritSectInit(&g_TcpCritSect);
666 if (RT_SUCCESS(rc) && g_enmTcpMode != TXSTCPMODE_CLIENT)
667 {
668 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
669 if (RT_FAILURE(rc))
670 {
671 if (rc == VERR_NET_DOWN)
672 {
673 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
674 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
675 uint64_t StartMs = RTTimeMilliTS();
676 do
677 {
678 RTThreadSleep(1000);
679 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
680 } while ( rc == VERR_NET_DOWN
681 && RTTimeMilliTS() - StartMs < 20000);
682 if (RT_SUCCESS(rc))
683 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
684 }
685 if (RT_FAILURE(rc))
686 {
687 g_pTcpServer = NULL;
688 RTCritSectDelete(&g_TcpCritSect);
689 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
690 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
691 }
692 }
693 }
694
695 return rc;
696}
697
698/** Options */
699enum TXSTCPOPT
700{
701 TXSTCPOPT_MODE = 1000,
702 TXSTCPOPT_BIND_ADDRESS,
703 TXSTCPOPT_BIND_PORT,
704 TXSTCPOPT_CONNECT_ADDRESS,
705 TXSTCPOPT_CONNECT_PORT,
706
707 /* legacy: */
708 TXSTCPOPT_LEGACY_PORT,
709 TXSTCPOPT_LEGACY_CONNECT
710};
711
712/**
713 * @interface_method_impl{TXSTRANSPORT,pfnOption}
714 */
715static DECLCALLBACK(int) txsTcpOption(int ch, PCRTGETOPTUNION pVal)
716{
717 int rc;
718
719 switch (ch)
720 {
721 case TXSTCPOPT_MODE:
722 if (!strcmp(pVal->psz, "both"))
723 g_enmTcpMode = TXSTCPMODE_BOTH;
724 else if (!strcmp(pVal->psz, "client"))
725 g_enmTcpMode = TXSTCPMODE_CLIENT;
726 else if (!strcmp(pVal->psz, "server"))
727 g_enmTcpMode = TXSTCPMODE_SERVER;
728 else
729 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid TCP mode: '%s'\n", pVal->psz);
730 return VINF_SUCCESS;
731
732 case TXSTCPOPT_BIND_ADDRESS:
733 rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
734 if (RT_FAILURE(rc))
735 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
736 return VINF_SUCCESS;
737
738 case TXSTCPOPT_BIND_PORT:
739 g_uTcpBindPort = pVal->u16 == 0 ? TXS_TCP_DEF_BIND_PORT : pVal->u16;
740 return VINF_SUCCESS;
741
742 case TXSTCPOPT_LEGACY_CONNECT:
743 g_enmTcpMode = TXSTCPMODE_CLIENT;
744 /* fall thru */
745 case TXSTCPOPT_CONNECT_ADDRESS:
746 rc = RTStrCopy(g_szTcpConnectAddr, sizeof(g_szTcpConnectAddr), pVal->psz);
747 if (RT_FAILURE(rc))
748 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
749 if (!g_szTcpConnectAddr[0])
750 strcpy(g_szTcpConnectAddr, TXS_TCP_DEF_CONNECT_ADDRESS);
751 return VINF_SUCCESS;
752
753 case TXSTCPOPT_CONNECT_PORT:
754 g_uTcpConnectPort = pVal->u16 == 0 ? TXS_TCP_DEF_CONNECT_PORT : pVal->u16;
755 return VINF_SUCCESS;
756
757 case TXSTCPOPT_LEGACY_PORT:
758 if (pVal->u16 == 0)
759 {
760 g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;
761 g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;
762 }
763 else
764 {
765 g_uTcpBindPort = pVal->u16;
766 g_uTcpConnectPort = pVal->u16;
767 }
768 return VINF_SUCCESS;
769 }
770 return VERR_TRY_AGAIN;
771}
772
773/**
774 * @interface_method_impl{TXSTRANSPORT,pfnUsage}
775 */
776DECLCALLBACK(void) txsTcpUsage(PRTSTREAM pStream)
777{
778 RTStrmPrintf(pStream,
779 " --tcp-mode <both|client|server>\n"
780 " Selects the mode of operation.\n"
781 " Default: both\n"
782 " --tcp-bind-address <address>\n"
783 " The address(es) to listen to TCP connection on. Empty string\n"
784 " means any address, this is the default.\n"
785 " --tcp-bind-port <port>\n"
786 " The port to listen to TCP connections on.\n"
787 " Default: %u\n"
788 " --tcp-connect-address <address>\n"
789 " The address of the server to try connect to in client mode.\n"
790 " Default: " TXS_TCP_DEF_CONNECT_ADDRESS "\n"
791 " --tcp-connect-port <port>\n"
792 " The port on the server to connect to in client mode.\n"
793 " Default: %u\n"
794 , TXS_TCP_DEF_BIND_PORT, TXS_TCP_DEF_CONNECT_PORT);
795}
796
797/** Command line options for the TCP/IP transport layer. */
798static const RTGETOPTDEF g_TcpOpts[] =
799{
800 { "--tcp-mode", TXSTCPOPT_MODE, RTGETOPT_REQ_STRING },
801 { "--tcp-bind-address", TXSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
802 { "--tcp-bind-port", TXSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
803 { "--tcp-connect-address", TXSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
804 { "--tcp-connect-port", TXSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 },
805
806 /* legacy */
807 { "--tcp-port", TXSTCPOPT_LEGACY_PORT, RTGETOPT_REQ_UINT16 },
808 { "--tcp-connect", TXSTCPOPT_LEGACY_CONNECT, RTGETOPT_REQ_STRING },
809};
810
811/** TCP/IP transport layer. */
812const TXSTRANSPORT g_TcpTransport =
813{
814 /* .szName = */ "tcp",
815 /* .pszDesc = */ "TCP/IP",
816 /* .cOpts = */ &g_TcpOpts[0],
817 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
818 /* .pfnUsage = */ txsTcpUsage,
819 /* .pfnOption = */ txsTcpOption,
820 /* .pfnInit = */ txsTcpInit,
821 /* .pfnTerm = */ txsTcpTerm,
822 /* .pfnPollIn = */ txsTcpPollIn,
823 /* .pfnPollSetAdd = */ txsTcpPollSetAdd,
824 /* .pfnRecvPkt = */ txsTcpRecvPkt,
825 /* .pfnSendPkt = */ txsTcpSendPkt,
826 /* .pfnBabble = */ txsTcpBabble,
827 /* .pfnNotifyHowdy = */ txsTcpNotifyHowdy,
828 /* .pfnNotifyBye = */ txsTcpNotifyBye,
829 /* .pfnNotifyReboot = */ txsTcpNotifyReboot,
830 /* .u32EndMarker = */ UINT32_C(0x12345678)
831};
832
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette