VirtualBox

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

Last change on this file since 64468 was 62956, checked in by vboxsync, 8 years ago

@copydoc -> @interface_method_impl

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