VirtualBox

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

Last change on this file since 93603 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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