VirtualBox

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

Last change on this file was 103425, checked in by vboxsync, 2 months ago

Devices/Serial: Fix some warnings, parfait:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use