VirtualBox

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

Last change on this file since 89468 was 89204, checked in by vboxsync, 4 years ago

Audio/ValKit: More code for the audio test execution service (ATS), client side. bugref:10008

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