VirtualBox

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

Last change on this file since 98477 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • 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 98103 2023-01-17 14:15:46Z 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 */
786DECLCALLBACK(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