VirtualBox

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

Last change on this file since 104838 was 104693, checked in by vboxsync, 6 months ago

Devices + GuestHost: Fixed warnings. ​​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 104693 2024-05-16 16:44:11Z 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 atsTcpFreeClient(pThis, pClient);
467 pClient = NULL;
468 }
469
470 if (RT_FAILURE(rc))
471 LogRelFunc(("Failed with %Rrc\n", rc));
472
473 return rc;
474}
475
476/**
477 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
478 */
479static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
480{
481 LogRelFlowFuncEnter();
482 if (pThis->pTcpServer)
483 {
484 int rc = RTTcpServerDestroy(pThis->pTcpServer);
485 if (RT_FAILURE(rc))
486 LogRelFunc(("RTTcpServerDestroy failed, rc=%Rrc", rc));
487 pThis->pTcpServer = NULL;
488 }
489 LogRelFlowFuncLeave();
490}
491
492/**
493 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
494 */
495static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
496{
497 LogRelFlowFunc(("pClient=%RTsock\n", pClient->hTcpClient));
498 atsTcpDisconnectClient(pThis, pClient);
499}
500
501/**
502 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
503 */
504static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
505{
506 LogRelFlowFunc(("pClient=%RTsock\n", pClient->hTcpClient));
507
508 /* nothing to do here */
509 RT_NOREF(pThis);
510}
511
512/**
513 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
514 */
515static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
516{
517 /*
518 * Try send the babble reply.
519 */
520 RT_NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
521 int rc;
522 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
523 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
524 while (rc == VERR_INTERRUPTED);
525
526 LogRelFlowFunc(("pClient=%RTsock, rc=%Rrc\n", pClient->hTcpClient, rc));
527
528 /*
529 * Disconnect the client.
530 */
531 atsTcpDisconnectClient(pThis, pClient);
532}
533
534/**
535 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
536 */
537static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
538{
539 AssertReturn(pPktHdr->cb >= sizeof(ATSPKTHDR), VERR_INVALID_PARAMETER);
540
541 /*
542 * Write it.
543 */
544 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
545
546 Log3Func(("%RU32 -> %zu\n", pPktHdr->cb, cbToSend));
547
548 LogRel4(("pClient=%RTsock\n", pClient->hTcpClient));
549 LogRel4(("Header:\n"
550 "%.*Rhxd\n", RT_MIN(sizeof(ATSPKTHDR), cbToSend), pPktHdr));
551
552 if (cbToSend > sizeof(ATSPKTHDR))
553 LogRel4(("Payload:\n"
554 "%.*Rhxd\n",
555 RT_MIN(64, cbToSend - sizeof(ATSPKTHDR)), (uint8_t *)pPktHdr + sizeof(ATSPKTHDR)));
556
557 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
558 if ( RT_FAILURE(rc)
559 && rc != VERR_INTERRUPTED)
560 {
561 /* assume fatal connection error. */
562 LogRelFunc(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
563 atsTcpDisconnectClient(pThis, pClient);
564 }
565
566 LogRel3(("atsTcpSendPkt: pClient=%RTsock, achOpcode=%.8s, cbSent=%zu -> %Rrc\n", pClient->hTcpClient, (const char *)pPktHdr->achOpcode, cbToSend, rc));
567 return rc;
568}
569
570/**
571 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
572 */
573static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
574{
575 int rc = VINF_SUCCESS;
576 *ppPktHdr = NULL;
577
578 LogRel4(("pClient=%RTsock (cbTcpStashed=%zu, cbTcpStashedAlloced=%zu)\n",
579 pClient->hTcpClient, pClient->cbTcpStashed, pClient->cbTcpStashedAlloced));
580
581 /*
582 * Read state.
583 */
584 size_t offData = 0;
585 size_t cbData = 0;
586 size_t cbDataAlloced;
587 uint8_t *pbData = NULL;
588
589 /*
590 * Any stashed data?
591 */
592 if (pClient->cbTcpStashedAlloced)
593 {
594 offData = pClient->cbTcpStashed;
595 cbDataAlloced = pClient->cbTcpStashedAlloced;
596 pbData = pClient->pbTcpStashed;
597
598 pClient->cbTcpStashed = 0;
599 pClient->cbTcpStashedAlloced = 0;
600 pClient->pbTcpStashed = NULL;
601 }
602 else
603 {
604 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
605 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
606 AssertPtrReturn(pbData, VERR_NO_MEMORY);
607 }
608
609 /*
610 * Read and validate the length.
611 */
612 while (offData < sizeof(uint32_t))
613 {
614 size_t cbRead;
615 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
616 if (RT_FAILURE(rc))
617 break;
618 if (cbRead == 0)
619 {
620 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
621 rc = VERR_NET_NOT_CONNECTED;
622 break;
623 }
624 offData += cbRead;
625 }
626 if (RT_SUCCESS(rc))
627 {
628 ASMCompilerBarrier(); /* paranoia^3 */
629 cbData = *(uint32_t volatile *)pbData;
630 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
631 {
632 /*
633 * Align the length and reallocate the return packet it necessary.
634 */
635 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
636 if (cbData > cbDataAlloced)
637 {
638 void *pvNew = RTMemRealloc(pbData, cbData);
639 if (pvNew)
640 {
641 pbData = (uint8_t *)pvNew;
642 cbDataAlloced = cbData;
643 }
644 else
645 rc = VERR_NO_MEMORY;
646 }
647 if (RT_SUCCESS(rc))
648 {
649 /*
650 * Read the remainder of the data.
651 */
652 while (offData < cbData)
653 {
654 size_t cbRead;
655 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
656 if (RT_FAILURE(rc))
657 break;
658 if (cbRead == 0)
659 {
660 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
661 rc = VERR_NET_NOT_CONNECTED;
662 break;
663 }
664
665 offData += cbRead;
666 }
667
668 LogRel4(("Header:\n"
669 "%.*Rhxd\n", sizeof(ATSPKTHDR), pbData));
670
671 if ( RT_SUCCESS(rc)
672 && cbData > sizeof(ATSPKTHDR))
673 LogRel4(("Payload:\n"
674 "%.*Rhxd\n", RT_MIN(64, cbData - sizeof(ATSPKTHDR)), (uint8_t *)pbData + sizeof(ATSPKTHDR)));
675 }
676 }
677 else
678 {
679 LogRelFunc(("Received invalid packet size (%zu)\n", cbData));
680 rc = VERR_NET_PROTOCOL_ERROR;
681 }
682 }
683 if (RT_SUCCESS(rc))
684 *ppPktHdr = (PATSPKTHDR)pbData;
685 else
686 {
687 /*
688 * Deal with errors.
689 */
690 if (rc == VERR_INTERRUPTED)
691 {
692 /* stash it away for the next call. */
693 pClient->cbTcpStashed = cbData;
694 pClient->cbTcpStashedAlloced = cbDataAlloced;
695 pClient->pbTcpStashed = pbData;
696 }
697 else
698 {
699 RTMemFree(pbData);
700
701 /* assume fatal connection error. */
702 LogRelFunc(("RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
703 atsTcpDisconnectClient(pThis, pClient);
704 }
705 }
706
707 PATSPKTHDR pPktHdr = (PATSPKTHDR)pbData;
708 LogRel3(("atsTcpRecvPkt: pClient=%RTsock, achOpcode=%.8s, cbRead=%zu -> %Rrc\n",
709 pClient->hTcpClient, pPktHdr ? (const char *)pPktHdr->achOpcode : "NONE ", cbData, rc));
710 return rc;
711}
712
713/**
714 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
715 */
716static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
717{
718 RT_NOREF(pThis);
719 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
720}
721
722/**
723 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
724 */
725static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
726{
727 RT_NOREF(pThis, pClient);
728 return RTPollSetRemove(hPollSet, idStart);
729}
730
731/**
732 * @interface_method_impl{ATSTRANSPORT,pfnDisconnect}
733 */
734static DECLCALLBACK(void) atsTcpDisconnect(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
735{
736 atsTcpFreeClient(pThis, pClient);
737}
738
739/**
740 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
741 */
742static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
743{
744 RT_NOREF(pThis);
745 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
746 return RT_SUCCESS(rc);
747}
748
749/**
750 * @interface_method_impl{ATSTRANSPORT,pfnStop}
751 */
752static DECLCALLBACK(void) atsTcpStop(PATSTRANSPORTINST pThis)
753{
754 LogRelFlowFuncEnter();
755
756 /* Signal thread */
757 if (RTCritSectIsInitialized(&pThis->CritSect))
758 {
759 RTCritSectEnter(&pThis->CritSect);
760 pThis->fStopConnecting = true;
761 RTCritSectLeave(&pThis->CritSect);
762 }
763
764 if (pThis->hThreadConnect != NIL_RTTHREAD)
765 {
766 RTThreadUserSignal(pThis->hThreadConnect);
767 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
768 }
769
770 /* Shut down the server (will wake up thread). */
771 if (pThis->pTcpServer)
772 {
773 LogRelFlowFunc(("Destroying server...\n"));
774 int rc = RTTcpServerDestroy(pThis->pTcpServer);
775 if (RT_FAILURE(rc))
776 LogRelFunc(("RTTcpServerDestroy failed with %Rrc", rc));
777 pThis->pTcpServer = NULL;
778 }
779
780 /* Wait for the thread (they should've had some time to quit by now). */
781 atsTcpConnectWaitOnThreads(pThis, 15000);
782
783 LogRelFlowFuncLeave();
784}
785
786/**
787 * @interface_method_impl{ATSTRANSPORT,pfnCreate}
788 */
789static DECLCALLBACK(int) atsTcpCreate(PATSTRANSPORTINST *ppThis)
790{
791 PATSTRANSPORTINST pThis = (PATSTRANSPORTINST)RTMemAllocZ(sizeof(ATSTRANSPORTINST));
792 AssertPtrReturn(pThis, VERR_NO_MEMORY);
793
794 int rc = RTCritSectInit(&pThis->CritSect);
795 if (RT_SUCCESS(rc))
796 {
797 *ppThis = pThis;
798 }
799
800 return rc;
801}
802
803/**
804 * @interface_method_impl{ATSTRANSPORT,pfnDestroy}
805 */
806static DECLCALLBACK(int) atsTcpDestroy(PATSTRANSPORTINST pThis)
807{
808 /* Stop things first. */
809 atsTcpStop(pThis);
810
811 /* Finally, clean up the critical section. */
812 if (RTCritSectIsInitialized(&pThis->CritSect))
813 RTCritSectDelete(&pThis->CritSect);
814
815 RTMemFree(pThis);
816 pThis = NULL;
817
818 return VINF_SUCCESS;
819}
820
821/**
822 * @interface_method_impl{ATSTRANSPORT,pfnStart}
823 */
824static DECLCALLBACK(int) atsTcpStart(PATSTRANSPORTINST pThis)
825{
826 int rc = VINF_SUCCESS;
827
828 if (pThis->enmConnMode != ATSCONNMODE_CLIENT)
829 {
830 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
831 if (RT_FAILURE(rc))
832 {
833 if (rc == VERR_NET_DOWN)
834 {
835 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
836 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
837 uint64_t StartMs = RTTimeMilliTS();
838 do
839 {
840 RTThreadSleep(1000);
841 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
842 } while ( rc == VERR_NET_DOWN
843 && RTTimeMilliTS() - StartMs < 20000);
844 if (RT_SUCCESS(rc))
845 LogRelFunc(("RTTcpServerCreateEx succceeded\n"));
846 }
847
848 if (RT_FAILURE(rc))
849 {
850 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
851 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
852 }
853 }
854 }
855
856 return rc;
857}
858
859/**
860 * @interface_method_impl{ATSTRANSPORT,pfnOption}
861 */
862static DECLCALLBACK(int) atsTcpOption(PATSTRANSPORTINST pThis, int ch, PCRTGETOPTUNION pVal)
863{
864 int rc;
865
866 switch (ch)
867 {
868 case ATSTCPOPT_CONN_MODE:
869 pThis->enmConnMode = (ATSCONNMODE)pVal->u32;
870 return VINF_SUCCESS;
871
872 case ATSTCPOPT_BIND_ADDRESS:
873 rc = RTStrCopy(pThis->szBindAddr, sizeof(pThis->szBindAddr), pVal->psz);
874 if (RT_FAILURE(rc))
875 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
876 if (!pThis->szBindAddr[0])
877 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP bind address specified: %s", pThis->szBindAddr);
878 return VINF_SUCCESS;
879
880 case ATSTCPOPT_BIND_PORT:
881 pThis->uBindPort = pVal->u16;
882 return VINF_SUCCESS;
883
884 case ATSTCPOPT_CONNECT_ADDRESS:
885 rc = RTStrCopy(pThis->szConnectAddr, sizeof(pThis->szConnectAddr), pVal->psz);
886 if (RT_FAILURE(rc))
887 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
888 if (!pThis->szConnectAddr[0])
889 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP connect address specified");
890 return VINF_SUCCESS;
891
892 case ATSTCPOPT_CONNECT_PORT:
893 pThis->uConnectPort = pVal->u16;
894 return VINF_SUCCESS;
895
896 default:
897 break;
898 }
899 return VERR_TRY_AGAIN;
900}
901
902/**
903 * @interface_method_impl{ATSTRANSPORT,pfnUsage}
904 */
905static DECLCALLBACK(void) atsTcpUsage(PRTSTREAM pStream)
906{
907 RTStrmPrintf(pStream,
908 " --tcp-conn-mode <0=both|1=client|2=server>\n"
909 " Selects the connection mode\n"
910 " Default: 0 (both)\n"
911 " --tcp-bind-addr[ess] <address>\n"
912 " The address(es) to listen to TCP connection on. Empty string\n"
913 " means any address, this is the default\n"
914 " --tcp-bind-port <port>\n"
915 " The port to listen to TCP connections on\n"
916 " Default: %u\n"
917 " --tcp-connect-addr[ess] <address>\n"
918 " The address of the server to try connect to in client mode\n"
919 " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR "\n"
920 " --tcp-connect-port <port>\n"
921 " The port on the server to connect to in client mode\n"
922 " Default: %u\n"
923 , ATS_TCP_DEF_BIND_PORT_GUEST, ATS_TCP_DEF_CONNECT_PORT_GUEST);
924}
925
926/** Command line options for the TCP/IP transport layer. */
927static const RTGETOPTDEF g_TcpOpts[] =
928{
929 { "--tcp-conn-mode", ATSTCPOPT_CONN_MODE, RTGETOPT_REQ_STRING },
930 { "--tcp-bind-addr", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
931 { "--tcp-bind-address", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
932 { "--tcp-bind-port", ATSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
933 { "--tcp-connect-addr", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
934 { "--tcp-connect-address", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
935 { "--tcp-connect-port", ATSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 }
936};
937
938/** TCP/IP transport layer. */
939const ATSTRANSPORT g_TcpTransport =
940{
941 /* .szName = */ "tcp",
942 /* .pszDesc = */ "TCP/IP",
943 /* .cOpts = */ &g_TcpOpts[0],
944 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
945 /* .pfnUsage = */ atsTcpUsage,
946 /* .pfnCreate = */ atsTcpCreate,
947 /* .pfnDestroy = */ atsTcpDestroy,
948 /* .pfnOption = */ atsTcpOption,
949 /* .pfnStart = */ atsTcpStart,
950 /* .pfnStop = */ atsTcpStop,
951 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
952 /* .pfnDisconnect = */ atsTcpDisconnect,
953 /* .pfnPollIn = */ atsTcpPollIn,
954 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
955 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
956 /* .pfnRecvPkt = */ atsTcpRecvPkt,
957 /* .pfnSendPkt = */ atsTcpSendPkt,
958 /* .pfnBabble = */ atsTcpBabble,
959 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
960 /* .pfnNotifyBye = */ atsTcpNotifyBye,
961 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
962 /* .u32EndMarker = */ UINT32_C(0x12345678)
963};
964
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