VirtualBox

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

Last change on this file since 99775 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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