VirtualBox

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

Last change on this file since 89636 was 89614, checked in by vboxsync, 4 years ago

Audio/ValKit: Implemented support for downloading (guest) test sets. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/* $Id: AudioTestServiceTcp.cpp 89614 2021-06-11 06:34:13Z 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 "AudioTestService.h"
37#include "AudioTestServiceInternal.h"
38
39
40/*********************************************************************************************************************************
41* Defined Constants And Macros *
42*********************************************************************************************************************************/
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48
49/**
50 * TCP specific client data.
51 */
52typedef struct ATSTRANSPORTCLIENT
53{
54 /** Socket of the current client. */
55 RTSOCKET hTcpClient;
56 /** The size of the stashed data. */
57 size_t cbTcpStashed;
58 /** The size of the stashed data allocation. */
59 size_t cbTcpStashedAlloced;
60 /** The stashed data. */
61 uint8_t *pbTcpStashed;
62} ATSTRANSPORTCLIENT;
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68
69/**
70 * Disconnects the current client and frees all stashed data.
71 */
72static void atsTcpDisconnectClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
73{
74 RT_NOREF(pThis);
75
76 if (pClient->hTcpClient != NIL_RTSOCKET)
77 {
78 int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
79 pClient->hTcpClient = NIL_RTSOCKET;
80 AssertRCSuccess(rc);
81 }
82
83 if (pClient->pbTcpStashed)
84 {
85 RTMemFree(pClient->pbTcpStashed);
86 pClient->pbTcpStashed = NULL;
87 }
88}
89
90/**
91 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
92 */
93static DECLCALLBACK(int) atsTcpWaitForConnect(PATSTRANSPORTINST pThis, PPATSTRANSPORTCLIENT ppClientNew)
94{
95 int rc;
96 RTSOCKET hClientNew;
97
98 rc = RTTcpServerListen2(pThis->pTcpServer, &hClientNew);
99 Log(("atsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc));
100
101 if (RT_SUCCESS(rc))
102 {
103 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
104 if (RT_LIKELY(pClient))
105 {
106 pClient->hTcpClient = hClientNew;
107 pClient->cbTcpStashed = 0;
108 pClient->cbTcpStashedAlloced = 0;
109 pClient->pbTcpStashed = NULL;
110 *ppClientNew = pClient;
111 }
112 else
113 {
114 RTTcpServerDisconnectClient2(hClientNew);
115 rc = VERR_NO_MEMORY;
116 }
117 }
118
119 return rc;
120}
121
122/**
123 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
124 */
125static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
126{
127 Log(("atsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", pThis->pTcpServer));
128 if (pThis->pTcpServer)
129 {
130 int rc = RTTcpServerDestroy(pThis->pTcpServer);
131 if (RT_FAILURE(rc))
132 RTMsgInfo("RTTcpServerDestroy failed in atsTcpNotifyReboot: %Rrc", rc);
133 pThis->pTcpServer = NULL;
134 }
135}
136
137/**
138 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
139 */
140static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
141{
142 Log(("atsTcpNotifyBye: atsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
143 atsTcpDisconnectClient(pThis, pClient);
144 RTMemFree(pClient);
145}
146
147/**
148 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
149 */
150static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
151{
152 /* nothing to do here */
153 RT_NOREF(pThis, pClient);
154}
155
156/**
157 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
158 */
159static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
160{
161 /*
162 * Try send the babble reply.
163 */
164 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
165 int rc;
166 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
167 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
168 while (rc == VERR_INTERRUPTED);
169
170 /*
171 * Disconnect the client.
172 */
173 Log(("atsTcpBabble: atsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
174 atsTcpDisconnectClient(pThis, pClient);
175}
176
177/**
178 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
179 */
180static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
181{
182 Assert(pPktHdr->cb >= sizeof(ATSPKTHDR));
183
184 /*
185 * Write it.
186 */
187 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
188 LogFlowFunc(("%RU32 -> %zu\n", pPktHdr->cb, cbToSend));
189 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
190 if ( RT_FAILURE(rc)
191 && rc != VERR_INTERRUPTED)
192 {
193 /* assume fatal connection error. */
194 Log(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
195 atsTcpDisconnectClient(pThis, pClient);
196 }
197
198 return rc;
199}
200
201/**
202 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
203 */
204static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
205{
206 int rc = VINF_SUCCESS;
207 *ppPktHdr = NULL;
208
209 /*
210 * Read state.
211 */
212 size_t offData = 0;
213 size_t cbData = 0;
214 size_t cbDataAlloced;
215 uint8_t *pbData = NULL;
216
217 /*
218 * Any stashed data?
219 */
220 if (pClient->cbTcpStashedAlloced)
221 {
222 offData = pClient->cbTcpStashed;
223 cbDataAlloced = pClient->cbTcpStashedAlloced;
224 pbData = pClient->pbTcpStashed;
225
226 pClient->cbTcpStashed = 0;
227 pClient->cbTcpStashedAlloced = 0;
228 pClient->pbTcpStashed = NULL;
229 }
230 else
231 {
232 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
233 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
234 if (!pbData)
235 return VERR_NO_MEMORY;
236 }
237
238 /*
239 * Read and valid the length.
240 */
241 while (offData < sizeof(uint32_t))
242 {
243 size_t cbRead;
244 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
245 if (RT_FAILURE(rc))
246 break;
247 if (cbRead == 0)
248 {
249 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
250 rc = VERR_NET_NOT_CONNECTED;
251 break;
252 }
253 offData += cbRead;
254 }
255 if (RT_SUCCESS(rc))
256 {
257 ASMCompilerBarrier(); /* paranoia^3 */
258 cbData = *(uint32_t volatile *)pbData;
259 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
260 {
261 /*
262 * Align the length and reallocate the return packet it necessary.
263 */
264 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
265 if (cbData > cbDataAlloced)
266 {
267 void *pvNew = RTMemRealloc(pbData, cbData);
268 if (pvNew)
269 {
270 pbData = (uint8_t *)pvNew;
271 cbDataAlloced = cbData;
272 }
273 else
274 rc = VERR_NO_MEMORY;
275 }
276 if (RT_SUCCESS(rc))
277 {
278 /*
279 * Read the remainder of the data.
280 */
281 while (offData < cbData)
282 {
283 size_t cbRead;
284 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
285 if (RT_FAILURE(rc))
286 break;
287 if (cbRead == 0)
288 {
289 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
290 rc = VERR_NET_NOT_CONNECTED;
291 break;
292 }
293 offData += cbRead;
294 }
295 }
296 }
297 else
298 rc = VERR_NET_PROTOCOL_ERROR;
299 }
300 if (RT_SUCCESS(rc))
301 *ppPktHdr = (PATSPKTHDR)pbData;
302 else
303 {
304 /*
305 * Deal with errors.
306 */
307 if (rc == VERR_INTERRUPTED)
308 {
309 /* stash it away for the next call. */
310 pClient->cbTcpStashed = cbData;
311 pClient->cbTcpStashedAlloced = cbDataAlloced;
312 pClient->pbTcpStashed = pbData;
313 }
314 else
315 {
316 RTMemFree(pbData);
317
318 /* assume fatal connection error. */
319 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
320 atsTcpDisconnectClient(pThis, pClient);
321 }
322 }
323
324 return rc;
325}
326
327/**
328 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
329 */
330static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
331{
332 RT_NOREF(pThis);
333 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
334}
335
336/**
337 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
338 */
339static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
340{
341 RT_NOREF(pThis, pClient);
342 return RTPollSetRemove(hPollSet, idStart);
343}
344
345/**
346 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
347 */
348static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
349{
350 RT_NOREF(pThis);
351 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
352 return RT_SUCCESS(rc);
353}
354
355/**
356 * @interface_method_impl{ATSTRANSPORT,pfnTerm}
357 */
358static DECLCALLBACK(void) atsTcpTerm(PATSTRANSPORTINST pThis)
359{
360 /* Shut down the server (will wake up thread). */
361 if (pThis->pTcpServer)
362 {
363 Log(("atsTcpTerm: Destroying server...\n"));
364 int rc = RTTcpServerDestroy(pThis->pTcpServer);
365 if (RT_FAILURE(rc))
366 RTMsgInfo("RTTcpServerDestroy failed in atsTcpTerm: %Rrc", rc);
367 pThis->pTcpServer = NULL;
368 }
369
370 Log(("atsTcpTerm: done\n"));
371}
372
373/**
374 * @interface_method_impl{ATSTRANSPORT,pfnInit}
375 */
376static DECLCALLBACK(int) atsTcpInit(PATSTRANSPORTINST pThis, const char *pszBindAddr, uint32_t uBindPort)
377{
378 int rc = RTTcpServerCreateEx(pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, &pThis->pTcpServer);
379 if (RT_FAILURE(rc))
380 {
381 if (rc == VERR_NET_DOWN)
382 {
383 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
384 pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, rc);
385 uint64_t StartMs = RTTimeMilliTS();
386 do
387 {
388 RTThreadSleep(1000);
389 rc = RTTcpServerCreateEx(pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, &pThis->pTcpServer);
390 } while ( rc == VERR_NET_DOWN
391 && RTTimeMilliTS() - StartMs < 20000);
392 if (RT_SUCCESS(rc))
393 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
394 }
395 if (RT_FAILURE(rc))
396 {
397 pThis->pTcpServer = NULL;
398 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
399 pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, rc);
400 }
401 }
402
403 return rc;
404}
405
406/** TCP/IP transport layer. */
407const ATSTRANSPORT g_TcpTransport =
408{
409 /* .szName = */ "tcp",
410 /* .pszDesc = */ "TCP/IP",
411 /* .pfnInit = */ atsTcpInit,
412 /* .pfnTerm = */ atsTcpTerm,
413 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
414 /* .pfnPollIn = */ atsTcpPollIn,
415 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
416 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
417 /* .pfnRecvPkt = */ atsTcpRecvPkt,
418 /* .pfnSendPkt = */ atsTcpSendPkt,
419 /* .pfnBabble = */ atsTcpBabble,
420 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
421 /* .pfnNotifyBye = */ atsTcpNotifyBye,
422 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
423 /* .u32EndMarker = */ UINT32_C(0x12345678)
424};
425
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