VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvTCP.cpp@ 56292

Last change on this file since 56292 was 55260, checked in by vboxsync, 10 years ago

5.0.0 Beta 2

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: DrvTCP.cpp 55260 2015-04-14 18:21:00Z vboxsync $ */
2/** @file
3 * TCP socket driver implementing the IStream interface.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation.
8 *
9 * This file was contributed by Alexey Eromenko (derived from DrvNamedPipe)
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*******************************************************************************
22* Header Files *
23*******************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_TCP
25#include <VBox/vmm/pdmdrv.h>
26#include <iprt/assert.h>
27#include <iprt/file.h>
28#include <iprt/stream.h>
29#include <iprt/alloc.h>
30#include <iprt/string.h>
31#include <iprt/semaphore.h>
32#include <iprt/uuid.h>
33#include <stdlib.h>
34
35#include "VBoxDD.h"
36
37#ifdef RT_OS_WINDOWS
38# include <ws2tcpip.h>
39#else /* !RT_OS_WINDOWS */
40# include <errno.h>
41# include <unistd.h>
42# include <sys/types.h>
43# include <sys/socket.h>
44# include <netinet/in.h>
45# include <netdb.h>
46# ifndef SHUT_RDWR /* OS/2 */
47# define SHUT_RDWR 3
48# endif
49#endif /* !RT_OS_WINDOWS */
50
51#ifndef SHUT_RDWR
52# ifdef SD_BOTH
53# define SHUT_RDWR SD_BOTH
54# else
55# define SHUT_RDWR 2
56# endif
57#endif
58
59/*******************************************************************************
60* Defined Constants And Macros *
61*******************************************************************************/
62/** Converts a pointer to DRVTCP::IMedia to a PDRVTCP. */
63#define PDMISTREAM_2_DRVTCP(pInterface) ( (PDRVTCP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTCP, IStream)) )
64
65/*******************************************************************************
66* Structures and Typedefs *
67*******************************************************************************/
68/**
69 * TCP driver instance data.
70 *
71 * @implements PDMISTREAM
72 */
73typedef struct DRVTCP
74{
75 /** The stream interface. */
76 PDMISTREAM IStream;
77 /** Pointer to the driver instance. */
78 PPDMDRVINS pDrvIns;
79 /** Pointer to the TCP server address:port or port only. (Freed by MM) */
80 char *pszLocation;
81 /** Flag whether VirtualBox represents the server or client side. */
82 bool fIsServer;
83
84 /** Socket handle of the TCP socket for server. */
85 int TCPServer;
86 /** Socket handle of the TCP socket connection. */
87 int TCPConnection;
88
89 /** Thread for listening for new connections. */
90 RTTHREAD ListenThread;
91 /** Flag to signal listening thread to shut down. */
92 bool volatile fShutdown;
93} DRVTCP, *PDRVTCP;
94
95
96/*******************************************************************************
97* Internal Functions *
98*******************************************************************************/
99
100
101/** @copydoc PDMISTREAM::pfnRead */
102static DECLCALLBACK(int) drvTCPRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
103{
104 int rc = VINF_SUCCESS;
105 PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);
106 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
107
108 Assert(pvBuf);
109
110 if (pThis->TCPConnection != -1)
111 {
112 ssize_t cbReallyRead;
113 unsigned cbBuf = (unsigned)*pcbRead;
114 cbReallyRead = recv(pThis->TCPConnection, (char *)pvBuf, cbBuf, 0);
115 if (cbReallyRead == 0)
116 {
117 int tmp = pThis->TCPConnection;
118 pThis->TCPConnection = -1;
119#ifdef RT_OS_WINDOWS
120 closesocket(tmp);
121#else
122 close(tmp);
123#endif
124 }
125 else if (cbReallyRead == -1)
126 {
127 cbReallyRead = 0;
128 rc = RTErrConvertFromErrno(errno);
129 }
130 *pcbRead = cbReallyRead;
131 }
132 else
133 {
134 RTThreadSleep(100);
135 *pcbRead = 0;
136 }
137
138 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
139 return rc;
140}
141
142
143/** @copydoc PDMISTREAM::pfnWrite */
144static DECLCALLBACK(int) drvTCPWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
145{
146 int rc = VINF_SUCCESS;
147 PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);
148 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
149
150 Assert(pvBuf);
151 if (pThis->TCPConnection != -1)
152 {
153 ssize_t cbWritten;
154 unsigned cbBuf = (unsigned)*pcbWrite;
155 cbWritten = send(pThis->TCPConnection, (const char *)pvBuf, cbBuf, 0);
156 if (cbWritten == 0)
157 {
158 int tmp = pThis->TCPConnection;
159 pThis->TCPConnection = -1;
160#ifdef RT_OS_WINDOWS
161 closesocket(tmp);
162#else
163 close(tmp);
164#endif
165 }
166 else if (cbWritten == -1)
167 {
168 cbWritten = 0;
169 rc = RTErrConvertFromErrno(errno);
170 }
171 *pcbWrite = cbWritten;
172 }
173
174 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
175 return rc;
176}
177
178
179/**
180 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
181 */
182static DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
183{
184 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
185 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
186 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
187 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
188 return NULL;
189}
190
191
192/* -=-=-=-=- listen thread -=-=-=-=- */
193
194/**
195 * Receive thread loop.
196 *
197 * @returns 0 on success.
198 * @param ThreadSelf Thread handle to this thread.
199 * @param pvUser User argument.
200 */
201static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD ThreadSelf, void *pvUser)
202{
203 PDRVTCP pThis = (PDRVTCP)pvUser;
204 int rc = VINF_SUCCESS;
205
206 while (RT_LIKELY(!pThis->fShutdown))
207 {
208 if (listen(pThis->TCPServer, 0) == -1)
209 {
210 rc = RTErrConvertFromErrno(errno);
211 LogRel(("DrvTCP%d: listen failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
212 break;
213 }
214 int s = accept(pThis->TCPServer, NULL, NULL);
215 if (s == -1)
216 {
217 rc = RTErrConvertFromErrno(errno);
218 LogRel(("DrvTCP%d: accept failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
219 break;
220 }
221 if (pThis->TCPConnection != -1)
222 {
223 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
224#ifdef RT_OS_WINDOWS
225 closesocket(s);
226#else
227 close(s);
228#endif
229 }
230 else
231 pThis->TCPConnection = s;
232 }
233
234 return VINF_SUCCESS;
235}
236
237/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
238
239/**
240 * Common worker for drvTCPPowerOff and drvTCPDestructor.
241 *
242 * @param pThis The instance data.
243 */
244static void drvTCPShutdownListener(PDRVTCP pThis)
245{
246 /*
247 * Signal shutdown of the listener thread.
248 */
249 pThis->fShutdown = true;
250 if ( pThis->fIsServer
251 && pThis->TCPServer != -1)
252 {
253 int rc = shutdown(pThis->TCPServer, SHUT_RDWR);
254 AssertRC(rc == 0); NOREF(rc);
255
256#ifdef RT_OS_WINDOWS
257 rc = closesocket(pThis->TCPServer);
258#else
259 rc = close(pThis->TCPServer);
260#endif
261 AssertRC(rc == 0);
262 pThis->TCPServer = -1;
263 }
264}
265
266
267/**
268 * Power off a TCP socket stream driver instance.
269 *
270 * This does most of the destruction work, to avoid ordering dependencies.
271 *
272 * @param pDrvIns The driver instance data.
273 */
274static DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
275{
276 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
277 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
278
279 drvTCPShutdownListener(pThis);
280}
281
282
283/**
284 * Destruct a TCP socket stream driver instance.
285 *
286 * Most VM resources are freed by the VM. This callback is provided so that
287 * any non-VM resources can be freed correctly.
288 *
289 * @param pDrvIns The driver instance data.
290 */
291static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
292{
293 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
294 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
295 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
296
297 drvTCPShutdownListener(pThis);
298
299 /*
300 * While the thread exits, clean up as much as we can.
301 */
302
303 Assert(pThis->TCPServer == -1);
304 if (pThis->TCPConnection != -1)
305 {
306 int rc = shutdown(pThis->TCPConnection, SHUT_RDWR);
307 AssertRC(rc == 0); NOREF(rc);
308
309#ifdef RT_OS_WINDOWS
310 rc = closesocket(pThis->TCPConnection);
311#else
312 rc = close(pThis->TCPConnection);
313#endif
314 Assert(rc == 0);
315 pThis->TCPConnection = -1;
316 }
317 if ( pThis->fIsServer
318 && pThis->pszLocation)
319 RTFileDelete(pThis->pszLocation);
320
321
322 MMR3HeapFree(pThis->pszLocation);
323 pThis->pszLocation = NULL;
324
325 /*
326 * Wait for the thread.
327 */
328 if (pThis->ListenThread != NIL_RTTHREAD)
329 {
330 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
331 if (RT_SUCCESS(rc))
332 pThis->ListenThread = NIL_RTTHREAD;
333 else
334 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
335 }
336
337}
338
339
340/**
341 * Construct a TCP socket stream driver instance.
342 *
343 * @copydoc FNPDMDRVCONSTRUCT
344 */
345static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
346{
347 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
348 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
349
350 /*
351 * Init the static parts.
352 */
353 pThis->pDrvIns = pDrvIns;
354 pThis->pszLocation = NULL;
355 pThis->fIsServer = false;
356
357 pThis->TCPServer = -1;
358 pThis->TCPConnection = -1;
359
360 pThis->ListenThread = NIL_RTTHREAD;
361 pThis->fShutdown = false;
362 /* IBase */
363 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
364 /* IStream */
365 pThis->IStream.pfnRead = drvTCPRead;
366 pThis->IStream.pfnWrite = drvTCPWrite;
367
368 /*
369 * Validate and read the configuration.
370 */
371 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
372
373 int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
374 if (RT_FAILURE(rc))
375 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
376 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
377 rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
378 if (RT_FAILURE(rc))
379 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
380 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
381
382 /*
383 * Create/Open the socket.
384 */
385 int s = socket(PF_INET, SOCK_STREAM, 0);
386 if (s == -1)
387 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
388 N_("DrvTCP#%d failed to create socket"), pDrvIns->iInstance);
389
390 struct sockaddr_in addr;
391 memset(&addr, 0, sizeof(addr));
392 addr.sin_family = AF_INET;
393
394 if (pThis->fIsServer)
395 {
396 addr.sin_addr.s_addr = INADDR_ANY;
397 addr.sin_port = htons(/*PORT*/ atoi(pThis->pszLocation));
398
399 /* Bind address to the telnet socket. */
400 pThis->TCPServer = s;
401 RTFileDelete(pThis->pszLocation);
402 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
403 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
404 N_("DrvTCP#%d failed to bind to socket %s"),
405 pDrvIns->iInstance, pThis->pszLocation);
406 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
407 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
408 if (RT_FAILURE(rc))
409 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
410 N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
411 }
412 else
413 {
414 char *token;
415 const char *delim = ":";
416 struct hostent *server;
417 token = strtok(pThis->pszLocation, delim);
418 if(token) {
419 server = gethostbyname(token);
420 memmove((char *)&addr.sin_addr.s_addr,
421 (char *)server->h_addr,
422 server->h_length);
423 }
424 token = strtok(NULL, delim);
425 if(token) {
426 addr.sin_port = htons(/*PORT*/ atoi(token));
427 }
428
429 /* Connect to the telnet socket. */
430 pThis->TCPConnection = s;
431 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
432 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
433 N_("DrvTCP#%d failed to connect to socket %s"),
434 pDrvIns->iInstance, pThis->pszLocation);
435 }
436
437 LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
438 return VINF_SUCCESS;
439}
440
441
442/**
443 * TCP stream driver registration record.
444 */
445const PDMDRVREG g_DrvTCP =
446{
447 /* u32Version */
448 PDM_DRVREG_VERSION,
449 /* szName */
450 "TCP",
451 /* szRCMod */
452 "",
453 /* szR0Mod */
454 "",
455 /* pszDescription */
456 "TCP serial stream driver.",
457 /* fFlags */
458 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
459 /* fClass. */
460 PDM_DRVREG_CLASS_STREAM,
461 /* cMaxInstances */
462 ~0U,
463 /* cbInstance */
464 sizeof(DRVTCP),
465 /* pfnConstruct */
466 drvTCPConstruct,
467 /* pfnDestruct */
468 drvTCPDestruct,
469 /* pfnRelocate */
470 NULL,
471 /* pfnIOCtl */
472 NULL,
473 /* pfnPowerOn */
474 NULL,
475 /* pfnReset */
476 NULL,
477 /* pfnSuspend */
478 NULL,
479 /* pfnResume */
480 NULL,
481 /* pfnAttach */
482 NULL,
483 /* pfnDetach */
484 NULL,
485 /* pfnPowerOff */
486 drvTCPPowerOff,
487 /* pfnSoftReset */
488 NULL,
489 /* u32EndVersion */
490 PDM_DRVREG_VERSION
491};
492
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