VirtualBox

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

Last change on this file since 92760 was 92463, checked in by vboxsync, 3 years ago

Audio/Validation Kit: Syntax help massaging. bugref:10008

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