VirtualBox

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

Last change on this file since 99739 was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • 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 99739 2023-05-11 01:01:08Z 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 cMillies -= RT_MIN(cMillies, tsPollSpanMs);
280 if (RT_SUCCESS(rc))
281 {
282 if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
283 {
284 /* We got woken up, drain the pipe and return. */
285 rc = drvTcpWakeupPipeCheckForRequest(pThis, fEvts);
286 }
287 else
288 {
289 Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);
290
291 /* On error we close the socket here. */
292 if (fEvtsRecv & RTPOLL_EVT_ERROR)
293 drvTcpConnectionClose(pThis); /* Continue with polling afterwards. */
294 else
295 {
296 if (fEvtsRecv & RTPOLL_EVT_WRITE)
297 pThis->fXmitBufFull = false;
298 else if (!pThis->fXmitBufFull)
299 fEvtsRecv |= RTPOLL_EVT_WRITE;
300 *pfEvts = fEvtsRecv;
301 break;
302 }
303 }
304 }
305 else if ( rc == VERR_TIMEOUT
306 && !pThis->fXmitBufFull)
307 {
308 *pfEvts = RTPOLL_EVT_WRITE;
309 rc = VINF_SUCCESS;
310 break;
311 }
312 }
313 }
314
315 return rc;
316}
317
318
319/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
320static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface)
321{
322 int rc = VINF_SUCCESS;
323 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
324
325 if (!ASMAtomicXchgBool(&pThis->fWokenUp, true))
326 rc = drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL);
327
328 return rc;
329}
330
331
332/** @interface_method_impl{PDMISTREAM,pfnRead} */
333static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
334{
335 int rc = VINF_SUCCESS;
336 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
337 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
338
339 Assert(pvBuf);
340
341 if (pThis->hTcpSock != NIL_RTSOCKET)
342 {
343 size_t cbRead;
344 size_t cbBuf = *pcbRead;
345 rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
346 if (RT_SUCCESS(rc))
347 {
348 if (!cbRead && rc != VINF_TRY_AGAIN)
349 {
350 drvTcpConnectionClose(pThis);
351 rc = VINF_SUCCESS;
352 }
353 *pcbRead = cbRead;
354 }
355 }
356 else
357 {
358 RTThreadSleep(100);
359 *pcbRead = 0;
360 }
361
362 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
363 return rc;
364}
365
366
367/** @interface_method_impl{PDMISTREAM,pfnWrite} */
368static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
369{
370 int rc = VINF_SUCCESS;
371 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
372 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
373
374 Assert(pvBuf);
375 if (pThis->hTcpSock != NIL_RTSOCKET)
376 {
377 size_t cbBuf = *pcbWrite;
378 rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite);
379 if (rc == VINF_TRY_AGAIN)
380 {
381 Assert(*pcbWrite == 0);
382 pThis->fXmitBufFull = true;
383 rc = VERR_TIMEOUT;
384 }
385 }
386 /* else Just pretend we wrote everything to not block. */
387
388 LogFlow(("%s: returns %Rrc *pcbWrite=%zu\n", __FUNCTION__, rc, *pcbWrite));
389 return rc;
390}
391
392
393/**
394 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
395 */
396static DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
397{
398 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
399 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
400 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
401 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
402 return NULL;
403}
404
405
406/* -=-=-=-=- listen thread -=-=-=-=- */
407
408/**
409 * Receive thread loop.
410 *
411 * @returns VINF_SUCCESS
412 * @param hThreadSelf Thread handle to this thread.
413 * @param pvUser User argument.
414 */
415static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser)
416{
417 RT_NOREF(hThreadSelf);
418 PDRVTCP pThis = (PDRVTCP)pvUser;
419
420 while (RT_LIKELY(!pThis->fShutdown))
421 {
422 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
423 int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
424 if (RT_SUCCESS(rc))
425 {
426 if (ASMAtomicReadU32(&pThis->cConnections) > 0)
427 {
428 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
429 RTTcpServerDisconnectClient2(hTcpSockNew);
430 }
431 else
432 {
433 ASMAtomicIncU32(&pThis->cConnections);
434
435 /* Inform the poller about the new socket. */
436 drvTcpPollerKickEx(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION, &hTcpSockNew, sizeof(hTcpSockNew));
437 }
438 }
439 }
440
441 return VINF_SUCCESS;
442}
443
444/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
445
446/**
447 * Common worker for drvTCPPowerOff and drvTCPDestructor.
448 *
449 * @param pThis The instance data.
450 */
451static void drvTCPShutdownListener(PDRVTCP pThis)
452{
453 /*
454 * Signal shutdown of the listener thread.
455 */
456 pThis->fShutdown = true;
457 if ( pThis->fIsServer
458 && pThis->hTcpServ != NULL)
459 {
460 int rc = RTTcpServerShutdown(pThis->hTcpServ);
461 AssertRC(rc);
462 pThis->hTcpServ = NULL;
463 }
464}
465
466
467/**
468 * Power off a TCP socket stream driver instance.
469 *
470 * This does most of the destruction work, to avoid ordering dependencies.
471 *
472 * @param pDrvIns The driver instance data.
473 */
474static DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
475{
476 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
477 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
478
479 drvTCPShutdownListener(pThis);
480}
481
482
483/**
484 * Destruct a TCP socket stream driver instance.
485 *
486 * Most VM resources are freed by the VM. This callback is provided so that
487 * any non-VM resources can be freed correctly.
488 *
489 * @param pDrvIns The driver instance data.
490 */
491static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
492{
493 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
494 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
495 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
496
497 drvTCPShutdownListener(pThis);
498
499 /*
500 * While the thread exits, clean up as much as we can.
501 */
502 if (pThis->hTcpSock != NIL_RTSOCKET)
503 {
504 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
505 AssertRC(rc);
506
507 rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */);
508 AssertRC(rc);
509
510 rc = RTSocketClose(pThis->hTcpSock);
511 AssertRC(rc); RT_NOREF(rc);
512
513 pThis->hTcpSock = NIL_RTSOCKET;
514 }
515
516 if (pThis->hPipeWakeR != NIL_RTPIPE)
517 {
518 int rc = RTPipeClose(pThis->hPipeWakeR);
519 AssertRC(rc);
520
521 pThis->hPipeWakeR = NIL_RTPIPE;
522 }
523
524 if (pThis->hPipeWakeW != NIL_RTPIPE)
525 {
526 int rc = RTPipeClose(pThis->hPipeWakeW);
527 AssertRC(rc);
528
529 pThis->hPipeWakeW = NIL_RTPIPE;
530 }
531
532 if (pThis->hPollSet != NIL_RTPOLLSET)
533 {
534 int rc = RTPollSetDestroy(pThis->hPollSet);
535 AssertRC(rc);
536
537 pThis->hPollSet = NIL_RTPOLLSET;
538 }
539
540 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszLocation);
541 pThis->pszLocation = NULL;
542
543 /*
544 * Wait for the thread.
545 */
546 if (pThis->ListenThread != NIL_RTTHREAD)
547 {
548 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
549 if (RT_SUCCESS(rc))
550 pThis->ListenThread = NIL_RTTHREAD;
551 else
552 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
553 }
554}
555
556
557/**
558 * Construct a TCP socket stream driver instance.
559 *
560 * @copydoc FNPDMDRVCONSTRUCT
561 */
562static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
563{
564 RT_NOREF(fFlags);
565 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
566 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
567 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
568
569 /*
570 * Init the static parts.
571 */
572 pThis->pDrvIns = pDrvIns;
573 pThis->pszLocation = NULL;
574 pThis->fIsServer = false;
575 pThis->fXmitBufFull = false;
576 pThis->cConnections = 0;
577
578 pThis->hTcpServ = NULL;
579 pThis->hTcpSock = NIL_RTSOCKET;
580
581 pThis->hPollSet = NIL_RTPOLLSET;
582 pThis->hPipeWakeR = NIL_RTPIPE;
583 pThis->hPipeWakeW = NIL_RTPIPE;
584
585 pThis->ListenThread = NIL_RTTHREAD;
586 pThis->fShutdown = false;
587 pThis->fWokenUp = false;
588 /* IBase */
589 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
590 /* IStream */
591 pThis->IStream.pfnPoll = drvTcpPoll;
592 pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt;
593 pThis->IStream.pfnRead = drvTcpRead;
594 pThis->IStream.pfnWrite = drvTcpWrite;
595
596 /*
597 * Validate and read the configuration.
598 */
599 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
600
601 int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
602 if (RT_FAILURE(rc))
603 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
604 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
605 rc = pHlp->pfnCFGMQueryBool(pCfg, "IsServer", &pThis->fIsServer);
606 if (RT_FAILURE(rc))
607 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
608 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
609
610 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
611 if (RT_FAILURE(rc))
612 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
613 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
614
615 rc = RTPollSetCreate(&pThis->hPollSet);
616 if (RT_FAILURE(rc))
617 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
618 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
619
620 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
621 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
622 DRVTCP_POLLSET_ID_WAKEUP);
623 if (RT_FAILURE(rc))
624 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
625 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
626 pDrvIns->iInstance, pThis->pszLocation);
627
628 /*
629 * Create/Open the socket.
630 */
631 if (pThis->fIsServer)
632 {
633 uint32_t uPort = 0;
634 rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
635 if (RT_FAILURE(rc))
636 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
637 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
638 pDrvIns->iInstance);
639
640 /** @todo Allow binding to distinct interfaces. */
641 rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
642 if (RT_FAILURE(rc))
643 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
644 N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);
645
646 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
647 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
648 if (RT_FAILURE(rc))
649 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
650 N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
651 }
652 else
653 {
654 char *pszPort = strchr(pThis->pszLocation, ':');
655 if (!pszPort)
656 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
657 N_("DrvTCP#%d: The location misses the port to connect to"),
658 pDrvIns->iInstance);
659
660 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
661 uint32_t uPort = 0;
662 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
663 if (RT_FAILURE(rc))
664 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
665 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
666 pDrvIns->iInstance);
667
668 rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
669 *pszPort = ':'; /* Restore delimiter before checking the status. */
670 if (RT_FAILURE(rc))
671 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
672 N_("DrvTCP#%d failed to connect to socket %s"),
673 pDrvIns->iInstance, pThis->pszLocation);
674
675 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
676 RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
677 DRVTCP_POLLSET_ID_SOCKET);
678 if (RT_FAILURE(rc))
679 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
680 N_("DrvTCP#%d failed to add socket for %s to poll set"),
681 pDrvIns->iInstance, pThis->pszLocation);
682
683 ASMAtomicIncU32(&pThis->cConnections);
684 }
685
686 LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
687 return VINF_SUCCESS;
688}
689
690
691/**
692 * TCP stream driver registration record.
693 */
694const PDMDRVREG g_DrvTCP =
695{
696 /* u32Version */
697 PDM_DRVREG_VERSION,
698 /* szName */
699 "TCP",
700 /* szRCMod */
701 "",
702 /* szR0Mod */
703 "",
704 /* pszDescription */
705 "TCP serial stream driver.",
706 /* fFlags */
707 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
708 /* fClass. */
709 PDM_DRVREG_CLASS_STREAM,
710 /* cMaxInstances */
711 ~0U,
712 /* cbInstance */
713 sizeof(DRVTCP),
714 /* pfnConstruct */
715 drvTCPConstruct,
716 /* pfnDestruct */
717 drvTCPDestruct,
718 /* pfnRelocate */
719 NULL,
720 /* pfnIOCtl */
721 NULL,
722 /* pfnPowerOn */
723 NULL,
724 /* pfnReset */
725 NULL,
726 /* pfnSuspend */
727 NULL,
728 /* pfnResume */
729 NULL,
730 /* pfnAttach */
731 NULL,
732 /* pfnDetach */
733 NULL,
734 /* pfnPowerOff */
735 drvTCPPowerOff,
736 /* pfnSoftReset */
737 NULL,
738 /* u32EndVersion */
739 PDM_DRVREG_VERSION
740};
741
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use