VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • 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 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * UsbTestService - Remote USB test configuration and execution server, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2010-2022 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 */
471DECLCALLBACK(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