VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use