VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestServiceTcp.cpp

Last change on this file was 99775, checked in by vboxsync, 12 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: 31.7 KB
Line 
1/* $Id: AudioTestServiceTcp.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * AudioTestServiceTcp - Audio test execution server, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2021-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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_AUDIO_TEST
33#include <iprt/log.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/critsect.h>
38#include <iprt/err.h>
39#include <iprt/mem.h>
40#include <iprt/message.h>
41#include <iprt/poll.h>
42#include <iprt/string.h>
43#include <iprt/tcp.h>
44#include <iprt/thread.h>
45#include <iprt/time.h>
46
47#include <VBox/log.h>
48
49#include "AudioTestService.h"
50#include "AudioTestServiceInternal.h"
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/**
62 * TCP specific client data.
63 */
64typedef struct ATSTRANSPORTCLIENT
65{
66 /** Socket of the current client. */
67 RTSOCKET hTcpClient;
68 /** Indicates whether \a hTcpClient comes from the server or from a client
69 * connect (relevant when closing it). */
70 bool fFromServer;
71 /** The size of the stashed data. */
72 size_t cbTcpStashed;
73 /** The size of the stashed data allocation. */
74 size_t cbTcpStashedAlloced;
75 /** The stashed data. */
76 uint8_t *pbTcpStashed;
77} ATSTRANSPORTCLIENT;
78
79/**
80 * Structure for keeping Audio Test Service (ATS) transport instance-specific data.
81 */
82typedef struct ATSTRANSPORTINST
83{
84 /** Critical section for serializing access. */
85 RTCRITSECT CritSect;
86 /** Connection mode to use. */
87 ATSCONNMODE enmConnMode;
88 /** The addresses to bind to. Empty string means any. */
89 char szBindAddr[256];
90 /** The TCP port to listen to. */
91 uint32_t uBindPort;
92 /** The addresses to connect to if running in reversed (VM NATed) mode. */
93 char szConnectAddr[256];
94 /** The TCP port to connect to if running in reversed (VM NATed) mode. */
95 uint32_t uConnectPort;
96 /** Pointer to the TCP server instance. */
97 PRTTCPSERVER pTcpServer;
98 /** Thread calling RTTcpServerListen2. */
99 RTTHREAD hThreadServer;
100 /** Thread calling RTTcpClientConnect. */
101 RTTHREAD hThreadConnect;
102 /** The main thread handle (for signalling). */
103 RTTHREAD hThreadMain;
104 /** Stop connecting attempts when set. */
105 bool fStopConnecting;
106 /** Connect cancel cookie. */
107 PRTTCPCLIENTCONNECTCANCEL volatile pConnectCancelCookie;
108} ATSTRANSPORTINST;
109/** Pointer to an Audio Test Service (ATS) TCP/IP transport instance. */
110typedef ATSTRANSPORTINST *PATSTRANSPORTINST;
111
112/**
113 * Structure holding an ATS connection context, which is
114 * required when connecting a client via server (listening) or client (connecting).
115 */
116typedef struct ATSCONNCTX
117{
118 /** Pointer to transport instance to use. */
119 PATSTRANSPORTINST pInst;
120 /** Pointer to transport client to connect. */
121 PATSTRANSPORTCLIENT pClient;
122 /** Connection timeout (in ms).
123 * Use RT_INDEFINITE_WAIT to wait indefinitely. */
124 uint32_t msTimeout;
125} ATSCONNCTX;
126/** Pointer to an Audio Test Service (ATS) TCP/IP connection context. */
127typedef ATSCONNCTX *PATSCONNCTX;
128
129
130/*********************************************************************************************************************************
131* Global Variables *
132*********************************************************************************************************************************/
133
134/**
135 * Disconnects the current client and frees all stashed data.
136 *
137 * @param pThis Transport instance.
138 * @param pClient Client to disconnect.
139 */
140static void atsTcpDisconnectClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
141{
142 RT_NOREF(pThis);
143
144 LogRelFlowFunc(("pClient=%RTsock\n", pClient->hTcpClient));
145
146 if (pClient->hTcpClient != NIL_RTSOCKET)
147 {
148 LogRelFlowFunc(("%RTsock\n", pClient->hTcpClient));
149
150 int rc;
151 if (pClient->fFromServer)
152 rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
153 else
154 rc = RTTcpClientClose(pClient->hTcpClient);
155 pClient->hTcpClient = NIL_RTSOCKET;
156 AssertRCSuccess(rc);
157 }
158
159 if (pClient->pbTcpStashed)
160 {
161 RTMemFree(pClient->pbTcpStashed);
162 pClient->pbTcpStashed = NULL;
163 }
164}
165
166/**
167 * Free's a client.
168 *
169 * @param pThis Transport instance.
170 * @param pClient Client to free.
171 * The pointer will be invalid after calling.
172 */
173static void atsTcpFreeClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
174{
175 if (!pClient)
176 return;
177
178 /* Make sure to disconnect first. */
179 atsTcpDisconnectClient(pThis, pClient);
180
181 RTMemFree(pClient);
182 pClient = NULL;
183}
184
185/**
186 * Sets the current client socket in a safe manner.
187 *
188 * @returns NIL_RTSOCKET if consumed, otherwise hTcpClient.
189 * @param pThis Transport instance.
190 * @param pClient Client to set the socket for.
191 * @param fFromServer Whether the socket is from a server (listening) or client (connecting) call.
192 * Important when closing / disconnecting.
193 * @param hTcpClient The client socket.
194 */
195static RTSOCKET atsTcpSetClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, bool fFromServer, RTSOCKET hTcpClient)
196{
197 RTCritSectEnter(&pThis->CritSect);
198 if ( pClient->hTcpClient == NIL_RTSOCKET
199 && !pThis->fStopConnecting)
200 {
201 LogRelFlowFunc(("New client %RTsock connected (fFromServer=%RTbool)\n", hTcpClient, fFromServer));
202
203 pClient->fFromServer = fFromServer;
204 pClient->hTcpClient = hTcpClient;
205 hTcpClient = NIL_RTSOCKET; /* Invalidate, as pClient has now ownership. */
206 }
207 RTCritSectLeave(&pThis->CritSect);
208 return hTcpClient;
209}
210
211/**
212 * Checks if it's a fatal RTTcpClientConnect return code.
213 *
214 * @returns true / false.
215 * @param rc The IPRT status code.
216 */
217static bool atsTcpIsFatalClientConnectStatus(int rc)
218{
219 return rc != VERR_NET_UNREACHABLE
220 && rc != VERR_NET_HOST_DOWN
221 && rc != VERR_NET_HOST_UNREACHABLE
222 && rc != VERR_NET_CONNECTION_REFUSED
223 && rc != VERR_TIMEOUT
224 && rc != VERR_NET_CONNECTION_TIMED_OUT;
225}
226
227/**
228 * Server mode connection thread.
229 *
230 * @returns iprt status code.
231 * @param hSelf Thread handle. Ignored.
232 * @param pvUser Pointer to ATSTRANSPORTINST the thread is bound to.
233 */
234static DECLCALLBACK(int) atsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
235{
236 RT_NOREF(hSelf);
237
238 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
239 PATSTRANSPORTINST pThis = pConnCtx->pInst;
240 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
241
242 /** @todo Implement cancellation support for using pConnCtx->msTimeout. */
243
244 LogRelFlowFuncEnter();
245
246 RTSOCKET hTcpClient;
247 int rc = RTTcpServerListen2(pThis->pTcpServer, &hTcpClient);
248 if (RT_SUCCESS(rc))
249 {
250 hTcpClient = atsTcpSetClient(pThis, pClient, true /* fFromServer */, hTcpClient);
251 RTTcpServerDisconnectClient2(hTcpClient);
252 }
253
254 LogRelFlowFuncLeaveRC(rc);
255 return rc;
256}
257
258/**
259 * Client mode connection thread.
260 *
261 * @returns iprt status code.
262 * @param hSelf Thread handle. Use to sleep on. The main thread will
263 * signal it to speed up thread shutdown.
264 * @param pvUser Pointer to a connection context (PATSCONNCTX) the thread is bound to.
265 */
266static DECLCALLBACK(int) atsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser)
267{
268 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
269 PATSTRANSPORTINST pThis = pConnCtx->pInst;
270 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
271
272 uint64_t msStartTs = RTTimeMilliTS();
273
274 LogRelFlowFuncEnter();
275
276 for (;;)
277 {
278 /* Stop? */
279 RTCritSectEnter(&pThis->CritSect);
280 bool fStop = pThis->fStopConnecting;
281 RTCritSectLeave(&pThis->CritSect);
282 if (fStop)
283 return VINF_SUCCESS;
284
285 /* Try connect. */ /** @todo make cancelable! */
286 RTSOCKET hTcpClient;
287 int rc = RTTcpClientConnectEx(pThis->szConnectAddr, pThis->uConnectPort, &hTcpClient,
288 RT_SOCKETCONNECT_DEFAULT_WAIT, &pThis->pConnectCancelCookie);
289 if (RT_SUCCESS(rc))
290 {
291 hTcpClient = atsTcpSetClient(pThis, pClient, false /* fFromServer */, hTcpClient);
292 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
293 break;
294 }
295
296 if (atsTcpIsFatalClientConnectStatus(rc))
297 return rc;
298
299 if ( pConnCtx->msTimeout != RT_INDEFINITE_WAIT
300 && RTTimeMilliTS() - msStartTs >= pConnCtx->msTimeout)
301 {
302 LogRelFlowFunc(("Timed out (%RU32ms)\n", pConnCtx->msTimeout));
303 return VERR_TIMEOUT;
304 }
305
306 /* Delay a wee bit before retrying. */
307 RTThreadUserWait(hSelf, 1536);
308 }
309
310 LogRelFlowFuncLeave();
311 return VINF_SUCCESS;
312}
313
314/**
315 * Wait on the threads to complete.
316 *
317 * @returns Thread status (if collected), otherwise VINF_SUCCESS.
318 * @param pThis Transport instance.
319 * @param cMillies The period to wait on each thread.
320 */
321static int atsTcpConnectWaitOnThreads(PATSTRANSPORTINST pThis, RTMSINTERVAL cMillies)
322{
323 int rcRet = VINF_SUCCESS;
324
325 LogRelFlowFuncEnter();
326
327 if (pThis->hThreadConnect != NIL_RTTHREAD)
328 {
329 int rcThread;
330 int rc2 = RTThreadWait(pThis->hThreadConnect, cMillies, &rcThread);
331 if (RT_SUCCESS(rc2))
332 {
333 pThis->hThreadConnect = NIL_RTTHREAD;
334 rcRet = rcThread;
335 }
336 }
337
338 if (pThis->hThreadServer != NIL_RTTHREAD)
339 {
340 int rcThread;
341 int rc2 = RTThreadWait(pThis->hThreadServer, cMillies, &rcThread);
342 if (RT_SUCCESS(rc2))
343 {
344 pThis->hThreadServer = NIL_RTTHREAD;
345 if (RT_SUCCESS(rc2))
346 rcRet = rcThread;
347 }
348 }
349
350 LogRelFlowFuncLeaveRC(rcRet);
351 return rcRet;
352}
353
354/**
355 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
356 */
357static DECLCALLBACK(int) atsTcpWaitForConnect(PATSTRANSPORTINST pThis, RTMSINTERVAL msTimeout,
358 bool *pfFromServer, PPATSTRANSPORTCLIENT ppClientNew)
359{
360 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
361 AssertPtrReturn(pClient, VERR_NO_MEMORY);
362
363 int rc;
364
365 LogRelFlowFunc(("msTimeout=%RU32, enmConnMode=%#x\n", msTimeout, pThis->enmConnMode));
366
367 uint64_t msStartTs = RTTimeMilliTS();
368
369 if (pThis->enmConnMode == ATSCONNMODE_SERVER)
370 {
371 /** @todo Implement cancellation support for using \a msTimeout. */
372
373 pClient->fFromServer = true;
374 rc = RTTcpServerListen2(pThis->pTcpServer, &pClient->hTcpClient);
375 LogRelFlowFunc(("RTTcpServerListen2(%RTsock) -> %Rrc\n", pClient->hTcpClient, rc));
376 }
377 else if (pThis->enmConnMode == ATSCONNMODE_CLIENT)
378 {
379 pClient->fFromServer = false;
380 for (;;)
381 {
382 LogRelFlowFunc(("Calling RTTcpClientConnect(%s, %u,)...\n", pThis->szConnectAddr, pThis->uConnectPort));
383 rc = RTTcpClientConnect(pThis->szConnectAddr, pThis->uConnectPort, &pClient->hTcpClient);
384 LogRelFlowFunc(("RTTcpClientConnect(%RTsock) -> %Rrc\n", pClient->hTcpClient, rc));
385 if (RT_SUCCESS(rc) || atsTcpIsFatalClientConnectStatus(rc))
386 break;
387
388 if ( msTimeout != RT_INDEFINITE_WAIT
389 && RTTimeMilliTS() - msStartTs >= msTimeout)
390 {
391 rc = VERR_TIMEOUT;
392 break;
393 }
394
395 if (pThis->fStopConnecting)
396 {
397 rc = VINF_SUCCESS;
398 break;
399 }
400
401 /* Delay a wee bit before retrying. */
402 RTThreadSleep(1536);
403 }
404 }
405 else
406 {
407 Assert(pThis->enmConnMode == ATSCONNMODE_BOTH);
408
409 /*
410 * Create client threads.
411 */
412 RTCritSectEnter(&pThis->CritSect);
413
414 pThis->fStopConnecting = false;
415 RTCritSectLeave(&pThis->CritSect);
416
417 atsTcpConnectWaitOnThreads(pThis, 32 /* cMillies */);
418
419 ATSCONNCTX ConnCtx;
420 RT_ZERO(ConnCtx);
421 ConnCtx.pInst = pThis;
422 ConnCtx.pClient = pClient;
423 ConnCtx.msTimeout = msTimeout;
424
425 rc = VINF_SUCCESS;
426 if (pThis->hThreadConnect == NIL_RTTHREAD)
427 {
428 pThis->pConnectCancelCookie = NULL;
429 rc = RTThreadCreate(&pThis->hThreadConnect, atsTcpClientConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
430 RTTHREADFLAGS_WAITABLE, "tcpconn");
431 }
432 if (pThis->hThreadServer == NIL_RTTHREAD && RT_SUCCESS(rc))
433 rc = RTThreadCreate(&pThis->hThreadServer, atsTcpServerConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
434 RTTHREADFLAGS_WAITABLE, "tcpserv");
435
436 RTCritSectEnter(&pThis->CritSect);
437
438 /*
439 * Wait for connection to be established.
440 */
441 while ( RT_SUCCESS(rc)
442 && pClient->hTcpClient == NIL_RTSOCKET)
443 {
444 RTCritSectLeave(&pThis->CritSect);
445 rc = atsTcpConnectWaitOnThreads(pThis, 10 /* cMillies */);
446 RTCritSectEnter(&pThis->CritSect);
447 }
448
449 /*
450 * Cancel the threads.
451 */
452 pThis->fStopConnecting = true;
453
454 RTCritSectLeave(&pThis->CritSect);
455 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
456 }
457
458 if (RT_SUCCESS(rc))
459 {
460 if (pfFromServer)
461 *pfFromServer = pClient->fFromServer;
462 *ppClientNew = pClient;
463 }
464 else
465 {
466 if (pClient)
467 {
468 atsTcpFreeClient(pThis, pClient);
469 pClient = NULL;
470 }
471 }
472
473 if (RT_FAILURE(rc))
474 LogRelFunc(("Failed with %Rrc\n", rc));
475
476 return rc;
477}
478
479/**
480 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
481 */
482static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
483{
484 LogRelFlowFuncEnter();
485 if (pThis->pTcpServer)
486 {
487 int rc = RTTcpServerDestroy(pThis->pTcpServer);
488 if (RT_FAILURE(rc))
489 LogRelFunc(("RTTcpServerDestroy failed, rc=%Rrc", rc));
490 pThis->pTcpServer = NULL;
491 }
492 LogRelFlowFuncLeave();
493}
494
495/**
496 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
497 */
498static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
499{
500 LogRelFlowFunc(("pClient=%RTsock\n", pClient->hTcpClient));
501 atsTcpDisconnectClient(pThis, pClient);
502}
503
504/**
505 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
506 */
507static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
508{
509 LogRelFlowFunc(("pClient=%RTsock\n", pClient->hTcpClient));
510
511 /* nothing to do here */
512 RT_NOREF(pThis);
513}
514
515/**
516 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
517 */
518static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
519{
520 /*
521 * Try send the babble reply.
522 */
523 RT_NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
524 int rc;
525 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
526 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
527 while (rc == VERR_INTERRUPTED);
528
529 LogRelFlowFunc(("pClient=%RTsock, rc=%Rrc\n", pClient->hTcpClient, rc));
530
531 /*
532 * Disconnect the client.
533 */
534 atsTcpDisconnectClient(pThis, pClient);
535}
536
537/**
538 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
539 */
540static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
541{
542 AssertReturn(pPktHdr->cb >= sizeof(ATSPKTHDR), VERR_INVALID_PARAMETER);
543
544 /*
545 * Write it.
546 */
547 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
548
549 Log3Func(("%RU32 -> %zu\n", pPktHdr->cb, cbToSend));
550
551 LogRel4(("pClient=%RTsock\n", pClient->hTcpClient));
552 LogRel4(("Header:\n"
553 "%.*Rhxd\n", RT_MIN(sizeof(ATSPKTHDR), cbToSend), pPktHdr));
554
555 if (cbToSend > sizeof(ATSPKTHDR))
556 LogRel4(("Payload:\n"
557 "%.*Rhxd\n",
558 RT_MIN(64, cbToSend - sizeof(ATSPKTHDR)), (uint8_t *)pPktHdr + sizeof(ATSPKTHDR)));
559
560 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
561 if ( RT_FAILURE(rc)
562 && rc != VERR_INTERRUPTED)
563 {
564 /* assume fatal connection error. */
565 LogRelFunc(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
566 atsTcpDisconnectClient(pThis, pClient);
567 }
568
569 LogRel3(("atsTcpSendPkt: pClient=%RTsock, achOpcode=%.8s, cbSent=%zu -> %Rrc\n", pClient->hTcpClient, (const char *)pPktHdr->achOpcode, cbToSend, rc));
570 return rc;
571}
572
573/**
574 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
575 */
576static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
577{
578 int rc = VINF_SUCCESS;
579 *ppPktHdr = NULL;
580
581 LogRel4(("pClient=%RTsock (cbTcpStashed=%zu, cbTcpStashedAlloced=%zu)\n",
582 pClient->hTcpClient, pClient->cbTcpStashed, pClient->cbTcpStashedAlloced));
583
584 /*
585 * Read state.
586 */
587 size_t offData = 0;
588 size_t cbData = 0;
589 size_t cbDataAlloced;
590 uint8_t *pbData = NULL;
591
592 /*
593 * Any stashed data?
594 */
595 if (pClient->cbTcpStashedAlloced)
596 {
597 offData = pClient->cbTcpStashed;
598 cbDataAlloced = pClient->cbTcpStashedAlloced;
599 pbData = pClient->pbTcpStashed;
600
601 pClient->cbTcpStashed = 0;
602 pClient->cbTcpStashedAlloced = 0;
603 pClient->pbTcpStashed = NULL;
604 }
605 else
606 {
607 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
608 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
609 AssertPtrReturn(pbData, VERR_NO_MEMORY);
610 }
611
612 /*
613 * Read and validate the length.
614 */
615 while (offData < sizeof(uint32_t))
616 {
617 size_t cbRead;
618 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
619 if (RT_FAILURE(rc))
620 break;
621 if (cbRead == 0)
622 {
623 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
624 rc = VERR_NET_NOT_CONNECTED;
625 break;
626 }
627 offData += cbRead;
628 }
629 if (RT_SUCCESS(rc))
630 {
631 ASMCompilerBarrier(); /* paranoia^3 */
632 cbData = *(uint32_t volatile *)pbData;
633 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
634 {
635 /*
636 * Align the length and reallocate the return packet it necessary.
637 */
638 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
639 if (cbData > cbDataAlloced)
640 {
641 void *pvNew = RTMemRealloc(pbData, cbData);
642 if (pvNew)
643 {
644 pbData = (uint8_t *)pvNew;
645 cbDataAlloced = cbData;
646 }
647 else
648 rc = VERR_NO_MEMORY;
649 }
650 if (RT_SUCCESS(rc))
651 {
652 /*
653 * Read the remainder of the data.
654 */
655 while (offData < cbData)
656 {
657 size_t cbRead;
658 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
659 if (RT_FAILURE(rc))
660 break;
661 if (cbRead == 0)
662 {
663 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
664 rc = VERR_NET_NOT_CONNECTED;
665 break;
666 }
667
668 offData += cbRead;
669 }
670
671 LogRel4(("Header:\n"
672 "%.*Rhxd\n", sizeof(ATSPKTHDR), pbData));
673
674 if ( RT_SUCCESS(rc)
675 && cbData > sizeof(ATSPKTHDR))
676 LogRel4(("Payload:\n"
677 "%.*Rhxd\n", RT_MIN(64, cbData - sizeof(ATSPKTHDR)), (uint8_t *)pbData + sizeof(ATSPKTHDR)));
678 }
679 }
680 else
681 {
682 LogRelFunc(("Received invalid packet size (%zu)\n", cbData));
683 rc = VERR_NET_PROTOCOL_ERROR;
684 }
685 }
686 if (RT_SUCCESS(rc))
687 *ppPktHdr = (PATSPKTHDR)pbData;
688 else
689 {
690 /*
691 * Deal with errors.
692 */
693 if (rc == VERR_INTERRUPTED)
694 {
695 /* stash it away for the next call. */
696 pClient->cbTcpStashed = cbData;
697 pClient->cbTcpStashedAlloced = cbDataAlloced;
698 pClient->pbTcpStashed = pbData;
699 }
700 else
701 {
702 RTMemFree(pbData);
703
704 /* assume fatal connection error. */
705 LogRelFunc(("RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
706 atsTcpDisconnectClient(pThis, pClient);
707 }
708 }
709
710 PATSPKTHDR pPktHdr = (PATSPKTHDR)pbData;
711 LogRel3(("atsTcpRecvPkt: pClient=%RTsock, achOpcode=%.8s, cbRead=%zu -> %Rrc\n",
712 pClient->hTcpClient, pPktHdr ? (const char *)pPktHdr->achOpcode : "NONE ", cbData, rc));
713 return rc;
714}
715
716/**
717 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
718 */
719static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
720{
721 RT_NOREF(pThis);
722 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
723}
724
725/**
726 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
727 */
728static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
729{
730 RT_NOREF(pThis, pClient);
731 return RTPollSetRemove(hPollSet, idStart);
732}
733
734/**
735 * @interface_method_impl{ATSTRANSPORT,pfnDisconnect}
736 */
737static DECLCALLBACK(void) atsTcpDisconnect(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
738{
739 atsTcpFreeClient(pThis, pClient);
740}
741
742/**
743 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
744 */
745static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
746{
747 RT_NOREF(pThis);
748 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
749 return RT_SUCCESS(rc);
750}
751
752/**
753 * @interface_method_impl{ATSTRANSPORT,pfnStop}
754 */
755static DECLCALLBACK(void) atsTcpStop(PATSTRANSPORTINST pThis)
756{
757 LogRelFlowFuncEnter();
758
759 /* Signal thread */
760 if (RTCritSectIsInitialized(&pThis->CritSect))
761 {
762 RTCritSectEnter(&pThis->CritSect);
763 pThis->fStopConnecting = true;
764 RTCritSectLeave(&pThis->CritSect);
765 }
766
767 if (pThis->hThreadConnect != NIL_RTTHREAD)
768 {
769 RTThreadUserSignal(pThis->hThreadConnect);
770 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
771 }
772
773 /* Shut down the server (will wake up thread). */
774 if (pThis->pTcpServer)
775 {
776 LogRelFlowFunc(("Destroying server...\n"));
777 int rc = RTTcpServerDestroy(pThis->pTcpServer);
778 if (RT_FAILURE(rc))
779 LogRelFunc(("RTTcpServerDestroy failed with %Rrc", rc));
780 pThis->pTcpServer = NULL;
781 }
782
783 /* Wait for the thread (they should've had some time to quit by now). */
784 atsTcpConnectWaitOnThreads(pThis, 15000);
785
786 LogRelFlowFuncLeave();
787}
788
789/**
790 * @interface_method_impl{ATSTRANSPORT,pfnCreate}
791 */
792static DECLCALLBACK(int) atsTcpCreate(PATSTRANSPORTINST *ppThis)
793{
794 PATSTRANSPORTINST pThis = (PATSTRANSPORTINST)RTMemAllocZ(sizeof(ATSTRANSPORTINST));
795 AssertPtrReturn(pThis, VERR_NO_MEMORY);
796
797 int rc = RTCritSectInit(&pThis->CritSect);
798 if (RT_SUCCESS(rc))
799 {
800 *ppThis = pThis;
801 }
802
803 return rc;
804}
805
806/**
807 * @interface_method_impl{ATSTRANSPORT,pfnDestroy}
808 */
809static DECLCALLBACK(int) atsTcpDestroy(PATSTRANSPORTINST pThis)
810{
811 /* Stop things first. */
812 atsTcpStop(pThis);
813
814 /* Finally, clean up the critical section. */
815 if (RTCritSectIsInitialized(&pThis->CritSect))
816 RTCritSectDelete(&pThis->CritSect);
817
818 RTMemFree(pThis);
819 pThis = NULL;
820
821 return VINF_SUCCESS;
822}
823
824/**
825 * @interface_method_impl{ATSTRANSPORT,pfnStart}
826 */
827static DECLCALLBACK(int) atsTcpStart(PATSTRANSPORTINST pThis)
828{
829 int rc = VINF_SUCCESS;
830
831 if (pThis->enmConnMode != ATSCONNMODE_CLIENT)
832 {
833 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
834 if (RT_FAILURE(rc))
835 {
836 if (rc == VERR_NET_DOWN)
837 {
838 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
839 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
840 uint64_t StartMs = RTTimeMilliTS();
841 do
842 {
843 RTThreadSleep(1000);
844 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
845 } while ( rc == VERR_NET_DOWN
846 && RTTimeMilliTS() - StartMs < 20000);
847 if (RT_SUCCESS(rc))
848 LogRelFunc(("RTTcpServerCreateEx succceeded\n"));
849 }
850
851 if (RT_FAILURE(rc))
852 {
853 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
854 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
855 }
856 }
857 }
858
859 return rc;
860}
861
862/**
863 * @interface_method_impl{ATSTRANSPORT,pfnOption}
864 */
865static DECLCALLBACK(int) atsTcpOption(PATSTRANSPORTINST pThis, int ch, PCRTGETOPTUNION pVal)
866{
867 int rc;
868
869 switch (ch)
870 {
871 case ATSTCPOPT_CONN_MODE:
872 pThis->enmConnMode = (ATSCONNMODE)pVal->u32;
873 return VINF_SUCCESS;
874
875 case ATSTCPOPT_BIND_ADDRESS:
876 rc = RTStrCopy(pThis->szBindAddr, sizeof(pThis->szBindAddr), pVal->psz);
877 if (RT_FAILURE(rc))
878 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
879 if (!pThis->szBindAddr[0])
880 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP bind address specified: %s", pThis->szBindAddr);
881 return VINF_SUCCESS;
882
883 case ATSTCPOPT_BIND_PORT:
884 pThis->uBindPort = pVal->u16;
885 return VINF_SUCCESS;
886
887 case ATSTCPOPT_CONNECT_ADDRESS:
888 rc = RTStrCopy(pThis->szConnectAddr, sizeof(pThis->szConnectAddr), pVal->psz);
889 if (RT_FAILURE(rc))
890 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
891 if (!pThis->szConnectAddr[0])
892 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP connect address specified");
893 return VINF_SUCCESS;
894
895 case ATSTCPOPT_CONNECT_PORT:
896 pThis->uConnectPort = pVal->u16;
897 return VINF_SUCCESS;
898
899 default:
900 break;
901 }
902 return VERR_TRY_AGAIN;
903}
904
905/**
906 * @interface_method_impl{ATSTRANSPORT,pfnUsage}
907 */
908static DECLCALLBACK(void) atsTcpUsage(PRTSTREAM pStream)
909{
910 RTStrmPrintf(pStream,
911 " --tcp-conn-mode <0=both|1=client|2=server>\n"
912 " Selects the connection mode\n"
913 " Default: 0 (both)\n"
914 " --tcp-bind-addr[ess] <address>\n"
915 " The address(es) to listen to TCP connection on. Empty string\n"
916 " means any address, this is the default\n"
917 " --tcp-bind-port <port>\n"
918 " The port to listen to TCP connections on\n"
919 " Default: %u\n"
920 " --tcp-connect-addr[ess] <address>\n"
921 " The address of the server to try connect to in client mode\n"
922 " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR "\n"
923 " --tcp-connect-port <port>\n"
924 " The port on the server to connect to in client mode\n"
925 " Default: %u\n"
926 , ATS_TCP_DEF_BIND_PORT_GUEST, ATS_TCP_DEF_CONNECT_PORT_GUEST);
927}
928
929/** Command line options for the TCP/IP transport layer. */
930static const RTGETOPTDEF g_TcpOpts[] =
931{
932 { "--tcp-conn-mode", ATSTCPOPT_CONN_MODE, RTGETOPT_REQ_STRING },
933 { "--tcp-bind-addr", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
934 { "--tcp-bind-address", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
935 { "--tcp-bind-port", ATSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
936 { "--tcp-connect-addr", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
937 { "--tcp-connect-address", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
938 { "--tcp-connect-port", ATSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 }
939};
940
941/** TCP/IP transport layer. */
942const ATSTRANSPORT g_TcpTransport =
943{
944 /* .szName = */ "tcp",
945 /* .pszDesc = */ "TCP/IP",
946 /* .cOpts = */ &g_TcpOpts[0],
947 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
948 /* .pfnUsage = */ atsTcpUsage,
949 /* .pfnCreate = */ atsTcpCreate,
950 /* .pfnDestroy = */ atsTcpDestroy,
951 /* .pfnOption = */ atsTcpOption,
952 /* .pfnStart = */ atsTcpStart,
953 /* .pfnStop = */ atsTcpStop,
954 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
955 /* .pfnDisconnect = */ atsTcpDisconnect,
956 /* .pfnPollIn = */ atsTcpPollIn,
957 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
958 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
959 /* .pfnRecvPkt = */ atsTcpRecvPkt,
960 /* .pfnSendPkt = */ atsTcpSendPkt,
961 /* .pfnBabble = */ atsTcpBabble,
962 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
963 /* .pfnNotifyBye = */ atsTcpNotifyBye,
964 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
965 /* .u32EndMarker = */ UINT32_C(0x12345678)
966};
967
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use