VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp@ 103914

Last change on this file since 103914 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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