VirtualBox

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

Last change on this file since 106212 was 106061, checked in by vboxsync, 4 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.6 KB
Line 
1/* $Id: TestExecServiceTcp.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * TestExecServ - Basic Remote Execution Service, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2010-2024 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 uint32_t cSecsIdle = 75; /* idle time in seconds before first keep-alive probe */
224 uint32_t cSecsInterval = 30; /* interval in seconds between keep-alive probes */
225 uint32_t cFailedPktsBeforeClose = 4; /* number of unacknowledged keep-alive probes before closing connection */
226 rc = RTTcpSetKeepAlive(hTcpClient, true /* fEnable */, cSecsIdle, cSecsInterval, cFailedPktsBeforeClose);
227 if (RT_FAILURE(rc))
228 RTMsgInfo("Failed to set SO_KEEPALIVE on client socket hTcpClient: rc=%Rrc\n", rc);
229 hTcpClient = txsTcpSetClient(hTcpClient);
230 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
231 break;
232 }
233
234 if (txsTcpIsFatalClientConnectStatus(rc))
235 return rc;
236
237 /* Delay a wee bit before retrying. */
238 RTThreadUserWait(hSelf, 1536);
239 }
240 return VINF_SUCCESS;
241}
242
243/**
244 * Wait on the threads to complete.
245 *
246 * @returns Thread status (if collected), otherwise VINF_SUCCESS.
247 * @param cMillies The period to wait on each thread.
248 */
249static int txsTcpConnectWaitOnThreads(RTMSINTERVAL cMillies)
250{
251 int rcRet = VINF_SUCCESS;
252
253 if (g_hThreadTcpConnect != NIL_RTTHREAD)
254 {
255 int rcThread;
256 int rc2 = RTThreadWait(g_hThreadTcpConnect, cMillies, &rcThread);
257 if (RT_SUCCESS(rc2))
258 {
259 g_hThreadTcpConnect = NIL_RTTHREAD;
260 rcRet = rcThread;
261 }
262 }
263
264 if (g_hThreadTcpServer != NIL_RTTHREAD)
265 {
266 int rcThread;
267 int rc2 = RTThreadWait(g_hThreadTcpServer, cMillies, &rcThread);
268 if (RT_SUCCESS(rc2))
269 {
270 g_hThreadTcpServer = NIL_RTTHREAD;
271 if (RT_SUCCESS(rc2))
272 rcRet = rcThread;
273 }
274 }
275 return rcRet;
276}
277
278/**
279 * Connects to the peer.
280 *
281 * @returns VBox status code. Updates g_hTcpClient and g_fTcpClientFromServer on
282 * success
283 */
284static int txsTcpConnect(void)
285{
286 int rc;
287 if (g_enmTcpMode == TXSTCPMODE_SERVER)
288 {
289 g_fTcpClientFromServer = true;
290 rc = RTTcpServerListen2(g_pTcpServer, &g_hTcpClient);
291 Log(("txsTcpRecvPkt: RTTcpServerListen2 -> %Rrc\n", rc));
292 }
293 else if (g_enmTcpMode == TXSTCPMODE_CLIENT)
294 {
295 g_fTcpClientFromServer = false;
296 for (;;)
297 {
298 Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort));
299 rc = RTTcpClientConnect(g_szTcpConnectAddr, g_uTcpConnectPort, &g_hTcpClient);
300 Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc));
301 if (RT_SUCCESS(rc) || txsTcpIsFatalClientConnectStatus(rc))
302 break;
303
304 /* Delay a wee bit before retrying. */
305 RTThreadSleep(1536);
306 }
307 }
308 else
309 {
310 Assert(g_enmTcpMode == TXSTCPMODE_BOTH);
311 RTTHREAD hSelf = RTThreadSelf();
312
313 /*
314 * Create client threads.
315 */
316 RTCritSectEnter(&g_TcpCritSect);
317 RTThreadUserReset(hSelf);
318 g_hThreadMain = hSelf;
319 g_fTcpStopConnecting = false;
320 RTCritSectLeave(&g_TcpCritSect);
321
322 txsTcpConnectWaitOnThreads(32);
323
324 rc = VINF_SUCCESS;
325 if (g_hThreadTcpConnect == NIL_RTTHREAD)
326 {
327 g_pTcpConnectCancelCookie = NULL;
328 rc = RTThreadCreate(&g_hThreadTcpConnect, txsTcpClientConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
329 RTTHREADFLAGS_WAITABLE, "tcpconn");
330 }
331 if (g_hThreadTcpServer == NIL_RTTHREAD && RT_SUCCESS(rc))
332 rc = RTThreadCreate(&g_hThreadTcpServer, txsTcpServerConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
333 RTTHREADFLAGS_WAITABLE, "tcpserv");
334
335 RTCritSectEnter(&g_TcpCritSect);
336
337 /*
338 * Wait for connection to be established.
339 */
340 while ( RT_SUCCESS(rc)
341 && g_hTcpClient == NIL_RTSOCKET)
342 {
343 RTCritSectLeave(&g_TcpCritSect);
344 RTThreadUserWait(hSelf, 1536);
345 rc = txsTcpConnectWaitOnThreads(0);
346 RTCritSectEnter(&g_TcpCritSect);
347 }
348
349 /*
350 * Cancel the threads.
351 */
352 g_hThreadMain = NIL_RTTHREAD;
353 g_fTcpStopConnecting = true;
354
355 RTCritSectLeave(&g_TcpCritSect);
356 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
357 }
358
359 AssertMsg(RT_SUCCESS(rc) ? g_hTcpClient != NIL_RTSOCKET : g_hTcpClient == NIL_RTSOCKET, ("%Rrc %p\n", rc, g_hTcpClient));
360 g_cbTcpStashed = 0;
361 return rc;
362}
363
364/**
365 * @interface_method_impl{TXSTRANSPORT,pfnNotifyReboot}
366 */
367static DECLCALLBACK(void) txsTcpNotifyReboot(void)
368{
369 Log(("txsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
370 if (g_pTcpServer)
371 {
372 int rc = RTTcpServerDestroy(g_pTcpServer);
373 if (RT_FAILURE(rc))
374 RTMsgInfo("RTTcpServerDestroy failed in txsTcpNotifyReboot: %Rrc", rc);
375 g_pTcpServer = NULL;
376 }
377}
378
379/**
380 * @interface_method_impl{TXSTRANSPORT,pfnNotifyBye}
381 */
382static DECLCALLBACK(void) txsTcpNotifyBye(void)
383{
384 Log(("txsTcpNotifyBye: txsTcpDisconnectClient %RTsock\n", g_hTcpClient));
385 txsTcpDisconnectClient();
386}
387
388/**
389 * @interface_method_impl{TXSTRANSPORT,pfnNotifyHowdy}
390 */
391static DECLCALLBACK(void) txsTcpNotifyHowdy(void)
392{
393 /* nothing to do here */
394}
395
396/**
397 * @interface_method_impl{TXSTRANSPORT,pfnBabble}
398 */
399static DECLCALLBACK(void) txsTcpBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
400{
401 /*
402 * Quietly ignore already disconnected client.
403 */
404 RTSOCKET hTcpClient = g_hTcpClient;
405 if (hTcpClient == NIL_RTSOCKET)
406 return;
407
408 /*
409 * Try send the babble reply.
410 */
411 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
412 int rc;
413 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
414 do rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
415 while (rc == VERR_INTERRUPTED);
416
417 /*
418 * Disconnect the client.
419 */
420 Log(("txsTcpBabble: txsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", g_hTcpClient, rc));
421 txsTcpDisconnectClient();
422}
423
424/**
425 * @interface_method_impl{TXSTRANSPORT,pfnSendPkt}
426 */
427static DECLCALLBACK(int) txsTcpSendPkt(PCTXSPKTHDR pPktHdr)
428{
429 Assert(pPktHdr->cb >= sizeof(TXSPKTHDR));
430
431 /*
432 * Fail if no client connection.
433 */
434 RTSOCKET hTcpClient = g_hTcpClient;
435 if (hTcpClient == NIL_RTSOCKET)
436 return VERR_NET_NOT_CONNECTED;
437
438 /*
439 * Write it.
440 */
441 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
442 int rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
443 if ( RT_FAILURE(rc)
444 && rc != VERR_INTERRUPTED)
445 {
446 /* assume fatal connection error. */
447 Log(("RTTcpWrite -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
448 txsTcpDisconnectClient();
449 }
450
451 return rc;
452}
453
454/**
455 * @interface_method_impl{TXSTRANSPORT,pfnRecvPkt}
456 */
457static DECLCALLBACK(int) txsTcpRecvPkt(PPTXSPKTHDR ppPktHdr)
458{
459 int rc = VINF_SUCCESS;
460 *ppPktHdr = NULL;
461
462 /*
463 * Do we have to wait for a client to connect?
464 */
465 RTSOCKET hTcpClient = g_hTcpClient;
466 if (hTcpClient == NIL_RTSOCKET)
467 {
468 rc = txsTcpConnect();
469 if (RT_FAILURE(rc))
470 return rc;
471 hTcpClient = g_hTcpClient; Assert(hTcpClient != NIL_RTSOCKET);
472 }
473
474 /*
475 * Read state.
476 */
477 size_t offData = 0;
478 size_t cbData = 0;
479 size_t cbDataAlloced;
480 uint8_t *pbData = NULL;
481
482 /*
483 * Any stashed data?
484 */
485 if (g_cbTcpStashedAlloced)
486 {
487 offData = g_cbTcpStashed;
488 cbDataAlloced = g_cbTcpStashedAlloced;
489 pbData = g_pbTcpStashed;
490
491 g_cbTcpStashed = 0;
492 g_cbTcpStashedAlloced = 0;
493 g_pbTcpStashed = NULL;
494 }
495 else
496 {
497 cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);
498 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
499 if (!pbData)
500 return VERR_NO_MEMORY;
501 }
502
503 /*
504 * Read and valid the length.
505 */
506 while (offData < sizeof(uint32_t))
507 {
508 size_t cbRead;
509 rc = RTTcpRead(hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
510 if (RT_FAILURE(rc))
511 break;
512 if (cbRead == 0)
513 {
514 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
515 rc = VERR_NET_NOT_CONNECTED;
516 break;
517 }
518 offData += cbRead;
519 }
520 if (RT_SUCCESS(rc))
521 {
522 ASMCompilerBarrier(); /* paranoia^3 */
523 cbData = *(uint32_t volatile *)pbData;
524 if (cbData >= sizeof(TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)
525 {
526 /*
527 * Align the length and reallocate the return packet it necessary.
528 */
529 cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);
530 if (cbData > cbDataAlloced)
531 {
532 void *pvNew = RTMemRealloc(pbData, cbData);
533 if (pvNew)
534 {
535 pbData = (uint8_t *)pvNew;
536 cbDataAlloced = cbData;
537 }
538 else
539 rc = VERR_NO_MEMORY;
540 }
541 if (RT_SUCCESS(rc))
542 {
543 /*
544 * Read the remainder of the data.
545 */
546 while (offData < cbData)
547 {
548 size_t cbRead;
549 rc = RTTcpRead(hTcpClient, pbData + offData, cbData - offData, &cbRead);
550 if (RT_FAILURE(rc))
551 break;
552 if (cbRead == 0)
553 {
554 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
555 rc = VERR_NET_NOT_CONNECTED;
556 break;
557 }
558 offData += cbRead;
559 }
560 }
561 }
562 else
563 rc = VERR_NET_PROTOCOL_ERROR;
564 }
565 if (RT_SUCCESS(rc))
566 *ppPktHdr = (PTXSPKTHDR)pbData;
567 else
568 {
569 /*
570 * Deal with errors.
571 */
572 if (rc == VERR_INTERRUPTED)
573 {
574 /* stash it away for the next call. */
575 g_cbTcpStashed = cbData;
576 g_cbTcpStashedAlloced = cbDataAlloced;
577 g_pbTcpStashed = pbData;
578 }
579 else
580 {
581 RTMemFree(pbData);
582
583 /* assume fatal connection error. */
584 Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
585 txsTcpDisconnectClient();
586 }
587 }
588
589 return rc;
590}
591
592/**
593 * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd}
594 */
595static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart)
596{
597 return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
598}
599
600/**
601 * @interface_method_impl{TXSTRANSPORT,pfnPollIn}
602 */
603static DECLCALLBACK(bool) txsTcpPollIn(void)
604{
605 RTSOCKET hTcpClient = g_hTcpClient;
606 if (hTcpClient == NIL_RTSOCKET)
607 return false;
608 int rc = RTTcpSelectOne(hTcpClient, 0/*cMillies*/);
609 return RT_SUCCESS(rc);
610}
611
612/**
613 * @interface_method_impl{TXSTRANSPORT,pfnTerm}
614 */
615static DECLCALLBACK(void) txsTcpTerm(void)
616{
617 /* Signal thread */
618 if (RTCritSectIsInitialized(&g_TcpCritSect))
619 {
620 RTCritSectEnter(&g_TcpCritSect);
621 g_fTcpStopConnecting = true;
622 RTCritSectLeave(&g_TcpCritSect);
623 }
624
625 if (g_hThreadTcpConnect != NIL_RTTHREAD)
626 {
627 RTThreadUserSignal(g_hThreadTcpConnect);
628 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
629 }
630
631 /* Shut down the server (will wake up thread). */
632 if (g_pTcpServer)
633 {
634 Log(("txsTcpTerm: Destroying server...\n"));
635 int rc = RTTcpServerDestroy(g_pTcpServer);
636 if (RT_FAILURE(rc))
637 RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc);
638 g_pTcpServer = NULL;
639 }
640
641 /* Shut down client */
642 if (g_hTcpClient != NIL_RTSOCKET)
643 {
644 if (g_fTcpClientFromServer)
645 {
646 Log(("txsTcpTerm: Disconnecting client...\n"));
647 int rc = RTTcpServerDisconnectClient2(g_hTcpClient);
648 if (RT_FAILURE(rc))
649 RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
650 }
651 else
652 {
653 int rc = RTTcpClientClose(g_hTcpClient);
654 if (RT_FAILURE(rc))
655 RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
656 }
657 g_hTcpClient = NIL_RTSOCKET;
658 }
659
660 /* Clean up stashing. */
661 RTMemFree(g_pbTcpStashed);
662 g_pbTcpStashed = NULL;
663 g_cbTcpStashed = 0;
664 g_cbTcpStashedAlloced = 0;
665
666 /* Wait for the thread (they should've had some time to quit by now). */
667 txsTcpConnectWaitOnThreads(15000);
668
669 /* Finally, clean up the critical section. */
670 if (RTCritSectIsInitialized(&g_TcpCritSect))
671 RTCritSectDelete(&g_TcpCritSect);
672
673 Log(("txsTcpTerm: done\n"));
674}
675
676/**
677 * @interface_method_impl{TXSTRANSPORT,pfnInit}
678 */
679static DECLCALLBACK(int) txsTcpInit(void)
680{
681 int rc = RTCritSectInit(&g_TcpCritSect);
682 if (RT_SUCCESS(rc) && g_enmTcpMode != TXSTCPMODE_CLIENT)
683 {
684 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
685 if (RT_FAILURE(rc))
686 {
687 if (rc == VERR_NET_DOWN)
688 {
689 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
690 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
691 uint64_t StartMs = RTTimeMilliTS();
692 do
693 {
694 RTThreadSleep(1000);
695 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
696 } while ( rc == VERR_NET_DOWN
697 && RTTimeMilliTS() - StartMs < 20000);
698 if (RT_SUCCESS(rc))
699 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
700 }
701 if (RT_FAILURE(rc))
702 {
703 g_pTcpServer = NULL;
704 RTCritSectDelete(&g_TcpCritSect);
705 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
706 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
707 }
708 }
709 }
710
711 return rc;
712}
713
714/** Options */
715enum TXSTCPOPT
716{
717 TXSTCPOPT_MODE = 1000,
718 TXSTCPOPT_BIND_ADDRESS,
719 TXSTCPOPT_BIND_PORT,
720 TXSTCPOPT_CONNECT_ADDRESS,
721 TXSTCPOPT_CONNECT_PORT,
722
723 /* legacy: */
724 TXSTCPOPT_LEGACY_PORT,
725 TXSTCPOPT_LEGACY_CONNECT
726};
727
728/**
729 * @interface_method_impl{TXSTRANSPORT,pfnOption}
730 */
731static DECLCALLBACK(int) txsTcpOption(int ch, PCRTGETOPTUNION pVal)
732{
733 int rc;
734
735 switch (ch)
736 {
737 case TXSTCPOPT_MODE:
738 if (!strcmp(pVal->psz, "both"))
739 g_enmTcpMode = TXSTCPMODE_BOTH;
740 else if (!strcmp(pVal->psz, "client"))
741 g_enmTcpMode = TXSTCPMODE_CLIENT;
742 else if (!strcmp(pVal->psz, "server"))
743 g_enmTcpMode = TXSTCPMODE_SERVER;
744 else
745 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid TCP mode: '%s'\n", pVal->psz);
746 return VINF_SUCCESS;
747
748 case TXSTCPOPT_BIND_ADDRESS:
749 rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
750 if (RT_FAILURE(rc))
751 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
752 return VINF_SUCCESS;
753
754 case TXSTCPOPT_BIND_PORT:
755 g_uTcpBindPort = pVal->u16 == 0 ? TXS_TCP_DEF_BIND_PORT : pVal->u16;
756 return VINF_SUCCESS;
757
758 case TXSTCPOPT_LEGACY_CONNECT:
759 g_enmTcpMode = TXSTCPMODE_CLIENT;
760 RT_FALL_THRU();
761 case TXSTCPOPT_CONNECT_ADDRESS:
762 rc = RTStrCopy(g_szTcpConnectAddr, sizeof(g_szTcpConnectAddr), pVal->psz);
763 if (RT_FAILURE(rc))
764 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
765 if (!g_szTcpConnectAddr[0])
766 strcpy(g_szTcpConnectAddr, TXS_TCP_DEF_CONNECT_ADDRESS);
767 return VINF_SUCCESS;
768
769 case TXSTCPOPT_CONNECT_PORT:
770 g_uTcpConnectPort = pVal->u16 == 0 ? TXS_TCP_DEF_CONNECT_PORT : pVal->u16;
771 return VINF_SUCCESS;
772
773 case TXSTCPOPT_LEGACY_PORT:
774 if (pVal->u16 == 0)
775 {
776 g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;
777 g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;
778 }
779 else
780 {
781 g_uTcpBindPort = pVal->u16;
782 g_uTcpConnectPort = pVal->u16;
783 }
784 return VINF_SUCCESS;
785 }
786 return VERR_TRY_AGAIN;
787}
788
789/**
790 * @interface_method_impl{TXSTRANSPORT,pfnUsage}
791 */
792static DECLCALLBACK(void) txsTcpUsage(PRTSTREAM pStream)
793{
794 RTStrmPrintf(pStream,
795 " --tcp-mode <both|client|server>\n"
796 " Selects the mode of operation.\n"
797 " Default: both\n"
798 " --tcp-bind-address <address>\n"
799 " The address(es) to listen to TCP connection on. Empty string\n"
800 " means any address, this is the default.\n"
801 " --tcp-bind-port <port>\n"
802 " The port to listen to TCP connections on.\n"
803 " Default: %u\n"
804 " --tcp-connect-address <address>\n"
805 " The address of the server to try connect to in client mode.\n"
806 " Default: " TXS_TCP_DEF_CONNECT_ADDRESS "\n"
807 " --tcp-connect-port <port>\n"
808 " The port on the server to connect to in client mode.\n"
809 " Default: %u\n"
810 , TXS_TCP_DEF_BIND_PORT, TXS_TCP_DEF_CONNECT_PORT);
811}
812
813/** Command line options for the TCP/IP transport layer. */
814static const RTGETOPTDEF g_TcpOpts[] =
815{
816 { "--tcp-mode", TXSTCPOPT_MODE, RTGETOPT_REQ_STRING },
817 { "--tcp-bind-address", TXSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
818 { "--tcp-bind-port", TXSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
819 { "--tcp-connect-address", TXSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
820 { "--tcp-connect-port", TXSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 },
821
822 /* legacy */
823 { "--tcp-port", TXSTCPOPT_LEGACY_PORT, RTGETOPT_REQ_UINT16 },
824 { "--tcp-connect", TXSTCPOPT_LEGACY_CONNECT, RTGETOPT_REQ_STRING },
825};
826
827/** TCP/IP transport layer. */
828const TXSTRANSPORT g_TcpTransport =
829{
830 /* .szName = */ "tcp",
831 /* .pszDesc = */ "TCP/IP",
832 /* .cOpts = */ &g_TcpOpts[0],
833 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
834 /* .pfnUsage = */ txsTcpUsage,
835 /* .pfnOption = */ txsTcpOption,
836 /* .pfnInit = */ txsTcpInit,
837 /* .pfnTerm = */ txsTcpTerm,
838 /* .pfnPollIn = */ txsTcpPollIn,
839 /* .pfnPollSetAdd = */ txsTcpPollSetAdd,
840 /* .pfnRecvPkt = */ txsTcpRecvPkt,
841 /* .pfnSendPkt = */ txsTcpSendPkt,
842 /* .pfnBabble = */ txsTcpBabble,
843 /* .pfnNotifyHowdy = */ txsTcpNotifyHowdy,
844 /* .pfnNotifyBye = */ txsTcpNotifyBye,
845 /* .pfnNotifyReboot = */ txsTcpNotifyReboot,
846 /* .u32EndMarker = */ UINT32_C(0x12345678)
847};
848
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