VirtualBox

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

Last change on this file since 84157 was 84157, checked in by vboxsync, 5 years ago

Devices/Serial/DrvTCP: Don't block the guest if there is no one connected in server mode because the serial write buffer is full and doesn't get drained

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