VirtualBox

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

Last change on this file since 86692 was 86114, checked in by vboxsync, 4 years ago

Devices/Serial/DrvTCP: Decrement connection count on orderly shutdown, fixes connection again after the remote end had disconnected, ticketref:19878

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.9 KB
Line 
1/* $Id: DrvTCP.cpp 86114 2020-09-13 17:52:04Z vboxsync $ */
2/** @file
3 * TCP socket driver implementing the IStream interface.
4 */
5
6/*
7 * Contributed by Alexey Eromenko (derived from DrvNamedPipe).
8 *
9 * Copyright (C) 2006-2020 Oracle Corporation
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/pipe.h>
31#include <iprt/poll.h>
32#include <iprt/string.h>
33#include <iprt/semaphore.h>
34#include <iprt/socket.h>
35#include <iprt/tcp.h>
36#include <iprt/uuid.h>
37#include <stdlib.h>
38
39#include "VBoxDD.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45
46#define DRVTCP_POLLSET_ID_SOCKET 0
47#define DRVTCP_POLLSET_ID_WAKEUP 1
48
49#define DRVTCP_WAKEUP_REASON_EXTERNAL 0
50#define DRVTCP_WAKEUP_REASON_NEW_CONNECTION 1
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/**
57 * TCP driver instance data.
58 *
59 * @implements PDMISTREAM
60 */
61typedef struct DRVTCP
62{
63 /** The stream interface. */
64 PDMISTREAM IStream;
65 /** Pointer to the driver instance. */
66 PPDMDRVINS pDrvIns;
67 /** Pointer to the TCP server address:port or port only. (Freed by MM) */
68 char *pszLocation;
69 /** Flag whether VirtualBox represents the server or client side. */
70 bool fIsServer;
71
72 /** Handle of the TCP server for incoming connections. */
73 PRTTCPSERVER hTcpServ;
74 /** Socket handle of the TCP socket connection. */
75 RTSOCKET hTcpSock;
76
77 /** Poll set used to wait for I/O events. */
78 RTPOLLSET hPollSet;
79 /** Reading end of the wakeup pipe. */
80 RTPIPE hPipeWakeR;
81 /** Writing end of the wakeup pipe. */
82 RTPIPE hPipeWakeW;
83 /** Flag whether the send buffer is full nad it is required to wait for more
84 * space until there is room again. */
85 bool fXmitBufFull;
86
87 /** Number of connections active. */
88 volatile uint32_t cConnections;
89 /** Thread for listening for new connections. */
90 RTTHREAD ListenThread;
91 /** Flag to signal listening thread to shut down. */
92 bool volatile fShutdown;
93 /** Flag to signal whether the thread was woken up from external. */
94 bool volatile fWokenUp;
95} DRVTCP, *PDRVTCP;
96
97
98/*********************************************************************************************************************************
99* Internal Functions *
100*********************************************************************************************************************************/
101
102
103/**
104 * Kicks any possibly polling thread to get informed about changes - extended version
105 * sending additional data along with the wakeup reason.
106 *
107 * @returns VBOx status code.
108 * @param pThis The TCP driver instance.
109 * @param bReason The reason code to handle.
110 * @param pvData The additional to send along with the wakeup reason.
111 * @param cbData Number of bytes to send along.
112 */
113static int drvTcpPollerKickEx(PDRVTCP pThis, uint8_t bReason, const void *pvData, size_t cbData)
114{
115 size_t cbWritten = 0;
116 int rc = RTPipeWriteBlocking(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
117 if (RT_SUCCESS(rc))
118 rc = RTPipeWriteBlocking(pThis->hPipeWakeW, pvData, cbData, &cbWritten);
119 return rc;
120}
121
122
123/**
124 * Kicks any possibly polling thread to get informed about changes.
125 *
126 * @returns VBOx status code.
127 * @param pThis The TCP driver instance.
128 * @param bReason The reason code to handle.
129 */
130static int drvTcpPollerKick(PDRVTCP pThis, uint8_t bReason)
131{
132 size_t cbWritten = 0;
133 return RTPipeWriteBlocking(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
134}
135
136
137/**
138 * Closes the connection.
139 *
140 * @returns nothing.
141 * @param pThis The TCP driver instance.
142 */
143static void drvTcpConnectionClose(PDRVTCP pThis)
144{
145 Assert(pThis->hTcpSock != NIL_RTSOCKET);
146
147 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
148 AssertRC(rc);
149
150 if (pThis->fIsServer)
151 RTTcpServerDisconnectClient2(pThis->hTcpSock);
152 else
153 RTSocketClose(pThis->hTcpSock);
154 pThis->hTcpSock = NIL_RTSOCKET;
155 ASMAtomicDecU32(&pThis->cConnections);
156}
157
158
159/**
160 * Checks the wakeup pipe for events.
161 *
162 * @returns VBox status code.
163 * @param pThis The TCP driver instance.
164 * @param fEvts Event mask to set if a new connection arrived.
165 */
166static int drvTcpWakeupPipeCheckForRequest(PDRVTCP pThis, uint32_t fEvts)
167{
168 int rc = VINF_SUCCESS;
169
170 while ( RT_SUCCESS(rc)
171 || rc == VERR_INTERRUPTED)
172 {
173 uint8_t bReason;
174 size_t cbRead = 0;
175 int rc2 = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
176 if (rc2 == VINF_TRY_AGAIN) /* Nothing there so we are done here. */
177 break;
178 else if (RT_SUCCESS(rc2))
179 {
180 if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
181 {
182 ASMAtomicXchgBool(&pThis->fWokenUp, false);
183 rc = VERR_INTERRUPTED;
184 }
185 else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
186 {
187 Assert(pThis->hTcpSock == NIL_RTSOCKET);
188
189 /* Read the socket handle. */
190 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
191 rc = RTPipeReadBlocking(pThis->hPipeWakeR, &hTcpSockNew, sizeof(hTcpSockNew), NULL);
192 AssertRC(rc);
193
194 /* Always include error event. */
195 fEvts |= RTPOLL_EVT_ERROR;
196 rc = RTPollSetAddSocket(pThis->hPollSet, hTcpSockNew,
197 fEvts, DRVTCP_POLLSET_ID_SOCKET);
198 if (RT_SUCCESS(rc))
199 pThis->hTcpSock = hTcpSockNew;
200 }
201 else
202 AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
203 }
204 }
205
206 return rc;
207}
208
209
210/** @interface_method_impl{PDMISTREAM,pfnPoll} */
211static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
212{
213 int rc = VINF_SUCCESS;
214 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
215
216 if (pThis->hTcpSock != NIL_RTSOCKET)
217 {
218 Assert(ASMAtomicReadU32(&pThis->cConnections) > 0);
219
220 /* Always include error event. */
221 fEvts |= RTPOLL_EVT_ERROR;
222 rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
223 AssertRC(rc);
224 }
225 else
226 {
227 /*
228 * Check whether new connection arrived first so we don't miss it in case
229 * the guest is constantly writing data and we always end up here.
230 */
231 rc = drvTcpWakeupPipeCheckForRequest(pThis, fEvts);
232 if ( pThis->hTcpSock == NIL_RTSOCKET
233 && (fEvts & RTPOLL_EVT_WRITE))
234 {
235 /*
236 * Just pretend we can always write to not fill up any buffers and block the guest
237 * from sending data.
238 */
239 *pfEvts |= RTPOLL_EVT_WRITE;
240 return rc;
241 }
242 }
243
244 if (RT_SUCCESS(rc))
245 {
246 while (RT_SUCCESS(rc))
247 {
248 uint32_t fEvtsRecv = 0;
249 uint32_t idHnd = 0;
250 uint64_t tsStartMs = RTTimeMilliTS();
251 RTMSINTERVAL cThisWaitMs = cMillies;
252
253 /*
254 * Just check for data available to be read if the send buffer wasn't full till now and
255 * the caller wants to check whether writing is possible with the event set.
256 *
257 * On Windows the write event is only posted after a send operation returned
258 * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting
259 * for an event which would never happen if the buffer has space left.
260 */
261 if ( (fEvts & RTPOLL_EVT_WRITE)
262 && !pThis->fXmitBufFull
263 && pThis->hTcpSock != NIL_RTSOCKET)
264 cThisWaitMs = 0;
265
266 rc = RTPoll(pThis->hPollSet, cThisWaitMs, &fEvtsRecv, &idHnd);
267
268 /* Adjust remaining time to wait. */
269 uint64_t tsPollSpanMs = RTTimeMilliTS() - tsStartMs;
270 cMillies -= RT_MIN(cMillies, tsPollSpanMs);
271 if (RT_SUCCESS(rc))
272 {
273 if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
274 {
275 /* We got woken up, drain the pipe and return. */
276 rc = drvTcpWakeupPipeCheckForRequest(pThis, fEvts);
277 }
278 else
279 {
280 Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);
281
282 /* On error we close the socket here. */
283 if (fEvtsRecv & RTPOLL_EVT_ERROR)
284 drvTcpConnectionClose(pThis); /* Continue with polling afterwards. */
285 else
286 {
287 if (fEvtsRecv & RTPOLL_EVT_WRITE)
288 pThis->fXmitBufFull = false;
289 else if (!pThis->fXmitBufFull)
290 fEvtsRecv |= RTPOLL_EVT_WRITE;
291 *pfEvts = fEvtsRecv;
292 break;
293 }
294 }
295 }
296 else if ( rc == VERR_TIMEOUT
297 && !pThis->fXmitBufFull)
298 {
299 *pfEvts = RTPOLL_EVT_WRITE;
300 rc = VINF_SUCCESS;
301 break;
302 }
303 }
304 }
305
306 return rc;
307}
308
309
310/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
311static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface)
312{
313 int rc = VINF_SUCCESS;
314 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
315
316 if (!ASMAtomicXchgBool(&pThis->fWokenUp, true))
317 rc = drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL);
318
319 return rc;
320}
321
322
323/** @interface_method_impl{PDMISTREAM,pfnRead} */
324static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
325{
326 int rc = VINF_SUCCESS;
327 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
328 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
329
330 Assert(pvBuf);
331
332 if (pThis->hTcpSock != NIL_RTSOCKET)
333 {
334 size_t cbRead;
335 size_t cbBuf = *pcbRead;
336 rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
337 if (RT_SUCCESS(rc))
338 {
339 if (!cbRead && rc != VINF_TRY_AGAIN)
340 {
341 drvTcpConnectionClose(pThis);
342 rc = VINF_SUCCESS;
343 }
344 *pcbRead = cbRead;
345 }
346 }
347 else
348 {
349 RTThreadSleep(100);
350 *pcbRead = 0;
351 }
352
353 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
354 return rc;
355}
356
357
358/** @interface_method_impl{PDMISTREAM,pfnWrite} */
359static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
360{
361 int rc = VINF_SUCCESS;
362 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
363 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
364
365 Assert(pvBuf);
366 if (pThis->hTcpSock != NIL_RTSOCKET)
367 {
368 size_t cbBuf = *pcbWrite;
369 rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite);
370 if (rc == VINF_TRY_AGAIN)
371 {
372 Assert(*pcbWrite == 0);
373 pThis->fXmitBufFull = true;
374 rc = VERR_TIMEOUT;
375 }
376 }
377 /* else Just pretend we wrote everything to not block. */
378
379 LogFlow(("%s: returns %Rrc *pcbWrite=%zu\n", __FUNCTION__, rc, *pcbWrite));
380 return rc;
381}
382
383
384/**
385 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
386 */
387static DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
388{
389 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
390 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
391 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
392 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
393 return NULL;
394}
395
396
397/* -=-=-=-=- listen thread -=-=-=-=- */
398
399/**
400 * Receive thread loop.
401 *
402 * @returns VINF_SUCCESS
403 * @param hThreadSelf Thread handle to this thread.
404 * @param pvUser User argument.
405 */
406static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser)
407{
408 RT_NOREF(hThreadSelf);
409 PDRVTCP pThis = (PDRVTCP)pvUser;
410
411 while (RT_LIKELY(!pThis->fShutdown))
412 {
413 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
414 int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
415 if (RT_SUCCESS(rc))
416 {
417 if (ASMAtomicReadU32(&pThis->cConnections) > 0)
418 {
419 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
420 RTTcpServerDisconnectClient2(hTcpSockNew);
421 }
422 else
423 {
424 ASMAtomicIncU32(&pThis->cConnections);
425
426 /* Inform the poller about the new socket. */
427 drvTcpPollerKickEx(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION, &hTcpSockNew, sizeof(hTcpSockNew));
428 }
429 }
430 }
431
432 return VINF_SUCCESS;
433}
434
435/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
436
437/**
438 * Common worker for drvTCPPowerOff and drvTCPDestructor.
439 *
440 * @param pThis The instance data.
441 */
442static void drvTCPShutdownListener(PDRVTCP pThis)
443{
444 /*
445 * Signal shutdown of the listener thread.
446 */
447 pThis->fShutdown = true;
448 if ( pThis->fIsServer
449 && pThis->hTcpServ != NULL)
450 {
451 int rc = RTTcpServerShutdown(pThis->hTcpServ);
452 AssertRC(rc);
453 pThis->hTcpServ = NULL;
454 }
455}
456
457
458/**
459 * Power off a TCP socket stream driver instance.
460 *
461 * This does most of the destruction work, to avoid ordering dependencies.
462 *
463 * @param pDrvIns The driver instance data.
464 */
465static DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
466{
467 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
468 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
469
470 drvTCPShutdownListener(pThis);
471}
472
473
474/**
475 * Destruct a TCP socket stream driver instance.
476 *
477 * Most VM resources are freed by the VM. This callback is provided so that
478 * any non-VM resources can be freed correctly.
479 *
480 * @param pDrvIns The driver instance data.
481 */
482static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
483{
484 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
485 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
486 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
487
488 drvTCPShutdownListener(pThis);
489
490 /*
491 * While the thread exits, clean up as much as we can.
492 */
493 if (pThis->hTcpSock != NIL_RTSOCKET)
494 {
495 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
496 AssertRC(rc);
497
498 rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */);
499 AssertRC(rc);
500
501 rc = RTSocketClose(pThis->hTcpSock);
502 AssertRC(rc); RT_NOREF(rc);
503
504 pThis->hTcpSock = NIL_RTSOCKET;
505 }
506
507 if (pThis->hPipeWakeR != NIL_RTPIPE)
508 {
509 int rc = RTPipeClose(pThis->hPipeWakeR);
510 AssertRC(rc);
511
512 pThis->hPipeWakeR = NIL_RTPIPE;
513 }
514
515 if (pThis->hPipeWakeW != NIL_RTPIPE)
516 {
517 int rc = RTPipeClose(pThis->hPipeWakeW);
518 AssertRC(rc);
519
520 pThis->hPipeWakeW = NIL_RTPIPE;
521 }
522
523 if (pThis->hPollSet != NIL_RTPOLLSET)
524 {
525 int rc = RTPollSetDestroy(pThis->hPollSet);
526 AssertRC(rc);
527
528 pThis->hPollSet = NIL_RTPOLLSET;
529 }
530
531 MMR3HeapFree(pThis->pszLocation);
532 pThis->pszLocation = NULL;
533
534 /*
535 * Wait for the thread.
536 */
537 if (pThis->ListenThread != NIL_RTTHREAD)
538 {
539 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
540 if (RT_SUCCESS(rc))
541 pThis->ListenThread = NIL_RTTHREAD;
542 else
543 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
544 }
545}
546
547
548/**
549 * Construct a TCP socket stream driver instance.
550 *
551 * @copydoc FNPDMDRVCONSTRUCT
552 */
553static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
554{
555 RT_NOREF(fFlags);
556 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
557 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
558
559 /*
560 * Init the static parts.
561 */
562 pThis->pDrvIns = pDrvIns;
563 pThis->pszLocation = NULL;
564 pThis->fIsServer = false;
565 pThis->fXmitBufFull = false;
566 pThis->cConnections = 0;
567
568 pThis->hTcpServ = NULL;
569 pThis->hTcpSock = NIL_RTSOCKET;
570
571 pThis->hPollSet = NIL_RTPOLLSET;
572 pThis->hPipeWakeR = NIL_RTPIPE;
573 pThis->hPipeWakeW = NIL_RTPIPE;
574
575 pThis->ListenThread = NIL_RTTHREAD;
576 pThis->fShutdown = false;
577 pThis->fWokenUp = false;
578 /* IBase */
579 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
580 /* IStream */
581 pThis->IStream.pfnPoll = drvTcpPoll;
582 pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt;
583 pThis->IStream.pfnRead = drvTcpRead;
584 pThis->IStream.pfnWrite = drvTcpWrite;
585
586 /*
587 * Validate and read the configuration.
588 */
589 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
590
591 int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
592 if (RT_FAILURE(rc))
593 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
594 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
595 rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
596 if (RT_FAILURE(rc))
597 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
598 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
599
600 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
601 if (RT_FAILURE(rc))
602 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
603 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
604
605 rc = RTPollSetCreate(&pThis->hPollSet);
606 if (RT_FAILURE(rc))
607 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
608 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
609
610 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
611 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
612 DRVTCP_POLLSET_ID_WAKEUP);
613 if (RT_FAILURE(rc))
614 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
615 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
616 pDrvIns->iInstance, pThis->pszLocation);
617
618 /*
619 * Create/Open the socket.
620 */
621 if (pThis->fIsServer)
622 {
623 uint32_t uPort = 0;
624 rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
625 if (RT_FAILURE(rc))
626 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
627 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
628 pDrvIns->iInstance);
629
630 /** @todo Allow binding to distinct interfaces. */
631 rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
632 if (RT_FAILURE(rc))
633 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
634 N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);
635
636 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
637 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
638 if (RT_FAILURE(rc))
639 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
640 N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
641 }
642 else
643 {
644 char *pszPort = strchr(pThis->pszLocation, ':');
645 if (!pszPort)
646 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
647 N_("DrvTCP#%d: The location misses the port to connect to"),
648 pDrvIns->iInstance);
649
650 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
651 uint32_t uPort = 0;
652 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
653 if (RT_FAILURE(rc))
654 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
655 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
656 pDrvIns->iInstance);
657
658 rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
659 *pszPort = ':'; /* Restore delimiter before checking the status. */
660 if (RT_FAILURE(rc))
661 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
662 N_("DrvTCP#%d failed to connect to socket %s"),
663 pDrvIns->iInstance, pThis->pszLocation);
664
665 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
666 RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
667 DRVTCP_POLLSET_ID_SOCKET);
668 if (RT_FAILURE(rc))
669 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
670 N_("DrvTCP#%d failed to add socket for %s to poll set"),
671 pDrvIns->iInstance, pThis->pszLocation);
672
673 ASMAtomicIncU32(&pThis->cConnections);
674 }
675
676 LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
677 return VINF_SUCCESS;
678}
679
680
681/**
682 * TCP stream driver registration record.
683 */
684const PDMDRVREG g_DrvTCP =
685{
686 /* u32Version */
687 PDM_DRVREG_VERSION,
688 /* szName */
689 "TCP",
690 /* szRCMod */
691 "",
692 /* szR0Mod */
693 "",
694 /* pszDescription */
695 "TCP serial stream driver.",
696 /* fFlags */
697 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
698 /* fClass. */
699 PDM_DRVREG_CLASS_STREAM,
700 /* cMaxInstances */
701 ~0U,
702 /* cbInstance */
703 sizeof(DRVTCP),
704 /* pfnConstruct */
705 drvTCPConstruct,
706 /* pfnDestruct */
707 drvTCPDestruct,
708 /* pfnRelocate */
709 NULL,
710 /* pfnIOCtl */
711 NULL,
712 /* pfnPowerOn */
713 NULL,
714 /* pfnReset */
715 NULL,
716 /* pfnSuspend */
717 NULL,
718 /* pfnResume */
719 NULL,
720 /* pfnAttach */
721 NULL,
722 /* pfnDetach */
723 NULL,
724 /* pfnPowerOff */
725 drvTCPPowerOff,
726 /* pfnSoftReset */
727 NULL,
728 /* u32EndVersion */
729 PDM_DRVREG_VERSION
730};
731
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