VirtualBox

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

Last change on this file since 62564 was 62471, checked in by vboxsync, 9 years ago

Misc: scm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: UsbTestServiceTcp.cpp 62471 2016-07-22 18:04:30Z vboxsync $ */
2/** @file
3 * UsbTestService - Remote USB test configuration and execution server, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/** Stop connecting attempts when set. */
91static bool g_fTcpStopConnecting = false;
92
93
94
95/**
96 * Disconnects the current client and frees all stashed data.
97 */
98static void utsTcpDisconnectClient(PUTSTRANSPORTCLIENT pClient)
99{
100 if (pClient->hTcpClient == NIL_RTSOCKET)
101 {
102 int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
103 pClient->hTcpClient = NIL_RTSOCKET;
104 AssertRCSuccess(rc);
105 }
106
107 if (pClient->pbTcpStashed)
108 {
109 RTMemFree(pClient->pbTcpStashed);
110 pClient->pbTcpStashed = NULL;
111 }
112}
113
114/**
115 * @interface_method_impl{UTSTRANSPORT,pfnWaitForConnect}
116 */
117static DECLCALLBACK(int) utsTcpWaitForConnect(PPUTSTRANSPORTCLIENT ppClientNew)
118{
119 int rc;
120 RTSOCKET hClientNew;
121
122 rc = RTTcpServerListen2(g_pTcpServer, &hClientNew);
123 Log(("utsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc));
124
125 if (RT_SUCCESS(rc))
126 {
127 PUTSTRANSPORTCLIENT pClient = (PUTSTRANSPORTCLIENT)RTMemAllocZ(sizeof(UTSTRANSPORTCLIENT));
128 if (RT_LIKELY(pClient))
129 {
130 pClient->hTcpClient = hClientNew;
131 pClient->cbTcpStashed = 0;
132 pClient->cbTcpStashedAlloced = 0;
133 pClient->pbTcpStashed = NULL;
134 *ppClientNew = pClient;
135 }
136 else
137 {
138 RTTcpServerDisconnectClient2(hClientNew);
139 rc = VERR_NO_MEMORY;
140 }
141 }
142
143 return rc;
144}
145
146/**
147 * @interface_method_impl{UTSTRANSPORT,pfnNotifyReboot}
148 */
149static DECLCALLBACK(void) utsTcpNotifyReboot(void)
150{
151 Log(("utsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
152 if (g_pTcpServer)
153 {
154 int rc = RTTcpServerDestroy(g_pTcpServer);
155 if (RT_FAILURE(rc))
156 RTMsgInfo("RTTcpServerDestroy failed in utsTcpNotifyReboot: %Rrc", rc);
157 g_pTcpServer = NULL;
158 }
159}
160
161/**
162 * @interface_method_impl{UTSTRANSPORT,pfnNotifyBye}
163 */
164static DECLCALLBACK(void) utsTcpNotifyBye(PUTSTRANSPORTCLIENT pClient)
165{
166 Log(("utsTcpNotifyBye: utsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
167 utsTcpDisconnectClient(pClient);
168 RTMemFree(pClient);
169}
170
171/**
172 * @interface_method_impl{UTSTRANSPORT,pfnNotifyHowdy}
173 */
174static DECLCALLBACK(void) utsTcpNotifyHowdy(PUTSTRANSPORTCLIENT pClient)
175{
176 /* nothing to do here */
177}
178
179/**
180 * @interface_method_impl{UTSTRANSPORT,pfnBabble}
181 */
182static DECLCALLBACK(void) utsTcpBabble(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
183{
184 /*
185 * Try send the babble reply.
186 */
187 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
188 int rc;
189 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT);
190 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
191 while (rc == VERR_INTERRUPTED);
192
193 /*
194 * Disconnect the client.
195 */
196 Log(("utsTcpBabble: utsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
197 utsTcpDisconnectClient(pClient);
198}
199
200/**
201 * @interface_method_impl{UTSTRANSPORT,pfnSendPkt}
202 */
203static DECLCALLBACK(int) utsTcpSendPkt(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr)
204{
205 Assert(pPktHdr->cb >= sizeof(UTSPKTHDR));
206
207 /*
208 * Write it.
209 */
210 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT);
211 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
212 if ( RT_FAILURE(rc)
213 && rc != VERR_INTERRUPTED)
214 {
215 /* assume fatal connection error. */
216 Log(("RTTcpWrite -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
217 utsTcpDisconnectClient(pClient);
218 }
219
220 return rc;
221}
222
223/**
224 * @interface_method_impl{UTSTRANSPORT,pfnRecvPkt}
225 */
226static DECLCALLBACK(int) utsTcpRecvPkt(PUTSTRANSPORTCLIENT pClient, PPUTSPKTHDR ppPktHdr)
227{
228 int rc = VINF_SUCCESS;
229 *ppPktHdr = NULL;
230
231 /*
232 * Read state.
233 */
234 size_t offData = 0;
235 size_t cbData = 0;
236 size_t cbDataAlloced;
237 uint8_t *pbData = NULL;
238
239 /*
240 * Any stashed data?
241 */
242 if (pClient->cbTcpStashedAlloced)
243 {
244 offData = pClient->cbTcpStashed;
245 cbDataAlloced = pClient->cbTcpStashedAlloced;
246 pbData = pClient->pbTcpStashed;
247
248 pClient->cbTcpStashed = 0;
249 pClient->cbTcpStashedAlloced = 0;
250 pClient->pbTcpStashed = NULL;
251 }
252 else
253 {
254 cbDataAlloced = RT_ALIGN_Z(64, UTSPKT_ALIGNMENT);
255 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
256 if (!pbData)
257 return VERR_NO_MEMORY;
258 }
259
260 /*
261 * Read and valid the length.
262 */
263 while (offData < sizeof(uint32_t))
264 {
265 size_t cbRead;
266 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
267 if (RT_FAILURE(rc))
268 break;
269 if (cbRead == 0)
270 {
271 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
272 rc = VERR_NET_NOT_CONNECTED;
273 break;
274 }
275 offData += cbRead;
276 }
277 if (RT_SUCCESS(rc))
278 {
279 ASMCompilerBarrier(); /* paranoia^3 */
280 cbData = *(uint32_t volatile *)pbData;
281 if (cbData >= sizeof(UTSPKTHDR) && cbData <= UTSPKT_MAX_SIZE)
282 {
283 /*
284 * Align the length and reallocate the return packet it necessary.
285 */
286 cbData = RT_ALIGN_Z(cbData, UTSPKT_ALIGNMENT);
287 if (cbData > cbDataAlloced)
288 {
289 void *pvNew = RTMemRealloc(pbData, cbData);
290 if (pvNew)
291 {
292 pbData = (uint8_t *)pvNew;
293 cbDataAlloced = cbData;
294 }
295 else
296 rc = VERR_NO_MEMORY;
297 }
298 if (RT_SUCCESS(rc))
299 {
300 /*
301 * Read the remainder of the data.
302 */
303 while (offData < cbData)
304 {
305 size_t cbRead;
306 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
307 if (RT_FAILURE(rc))
308 break;
309 if (cbRead == 0)
310 {
311 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
312 rc = VERR_NET_NOT_CONNECTED;
313 break;
314 }
315 offData += cbRead;
316 }
317 }
318 }
319 else
320 rc = VERR_NET_PROTOCOL_ERROR;
321 }
322 if (RT_SUCCESS(rc))
323 *ppPktHdr = (PUTSPKTHDR)pbData;
324 else
325 {
326 /*
327 * Deal with errors.
328 */
329 if (rc == VERR_INTERRUPTED)
330 {
331 /* stash it away for the next call. */
332 pClient->cbTcpStashed = cbData;
333 pClient->cbTcpStashedAlloced = cbDataAlloced;
334 pClient->pbTcpStashed = pbData;
335 }
336 else
337 {
338 RTMemFree(pbData);
339
340 /* assume fatal connection error. */
341 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
342 utsTcpDisconnectClient(pClient);
343 }
344 }
345
346 return rc;
347}
348
349/**
350 * @interface_method_impl{UTSTRANSPORT,pfnPollSetAdd}
351 */
352static DECLCALLBACK(int) utsTcpPollSetAdd(RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart)
353{
354 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
355}
356
357/**
358 * @interface_method_impl{UTSTRANSPORT,pfnPollSetRemove}
359 */
360static DECLCALLBACK(int) utsTcpPollSetRemove(RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart)
361{
362 return RTPollSetRemove(hPollSet, idStart);
363}
364
365/**
366 * @interface_method_impl{UTSTRANSPORT,pfnPollIn}
367 */
368static DECLCALLBACK(bool) utsTcpPollIn(PUTSTRANSPORTCLIENT pClient)
369{
370 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
371 return RT_SUCCESS(rc);
372}
373
374/**
375 * @interface_method_impl{UTSTRANSPORT,pfnTerm}
376 */
377static DECLCALLBACK(void) utsTcpTerm(void)
378{
379 /* Shut down the server (will wake up thread). */
380 if (g_pTcpServer)
381 {
382 Log(("utsTcpTerm: Destroying server...\n"));
383 int rc = RTTcpServerDestroy(g_pTcpServer);
384 if (RT_FAILURE(rc))
385 RTMsgInfo("RTTcpServerDestroy failed in utsTcpTerm: %Rrc", rc);
386 g_pTcpServer = NULL;
387 }
388
389 Log(("utsTcpTerm: done\n"));
390}
391
392/**
393 * @interface_method_impl{UTSTRANSPORT,pfnInit}
394 */
395static DECLCALLBACK(int) utsTcpInit(void)
396{
397 int rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
398 if (RT_FAILURE(rc))
399 {
400 if (rc == VERR_NET_DOWN)
401 {
402 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
403 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
404 uint64_t StartMs = RTTimeMilliTS();
405 do
406 {
407 RTThreadSleep(1000);
408 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
409 } while ( rc == VERR_NET_DOWN
410 && RTTimeMilliTS() - StartMs < 20000);
411 if (RT_SUCCESS(rc))
412 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
413 }
414 if (RT_FAILURE(rc))
415 {
416 g_pTcpServer = NULL;
417 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
418 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
419 }
420 }
421
422 return rc;
423}
424
425/** Options */
426enum UTSTCPOPT
427{
428 UTSTCPOPT_BIND_ADDRESS = 1000,
429 UTSTCPOPT_BIND_PORT
430};
431
432/**
433 * @interface_method_impl{UTSTRANSPORT,pfnOption}
434 */
435static DECLCALLBACK(int) utsTcpOption(int ch, PCRTGETOPTUNION pVal)
436{
437 int rc;
438
439 switch (ch)
440 {
441 case UTSTCPOPT_BIND_ADDRESS:
442 rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
443 if (RT_FAILURE(rc))
444 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
445 return VINF_SUCCESS;
446
447 case UTSTCPOPT_BIND_PORT:
448 g_uTcpBindPort = pVal->u16 == 0 ? UTS_TCP_DEF_BIND_PORT : pVal->u16;
449 return VINF_SUCCESS;
450 }
451 return VERR_TRY_AGAIN;
452}
453
454/**
455 * @interface_method_impl{UTSTRANSPORT,pfnUsage}
456 */
457DECLCALLBACK(void) utsTcpUsage(PRTSTREAM pStream)
458{
459 RTStrmPrintf(pStream,
460 " --tcp-bind-address <address>\n"
461 " The address(es) to listen to TCP connection on. Empty string\n"
462 " means any address, this is the default.\n"
463 " --tcp-bind-port <port>\n"
464 " The port to listen to TCP connections on.\n"
465 " Default: %u\n"
466 , UTS_TCP_DEF_BIND_PORT);
467}
468
469/** Command line options for the TCP/IP transport layer. */
470static const RTGETOPTDEF g_TcpOpts[] =
471{
472 { "--tcp-bind-address", UTSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
473 { "--tcp-bind-port", UTSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 }
474};
475
476/** TCP/IP transport layer. */
477const UTSTRANSPORT g_TcpTransport =
478{
479 /* .szName = */ "tcp",
480 /* .pszDesc = */ "TCP/IP",
481 /* .cOpts = */ &g_TcpOpts[0],
482 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
483 /* .pfnUsage = */ utsTcpUsage,
484 /* .pfnOption = */ utsTcpOption,
485 /* .pfnInit = */ utsTcpInit,
486 /* .pfnTerm = */ utsTcpTerm,
487 /* .pfnWaitForConnect = */ utsTcpWaitForConnect,
488 /* .pfnPollIn = */ utsTcpPollIn,
489 /* .pfnPollSetAdd = */ utsTcpPollSetAdd,
490 /* .pfnPollSetRemove = */ utsTcpPollSetRemove,
491 /* .pfnRecvPkt = */ utsTcpRecvPkt,
492 /* .pfnSendPkt = */ utsTcpSendPkt,
493 /* .pfnBabble = */ utsTcpBabble,
494 /* .pfnNotifyHowdy = */ utsTcpNotifyHowdy,
495 /* .pfnNotifyBye = */ utsTcpNotifyBye,
496 /* .pfnNotifyReboot = */ utsTcpNotifyReboot,
497 /* .u32EndMarker = */ UINT32_C(0x12345678)
498};
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette