VirtualBox

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

Last change on this file since 61239 was 60488, checked in by vboxsync, 9 years ago

ValidationKit/usb: Fixes

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