VirtualBox

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

Last change on this file since 89186 was 89180, checked in by vboxsync, 4 years ago

Audio/ValKit: Started working on the audio test execution service (ATS) [SCM fixes]. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: AudioTestServiceTcp.cpp 89180 2021-05-19 15:46:15Z 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 RTLOGGROUP_DEFAULT
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#include <iprt/critsect.h>
26#include <iprt/err.h>
27#include <iprt/log.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/poll.h>
31#include <iprt/string.h>
32#include <iprt/tcp.h>
33#include <iprt/thread.h>
34#include <iprt/time.h>
35
36#include "AudioTestServiceInternal.h"
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42/** The default server port. */
43#define ATS_TCP_DEF_BIND_PORT 6042
44/** The default server bind address. */
45#define ATS_TCP_DEF_BIND_ADDRESS ""
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51
52/**
53 * TCP specific client data.
54 */
55typedef struct ATSTRANSPORTCLIENT
56{
57 /** Socket of the current client. */
58 RTSOCKET hTcpClient;
59 /** The size of the stashed data. */
60 size_t cbTcpStashed;
61 /** The size of the stashed data allocation. */
62 size_t cbTcpStashedAlloced;
63 /** The stashed data. */
64 uint8_t *pbTcpStashed;
65} ATSTRANSPORTCLIENT;
66
67
68/*********************************************************************************************************************************
69* Global Variables *
70*********************************************************************************************************************************/
71/** @name TCP Parameters
72 * @{ */
73/** The addresses to bind to. Empty string means any. */
74static char g_szTcpBindAddr[256] = ATS_TCP_DEF_BIND_ADDRESS;
75/** The TCP port to listen to. */
76static uint32_t g_uTcpBindPort = ATS_TCP_DEF_BIND_PORT;
77/** @} */
78
79/** Pointer to the TCP server instance. */
80static PRTTCPSERVER g_pTcpServer = NULL;
81#if 0 /* unused */
82/** Stop connecting attempts when set. */
83static bool g_fTcpStopConnecting = false;
84#endif
85
86
87
88/**
89 * Disconnects the current client and frees all stashed data.
90 */
91static void atsTcpDisconnectClient(PATSTRANSPORTCLIENT pClient)
92{
93 if (pClient->hTcpClient != NIL_RTSOCKET)
94 {
95 int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
96 pClient->hTcpClient = NIL_RTSOCKET;
97 AssertRCSuccess(rc);
98 }
99
100 if (pClient->pbTcpStashed)
101 {
102 RTMemFree(pClient->pbTcpStashed);
103 pClient->pbTcpStashed = NULL;
104 }
105}
106
107/**
108 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
109 */
110static DECLCALLBACK(int) atsTcpWaitForConnect(PPATSTRANSPORTCLIENT ppClientNew)
111{
112 int rc;
113 RTSOCKET hClientNew;
114
115 rc = RTTcpServerListen2(g_pTcpServer, &hClientNew);
116 Log(("atsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc));
117
118 if (RT_SUCCESS(rc))
119 {
120 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
121 if (RT_LIKELY(pClient))
122 {
123 pClient->hTcpClient = hClientNew;
124 pClient->cbTcpStashed = 0;
125 pClient->cbTcpStashedAlloced = 0;
126 pClient->pbTcpStashed = NULL;
127 *ppClientNew = pClient;
128 }
129 else
130 {
131 RTTcpServerDisconnectClient2(hClientNew);
132 rc = VERR_NO_MEMORY;
133 }
134 }
135
136 return rc;
137}
138
139/**
140 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
141 */
142static DECLCALLBACK(void) atsTcpNotifyReboot(void)
143{
144 Log(("atsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
145 if (g_pTcpServer)
146 {
147 int rc = RTTcpServerDestroy(g_pTcpServer);
148 if (RT_FAILURE(rc))
149 RTMsgInfo("RTTcpServerDestroy failed in atsTcpNotifyReboot: %Rrc", rc);
150 g_pTcpServer = NULL;
151 }
152}
153
154/**
155 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
156 */
157static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTCLIENT pClient)
158{
159 Log(("atsTcpNotifyBye: atsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
160 atsTcpDisconnectClient(pClient);
161 RTMemFree(pClient);
162}
163
164/**
165 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
166 */
167static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTCLIENT pClient)
168{
169 /* nothing to do here */
170 RT_NOREF1(pClient);
171}
172
173/**
174 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
175 */
176static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
177{
178 /*
179 * Try send the babble reply.
180 */
181 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
182 int rc;
183 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
184 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
185 while (rc == VERR_INTERRUPTED);
186
187 /*
188 * Disconnect the client.
189 */
190 Log(("atsTcpBabble: atsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
191 atsTcpDisconnectClient(pClient);
192}
193
194/**
195 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
196 */
197static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
198{
199 Assert(pPktHdr->cb >= sizeof(ATSPKTHDR));
200
201 /*
202 * Write it.
203 */
204 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
205 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
206 if ( RT_FAILURE(rc)
207 && rc != VERR_INTERRUPTED)
208 {
209 /* assume fatal connection error. */
210 Log(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
211 atsTcpDisconnectClient(pClient);
212 }
213
214 return rc;
215}
216
217/**
218 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
219 */
220static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
221{
222 int rc = VINF_SUCCESS;
223 *ppPktHdr = NULL;
224
225 /*
226 * Read state.
227 */
228 size_t offData = 0;
229 size_t cbData = 0;
230 size_t cbDataAlloced;
231 uint8_t *pbData = NULL;
232
233 /*
234 * Any stashed data?
235 */
236 if (pClient->cbTcpStashedAlloced)
237 {
238 offData = pClient->cbTcpStashed;
239 cbDataAlloced = pClient->cbTcpStashedAlloced;
240 pbData = pClient->pbTcpStashed;
241
242 pClient->cbTcpStashed = 0;
243 pClient->cbTcpStashedAlloced = 0;
244 pClient->pbTcpStashed = NULL;
245 }
246 else
247 {
248 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
249 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
250 if (!pbData)
251 return VERR_NO_MEMORY;
252 }
253
254 /*
255 * Read and valid the length.
256 */
257 while (offData < sizeof(uint32_t))
258 {
259 size_t cbRead;
260 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
261 if (RT_FAILURE(rc))
262 break;
263 if (cbRead == 0)
264 {
265 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
266 rc = VERR_NET_NOT_CONNECTED;
267 break;
268 }
269 offData += cbRead;
270 }
271 if (RT_SUCCESS(rc))
272 {
273 ASMCompilerBarrier(); /* paranoia^3 */
274 cbData = *(uint32_t volatile *)pbData;
275 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
276 {
277 /*
278 * Align the length and reallocate the return packet it necessary.
279 */
280 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
281 if (cbData > cbDataAlloced)
282 {
283 void *pvNew = RTMemRealloc(pbData, cbData);
284 if (pvNew)
285 {
286 pbData = (uint8_t *)pvNew;
287 cbDataAlloced = cbData;
288 }
289 else
290 rc = VERR_NO_MEMORY;
291 }
292 if (RT_SUCCESS(rc))
293 {
294 /*
295 * Read the remainder of the data.
296 */
297 while (offData < cbData)
298 {
299 size_t cbRead;
300 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
301 if (RT_FAILURE(rc))
302 break;
303 if (cbRead == 0)
304 {
305 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
306 rc = VERR_NET_NOT_CONNECTED;
307 break;
308 }
309 offData += cbRead;
310 }
311 }
312 }
313 else
314 rc = VERR_NET_PROTOCOL_ERROR;
315 }
316 if (RT_SUCCESS(rc))
317 *ppPktHdr = (PATSPKTHDR)pbData;
318 else
319 {
320 /*
321 * Deal with errors.
322 */
323 if (rc == VERR_INTERRUPTED)
324 {
325 /* stash it away for the next call. */
326 pClient->cbTcpStashed = cbData;
327 pClient->cbTcpStashedAlloced = cbDataAlloced;
328 pClient->pbTcpStashed = pbData;
329 }
330 else
331 {
332 RTMemFree(pbData);
333
334 /* assume fatal connection error. */
335 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
336 atsTcpDisconnectClient(pClient);
337 }
338 }
339
340 return rc;
341}
342
343/**
344 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
345 */
346static DECLCALLBACK(int) atsTcpPollSetAdd(RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
347{
348 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
349}
350
351/**
352 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
353 */
354static DECLCALLBACK(int) atsTcpPollSetRemove(RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
355{
356 RT_NOREF1(pClient);
357 return RTPollSetRemove(hPollSet, idStart);
358}
359
360/**
361 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
362 */
363static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTCLIENT pClient)
364{
365 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
366 return RT_SUCCESS(rc);
367}
368
369/**
370 * @interface_method_impl{ATSTRANSPORT,pfnTerm}
371 */
372static DECLCALLBACK(void) atsTcpTerm(void)
373{
374 /* Shut down the server (will wake up thread). */
375 if (g_pTcpServer)
376 {
377 Log(("atsTcpTerm: Destroying server...\n"));
378 int rc = RTTcpServerDestroy(g_pTcpServer);
379 if (RT_FAILURE(rc))
380 RTMsgInfo("RTTcpServerDestroy failed in atsTcpTerm: %Rrc", rc);
381 g_pTcpServer = NULL;
382 }
383
384 Log(("atsTcpTerm: done\n"));
385}
386
387/**
388 * @interface_method_impl{ATSTRANSPORT,pfnInit}
389 */
390static DECLCALLBACK(int) atsTcpInit(void)
391{
392 int rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
393 if (RT_FAILURE(rc))
394 {
395 if (rc == VERR_NET_DOWN)
396 {
397 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
398 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
399 uint64_t StartMs = RTTimeMilliTS();
400 do
401 {
402 RTThreadSleep(1000);
403 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
404 } while ( rc == VERR_NET_DOWN
405 && RTTimeMilliTS() - StartMs < 20000);
406 if (RT_SUCCESS(rc))
407 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
408 }
409 if (RT_FAILURE(rc))
410 {
411 g_pTcpServer = NULL;
412 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
413 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
414 }
415 }
416
417 return rc;
418}
419
420/** Options */
421enum ATSTCPOPT
422{
423 ATSTCPOPT_BIND_ADDRESS = 1000,
424 ATSTCPOPT_BIND_PORT
425};
426
427/**
428 * @interface_method_impl{ATSTRANSPORT,pfnOption}
429 */
430static DECLCALLBACK(int) atsTcpOption(int ch, PCRTGETOPTUNION pVal)
431{
432 int rc;
433
434 switch (ch)
435 {
436 case ATSTCPOPT_BIND_ADDRESS:
437 rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
438 if (RT_FAILURE(rc))
439 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
440 return VINF_SUCCESS;
441
442 case ATSTCPOPT_BIND_PORT:
443 g_uTcpBindPort = pVal->u16 == 0 ? ATS_TCP_DEF_BIND_PORT : pVal->u16;
444 return VINF_SUCCESS;
445 }
446 return VERR_TRY_AGAIN;
447}
448
449/**
450 * @interface_method_impl{ATSTRANSPORT,pfnUsage}
451 */
452DECLCALLBACK(void) atsTcpUsage(PRTSTREAM pStream)
453{
454 RTStrmPrintf(pStream,
455 " --tcp-bind-address <address>\n"
456 " The address(es) to listen to TCP connection on. Empty string\n"
457 " means any address, this is the default.\n"
458 " --tcp-bind-port <port>\n"
459 " The port to listen to TCP connections on.\n"
460 " Default: %u\n"
461 , ATS_TCP_DEF_BIND_PORT);
462}
463
464/** Command line options for the TCP/IP transport layer. */
465static const RTGETOPTDEF g_TcpOpts[] =
466{
467 { "--tcp-bind-address", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
468 { "--tcp-bind-port", ATSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 }
469};
470
471/** TCP/IP transport layer. */
472const ATSTRANSPORT g_TcpTransport =
473{
474 /* .szName = */ "tcp",
475 /* .pszDesc = */ "TCP/IP",
476 /* .cOpts = */ &g_TcpOpts[0],
477 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
478 /* .pfnUsage = */ atsTcpUsage,
479 /* .pfnOption = */ atsTcpOption,
480 /* .pfnInit = */ atsTcpInit,
481 /* .pfnTerm = */ atsTcpTerm,
482 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
483 /* .pfnPollIn = */ atsTcpPollIn,
484 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
485 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
486 /* .pfnRecvPkt = */ atsTcpRecvPkt,
487 /* .pfnSendPkt = */ atsTcpSendPkt,
488 /* .pfnBabble = */ atsTcpBabble,
489 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
490 /* .pfnNotifyBye = */ atsTcpNotifyBye,
491 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
492 /* .u32EndMarker = */ UINT32_C(0x12345678)
493};
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