VirtualBox

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

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

build fix

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