VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestServiceTcp.cpp@ 90012

Last change on this file since 90012 was 89962, checked in by vboxsync, 3 years ago

Audio/ValKit: Initial implementation / support for NATed VMs by using reversed (server) connections. The ATS client now also makes use of the transport layer and now can also be configured more flexible on a per-transport layer basis. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.6 KB
Line 
1/* $Id: AudioTestServiceTcp.cpp 89962 2021-06-30 07:02:07Z vboxsync $ */
2/** @file
3 * AudioTestServiceTcp - Audio test execution server, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO /** @todo Add an own log group for this? */
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#include <iprt/critsect.h>
26#include <iprt/err.h>
27#include <iprt/log.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/poll.h>
31#include <iprt/string.h>
32#include <iprt/tcp.h>
33#include <iprt/thread.h>
34#include <iprt/time.h>
35
36#include <VBox/log.h>
37
38#include "AudioTestService.h"
39#include "AudioTestServiceInternal.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50/**
51 * TCP specific client data.
52 */
53typedef struct ATSTRANSPORTCLIENT
54{
55 /** Socket of the current client. */
56 RTSOCKET hTcpClient;
57 /** Indicates whether \a hTcpClient comes from the server or from a client
58 * connect (relevant when closing it). */
59 bool fFromServer;
60 /** The size of the stashed data. */
61 size_t cbTcpStashed;
62 /** The size of the stashed data allocation. */
63 size_t cbTcpStashedAlloced;
64 /** The stashed data. */
65 uint8_t *pbTcpStashed;
66} ATSTRANSPORTCLIENT;
67
68/**
69 * Enumeration for the TCP/IP connection mode.
70 */
71typedef enum ATSTCPMODE
72{
73 /** Both: Uses parallel client and server connection methods (via threads). */
74 ATSTCPMODE_BOTH = 0,
75 /** Client only: Connects to a server. */
76 ATSTCPMODE_CLIENT,
77 /** Server only: Listens for new incoming client connections. */
78 ATSTCPMODE_SERVER
79} ATSTCPMODE;
80
81/**
82 * Structure for keeping Audio Test Service (ATS) transport instance-specific data.
83 */
84typedef struct ATSTRANSPORTINST
85{
86 /** Critical section for serializing access. */
87 RTCRITSECT CritSect;
88 /** Connection mode to use. */
89 ATSTCPMODE enmMode;
90 /** The addresses to bind to. Empty string means any. */
91 char szBindAddr[256];
92 /** The TCP port to listen to. */
93 uint32_t uBindPort;
94 /** The addresses to connect to if running in reversed (VM NATed) mode. */
95 char szConnectAddr[256];
96 /** The TCP port to connect to if running in reversed (VM NATed) mode. */
97 uint32_t uConnectPort;
98 /** Pointer to the TCP server instance. */
99 PRTTCPSERVER pTcpServer;
100 /** Thread calling RTTcpServerListen2. */
101 RTTHREAD hThreadServer;
102 /** Thread calling RTTcpClientConnect. */
103 RTTHREAD hThreadConnect;
104 /** The main thread handle (for signalling). */
105 RTTHREAD hThreadMain;
106 /** Stop connecting attempts when set. */
107 bool fStopConnecting;
108 /** Connect cancel cookie. */
109 PRTTCPCLIENTCONNECTCANCEL volatile pConnectCancelCookie;
110} ATSTRANSPORTINST;
111/** Pointer to an Audio Test Service (ATS) TCP/IP transport instance. */
112typedef ATSTRANSPORTINST *PATSTRANSPORTINST;
113
114/**
115 * Structure holding an ATS connection context, which is
116 * required when connecting a client via server (listening) or client (connecting).
117 */
118typedef struct ATSCONNCTX
119{
120 /** Pointer to transport instance to use. */
121 PATSTRANSPORTINST pInst;
122 /** Pointer to transport client to connect. */
123 PATSTRANSPORTCLIENT pClient;
124} ATSCONNCTX;
125/** Pointer to an Audio Test Service (ATS) TCP/IP connection context. */
126typedef ATSCONNCTX *PATSCONNCTX;
127
128
129/*********************************************************************************************************************************
130* Global Variables *
131*********************************************************************************************************************************/
132
133/**
134 * Disconnects the current client and frees all stashed data.
135 */
136static void atsTcpDisconnectClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
137{
138 RT_NOREF(pThis);
139
140 if (pClient->hTcpClient != NIL_RTSOCKET)
141 {
142 int rc;
143 if (pClient->fFromServer)
144 rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
145 else
146 rc = RTTcpClientClose(pClient->hTcpClient);
147 pClient->hTcpClient = NIL_RTSOCKET;
148 AssertRCSuccess(rc);
149 }
150
151 if (pClient->pbTcpStashed)
152 {
153 RTMemFree(pClient->pbTcpStashed);
154 pClient->pbTcpStashed = NULL;
155 }
156}
157
158/**
159 * Sets the current client socket in a safe manner.
160 *
161 * @returns NIL_RTSOCKET if consumed, other wise hTcpClient.
162 * @param pThis Transport instance.
163 * @param pClient Client to set the socket for.
164 * @param fFromServer Whether the socket is from a server (listening) or client (connecting) call.
165 * Important when closing / disconnecting.
166 * @param hTcpClient The client socket.
167 */
168static RTSOCKET atsTcpSetClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, bool fFromServer, RTSOCKET hTcpClient)
169{
170 RTCritSectEnter(&pThis->CritSect);
171 if ( pClient->hTcpClient == NIL_RTSOCKET
172 && !pThis->fStopConnecting)
173 {
174 LogFunc(("New client connected\n"));
175
176 pClient->fFromServer = fFromServer;
177 pClient->hTcpClient = hTcpClient;
178 hTcpClient = NIL_RTSOCKET; /* Invalidate, as pClient has now ownership. */
179 }
180 RTCritSectLeave(&pThis->CritSect);
181 return hTcpClient;
182}
183
184/**
185 * Checks if it's a fatal RTTcpClientConnect return code.
186 *
187 * @returns true / false.
188 * @param rc The IPRT status code.
189 */
190static bool atsTcpIsFatalClientConnectStatus(int rc)
191{
192 return rc != VERR_NET_UNREACHABLE
193 && rc != VERR_NET_HOST_DOWN
194 && rc != VERR_NET_HOST_UNREACHABLE
195 && rc != VERR_NET_CONNECTION_REFUSED
196 && rc != VERR_TIMEOUT
197 && rc != VERR_NET_CONNECTION_TIMED_OUT;
198}
199
200/**
201 * Server mode connection thread.
202 *
203 * @returns iprt status code.
204 * @param hSelf Thread handle. Ignored.
205 * @param pvUser Pointer to ATSTRANSPORTINST the thread is bound to.
206 */
207static DECLCALLBACK(int) atsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
208{
209 RT_NOREF(hSelf);
210
211 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
212 PATSTRANSPORTINST pThis = pConnCtx->pInst;
213 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
214
215 RTSOCKET hTcpClient;
216 int rc = RTTcpServerListen2(pThis->pTcpServer, &hTcpClient);
217 if (RT_SUCCESS(rc))
218 {
219 hTcpClient = atsTcpSetClient(pThis, pClient, true /* fFromServer */, hTcpClient);
220 RTTcpServerDisconnectClient2(hTcpClient);
221 }
222
223 return rc;
224}
225
226/**
227 * Client mode connection thread.
228 *
229 * @returns iprt status code.
230 * @param hSelf Thread handle. Use to sleep on. The main thread will
231 * signal it to speed up thread shutdown.
232 * @param pvUser Pointer to a connection context (PATSCONNCTX) the thread is bound to.
233 */
234static DECLCALLBACK(int) atsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser)
235{
236 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
237 PATSTRANSPORTINST pThis = pConnCtx->pInst;
238 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
239
240 for (;;)
241 {
242 /* Stop? */
243 RTCritSectEnter(&pThis->CritSect);
244 bool fStop = pThis->fStopConnecting;
245 RTCritSectLeave(&pThis->CritSect);
246 if (fStop)
247 return VINF_SUCCESS;
248
249 /* Try connect. */ /** @todo make cancelable! */
250 RTSOCKET hTcpClient;
251 int rc = RTTcpClientConnectEx(pThis->szConnectAddr, pThis->uConnectPort, &hTcpClient,
252 RT_SOCKETCONNECT_DEFAULT_WAIT, &pThis->pConnectCancelCookie);
253 if (RT_SUCCESS(rc))
254 {
255 hTcpClient = atsTcpSetClient(pThis, pClient, false /* fFromServer */, hTcpClient);
256 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
257 break;
258 }
259
260 if (atsTcpIsFatalClientConnectStatus(rc))
261 return rc;
262
263 /* Delay a wee bit before retrying. */
264 RTThreadUserWait(hSelf, 1536);
265 }
266 return VINF_SUCCESS;
267}
268
269/**
270 * Wait on the threads to complete.
271 *
272 * @returns Thread status (if collected), otherwise VINF_SUCCESS.
273 * @param pThis Transport instance.
274 * @param cMillies The period to wait on each thread.
275 */
276static int atsTcpConnectWaitOnThreads(PATSTRANSPORTINST pThis, RTMSINTERVAL cMillies)
277{
278 int rcRet = VINF_SUCCESS;
279
280 if (pThis->hThreadConnect != NIL_RTTHREAD)
281 {
282 int rcThread;
283 int rc2 = RTThreadWait(pThis->hThreadConnect, cMillies, &rcThread);
284 if (RT_SUCCESS(rc2))
285 {
286 pThis->hThreadConnect = NIL_RTTHREAD;
287 rcRet = rcThread;
288 }
289 }
290
291 if (pThis->hThreadServer != NIL_RTTHREAD)
292 {
293 int rcThread;
294 int rc2 = RTThreadWait(pThis->hThreadServer, cMillies, &rcThread);
295 if (RT_SUCCESS(rc2))
296 {
297 pThis->hThreadServer = NIL_RTTHREAD;
298 if (RT_SUCCESS(rc2))
299 rcRet = rcThread;
300 }
301 }
302 return rcRet;
303}
304
305/**
306 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
307 */
308static DECLCALLBACK(int) atsTcpWaitForConnect(PATSTRANSPORTINST pThis, PPATSTRANSPORTCLIENT ppClientNew)
309{
310 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
311 AssertPtrReturn(pClient, VERR_NO_MEMORY);
312
313 int rc;
314
315 if (pThis->enmMode == ATSTCPMODE_SERVER)
316 {
317 pClient->fFromServer = true;
318 rc = RTTcpServerListen2(pThis->pTcpServer, &pClient->hTcpClient);
319 LogFunc(("RTTcpServerListen2 -> %Rrc\n", rc));
320 }
321 else if (pThis->enmMode == ATSTCPMODE_CLIENT)
322 {
323 pClient->fFromServer = false;
324 for (;;)
325 {
326 Log2Func(("Calling RTTcpClientConnect(%s, %u,)...\n", pThis->szConnectAddr, pThis->uConnectPort));
327 rc = RTTcpClientConnect(pThis->szConnectAddr, pThis->uConnectPort, &pClient->hTcpClient);
328 LogFunc(("RTTcpClientConnect -> %Rrc\n", rc));
329 if (RT_SUCCESS(rc) || atsTcpIsFatalClientConnectStatus(rc))
330 break;
331
332 /* Delay a wee bit before retrying. */
333 RTThreadSleep(1536);
334 }
335 }
336 else
337 {
338 Assert(pThis->enmMode == ATSTCPMODE_BOTH);
339
340 /*
341 * Create client threads.
342 */
343 RTCritSectEnter(&pThis->CritSect);
344
345 pThis->fStopConnecting = false;
346 RTCritSectLeave(&pThis->CritSect);
347
348 atsTcpConnectWaitOnThreads(pThis, 32 /* cMillies */);
349
350 ATSCONNCTX ConnCtx;
351 RT_ZERO(ConnCtx);
352 ConnCtx.pInst = pThis;
353 ConnCtx.pClient = pClient;
354
355 rc = VINF_SUCCESS;
356 if (pThis->hThreadConnect == NIL_RTTHREAD)
357 {
358 pThis->pConnectCancelCookie = NULL;
359 rc = RTThreadCreate(&pThis->hThreadConnect, atsTcpClientConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
360 RTTHREADFLAGS_WAITABLE, "tcpconn");
361 }
362 if (pThis->hThreadServer == NIL_RTTHREAD && RT_SUCCESS(rc))
363 rc = RTThreadCreate(&pThis->hThreadServer, atsTcpServerConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
364 RTTHREADFLAGS_WAITABLE, "tcpserv");
365
366 RTCritSectEnter(&pThis->CritSect);
367
368 /*
369 * Wait for connection to be established.
370 */
371 while ( RT_SUCCESS(rc)
372 && pClient->hTcpClient == NIL_RTSOCKET)
373 {
374 RTCritSectLeave(&pThis->CritSect);
375 rc = atsTcpConnectWaitOnThreads(pThis, 10 /* cMillies */);
376 RTCritSectEnter(&pThis->CritSect);
377 }
378
379 /*
380 * Cancel the threads.
381 */
382 pThis->fStopConnecting = true;
383
384 RTCritSectLeave(&pThis->CritSect);
385 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
386 }
387
388 if (RT_SUCCESS(rc))
389 {
390 *ppClientNew = pClient;
391 }
392 else
393 {
394 if (pClient)
395 {
396 RTTcpServerDisconnectClient2(pClient->hTcpClient);
397
398 RTMemFree(pClient);
399 pClient = NULL;
400 }
401 }
402
403 return rc;
404}
405
406/**
407 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
408 */
409static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
410{
411 LogFunc(("RTTcpServerDestroy(%p)\n", pThis->pTcpServer));
412 if (pThis->pTcpServer)
413 {
414 int rc = RTTcpServerDestroy(pThis->pTcpServer);
415 if (RT_FAILURE(rc))
416 RTMsgInfo("RTTcpServerDestroy failed in atsTcpNotifyReboot: %Rrc", rc);
417 pThis->pTcpServer = NULL;
418 }
419}
420
421/**
422 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
423 */
424static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
425{
426 LogFunc(("atsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
427 atsTcpDisconnectClient(pThis, pClient);
428 RTMemFree(pClient);
429}
430
431/**
432 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
433 */
434static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
435{
436 /* nothing to do here */
437 RT_NOREF(pThis, pClient);
438}
439
440/**
441 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
442 */
443static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
444{
445 /*
446 * Try send the babble reply.
447 */
448 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
449 int rc;
450 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
451 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
452 while (rc == VERR_INTERRUPTED);
453
454 /*
455 * Disconnect the client.
456 */
457 LogFunc(("atsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
458 atsTcpDisconnectClient(pThis, pClient);
459}
460
461/**
462 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
463 */
464static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
465{
466 AssertReturn(pPktHdr->cb >= sizeof(ATSPKTHDR), VERR_INVALID_PARAMETER);
467
468 /*
469 * Write it.
470 */
471 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
472
473 Log3Func(("%RU32 -> %zu\n", pPktHdr->cb, cbToSend));
474
475 Log3Func(("Header:\n"
476 "%.*Rhxd\n", RT_MIN(sizeof(ATSPKTHDR), cbToSend), pPktHdr));
477
478 if (cbToSend > sizeof(ATSPKTHDR))
479 Log3Func(("Payload:\n"
480 "%.*Rhxd\n",
481 RT_MIN(64, cbToSend - sizeof(ATSPKTHDR)), (uint8_t *)pPktHdr + sizeof(ATSPKTHDR)));
482
483 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
484 if ( RT_FAILURE(rc)
485 && rc != VERR_INTERRUPTED)
486 {
487 /* assume fatal connection error. */
488 LogFunc(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
489 atsTcpDisconnectClient(pThis, pClient);
490 }
491
492 return rc;
493}
494
495/**
496 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
497 */
498static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
499{
500 int rc = VINF_SUCCESS;
501 *ppPktHdr = NULL;
502
503 /*
504 * Read state.
505 */
506 size_t offData = 0;
507 size_t cbData = 0;
508 size_t cbDataAlloced;
509 uint8_t *pbData = NULL;
510
511 /*
512 * Any stashed data?
513 */
514 if (pClient->cbTcpStashedAlloced)
515 {
516 offData = pClient->cbTcpStashed;
517 cbDataAlloced = pClient->cbTcpStashedAlloced;
518 pbData = pClient->pbTcpStashed;
519
520 pClient->cbTcpStashed = 0;
521 pClient->cbTcpStashedAlloced = 0;
522 pClient->pbTcpStashed = NULL;
523 }
524 else
525 {
526 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
527 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
528 if (!pbData)
529 return VERR_NO_MEMORY;
530 }
531
532 /*
533 * Read and validate the length.
534 */
535 while (offData < sizeof(uint32_t))
536 {
537 size_t cbRead;
538 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
539 if (RT_FAILURE(rc))
540 break;
541 if (cbRead == 0)
542 {
543 LogFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
544 rc = VERR_NET_NOT_CONNECTED;
545 break;
546 }
547 offData += cbRead;
548 }
549 if (RT_SUCCESS(rc))
550 {
551 ASMCompilerBarrier(); /* paranoia^3 */
552 cbData = *(uint32_t volatile *)pbData;
553 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
554 {
555 /*
556 * Align the length and reallocate the return packet it necessary.
557 */
558 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
559 if (cbData > cbDataAlloced)
560 {
561 void *pvNew = RTMemRealloc(pbData, cbData);
562 if (pvNew)
563 {
564 pbData = (uint8_t *)pvNew;
565 cbDataAlloced = cbData;
566 }
567 else
568 rc = VERR_NO_MEMORY;
569 }
570 if (RT_SUCCESS(rc))
571 {
572 /*
573 * Read the remainder of the data.
574 */
575 while (offData < cbData)
576 {
577 size_t cbRead;
578 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
579 if (RT_FAILURE(rc))
580 break;
581 if (cbRead == 0)
582 {
583 LogFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
584 rc = VERR_NET_NOT_CONNECTED;
585 break;
586 }
587
588 offData += cbRead;
589 }
590
591 Log3Func(("Header:\n"
592 "%.*Rhxd\n", sizeof(ATSPKTHDR), pbData));
593
594 if ( RT_SUCCESS(rc)
595 && cbData > sizeof(ATSPKTHDR))
596 Log3Func(("Payload:\n"
597 "%.*Rhxd\n", RT_MIN(64, cbData - sizeof(ATSPKTHDR)), (uint8_t *)pbData + sizeof(ATSPKTHDR)));
598 }
599 }
600 else
601 rc = VERR_NET_PROTOCOL_ERROR;
602 }
603 if (RT_SUCCESS(rc))
604 *ppPktHdr = (PATSPKTHDR)pbData;
605 else
606 {
607 /*
608 * Deal with errors.
609 */
610 if (rc == VERR_INTERRUPTED)
611 {
612 /* stash it away for the next call. */
613 pClient->cbTcpStashed = cbData;
614 pClient->cbTcpStashedAlloced = cbDataAlloced;
615 pClient->pbTcpStashed = pbData;
616 }
617 else
618 {
619 RTMemFree(pbData);
620
621 /* assume fatal connection error. */
622 LogFunc(("RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
623 atsTcpDisconnectClient(pThis, pClient);
624 }
625 }
626
627 return rc;
628}
629
630/**
631 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
632 */
633static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
634{
635 RT_NOREF(pThis);
636 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
637}
638
639/**
640 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
641 */
642static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
643{
644 RT_NOREF(pThis, pClient);
645 return RTPollSetRemove(hPollSet, idStart);
646}
647
648/**
649 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
650 */
651static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
652{
653 RT_NOREF(pThis);
654 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
655 return RT_SUCCESS(rc);
656}
657
658/**
659 * @interface_method_impl{ATSTRANSPORT,pfnTerm}
660 */
661static DECLCALLBACK(void) atsTcpTerm(PATSTRANSPORTINST pThis)
662{
663 /* Signal thread */
664 if (RTCritSectIsInitialized(&pThis->CritSect))
665 {
666 RTCritSectEnter(&pThis->CritSect);
667 pThis->fStopConnecting = true;
668 RTCritSectLeave(&pThis->CritSect);
669 }
670
671 if (pThis->hThreadConnect != NIL_RTTHREAD)
672 {
673 RTThreadUserSignal(pThis->hThreadConnect);
674 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
675 }
676
677 /* Shut down the server (will wake up thread). */
678 if (pThis->pTcpServer)
679 {
680 LogFunc(("Destroying server...\n"));
681 int rc = RTTcpServerDestroy(pThis->pTcpServer);
682 if (RT_FAILURE(rc))
683 RTMsgInfo("RTTcpServerDestroy failed in atsTcpTerm: %Rrc", rc);
684 pThis->pTcpServer = NULL;
685 }
686
687 /* Wait for the thread (they should've had some time to quit by now). */
688 atsTcpConnectWaitOnThreads(pThis, 15000);
689
690 /* Finally, clean up the critical section. */
691 if (RTCritSectIsInitialized(&pThis->CritSect))
692 RTCritSectDelete(&pThis->CritSect);
693
694 LogFunc(("Done\n"));
695}
696
697/**
698 * @interface_method_impl{ATSTRANSPORT,pfnCreate}
699 */
700static DECLCALLBACK(int) atsTcpCreate(PATSTRANSPORTINST *ppThis)
701{
702 PATSTRANSPORTINST pThis = (PATSTRANSPORTINST)RTMemAllocZ(sizeof(ATSTRANSPORTINST));
703 AssertPtrReturn(pThis, VERR_NO_MEMORY);
704
705 *ppThis = pThis;
706 return VINF_SUCCESS;
707}
708
709/**
710 * @interface_method_impl{ATSTRANSPORT,pfnDestroy}
711 */
712static DECLCALLBACK(int) atsTcpDestroy(PATSTRANSPORTINST pThis)
713{
714 /** @todo Anything else to do here? */
715 RTMemFree(pThis);
716
717 return VINF_SUCCESS;
718}
719
720/**
721 * @interface_method_impl{ATSTRANSPORT,pfnStart}
722 */
723static DECLCALLBACK(int) atsTcpStart(PATSTRANSPORTINST pThis)
724{
725 int rc = RTCritSectInit(&pThis->CritSect);
726 if (RT_SUCCESS(rc) && pThis->enmMode != ATSTCPMODE_CLIENT)
727 {
728 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
729 if (RT_FAILURE(rc))
730 {
731 if (rc == VERR_NET_DOWN)
732 {
733 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
734 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc);
735 uint64_t StartMs = RTTimeMilliTS();
736 do
737 {
738 RTThreadSleep(1000);
739 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
740 } while ( rc == VERR_NET_DOWN
741 && RTTimeMilliTS() - StartMs < 20000);
742 if (RT_SUCCESS(rc))
743 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
744 }
745
746 if (RT_FAILURE(rc))
747 {
748 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
749 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc);
750 }
751 }
752 }
753
754 return rc;
755}
756
757/**
758 * @interface_method_impl{ATSTRANSPORT,pfnOption}
759 */
760static DECLCALLBACK(int) atsTcpOption(PATSTRANSPORTINST pThis, int ch, PCRTGETOPTUNION pVal)
761{
762 int rc;
763
764 switch (ch)
765 {
766 case ATSTCPOPT_MODE:
767 if (!strcmp(pVal->psz, "both"))
768 pThis->enmMode = ATSTCPMODE_BOTH;
769 else if (!strcmp(pVal->psz, "client"))
770 pThis->enmMode = ATSTCPMODE_CLIENT;
771 else if (!strcmp(pVal->psz, "server"))
772 pThis->enmMode = ATSTCPMODE_SERVER;
773 else
774 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid TCP mode: '%s'\n", pVal->psz);
775 return VINF_SUCCESS;
776
777 case ATSTCPOPT_BIND_ADDRESS:
778 rc = RTStrCopy(pThis->szBindAddr, sizeof(pThis->szBindAddr), pVal->psz);
779 if (RT_FAILURE(rc))
780 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
781 return VINF_SUCCESS;
782
783 case ATSTCPOPT_BIND_PORT:
784 pThis->uBindPort = pVal->u16 == 0 ? ATS_TCP_DEF_BIND_PORT_GUEST : pVal->u16;
785 return VINF_SUCCESS;
786
787 case ATSTCPOPT_CONNECT_ADDRESS:
788 rc = RTStrCopy(pThis->szConnectAddr, sizeof(pThis->szConnectAddr), pVal->psz);
789 if (RT_FAILURE(rc))
790 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
791 if (!pThis->szConnectAddr[0])
792 strcpy(pThis->szConnectAddr, ATS_TCP_DEF_CONNECT_GUEST_STR);
793 return VINF_SUCCESS;
794
795 case ATSTCPOPT_CONNECT_PORT:
796 pThis->uConnectPort = pVal->u16 == 0 ? ATS_TCP_DEF_BIND_PORT_GUEST : pVal->u16;
797 return VINF_SUCCESS;
798
799 default:
800 break;
801 }
802 return VERR_TRY_AGAIN;
803}
804
805/**
806 * @interface_method_impl{ATSTRANSPORT,pfnUsage}
807 */
808DECLCALLBACK(void) atsTcpUsage(PRTSTREAM pStream)
809{
810 RTStrmPrintf(pStream,
811 " --tcp-mode <both|client|server>\n"
812 " Selects the mode of operation.\n"
813 " Default: both\n"
814 " --tcp-bind-address <address>\n"
815 " The address(es) to listen to TCP connection on. Empty string\n"
816 " means any address, this is the default.\n"
817 " --tcp-bind-port <port>\n"
818 " The port to listen to TCP connections on.\n"
819 " Default: %u\n"
820 " --tcp-connect-address <address>\n"
821 " The address of the server to try connect to in client mode.\n"
822 " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR "\n"
823 " --tcp-connect-port <port>\n"
824 " The port on the server to connect to in client mode.\n"
825 " Default: %u\n"
826 , ATS_TCP_DEF_BIND_PORT_GUEST, ATS_TCP_DEF_CONNECT_PORT_GUEST);
827}
828
829/** Command line options for the TCP/IP transport layer. */
830static const RTGETOPTDEF g_TcpOpts[] =
831{
832 { "--tcp-mode", ATSTCPOPT_MODE, RTGETOPT_REQ_STRING },
833 { "--tcp-bind-address", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
834 { "--tcp-bind-port", ATSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
835 { "--tcp-connect-address", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
836 { "--tcp-connect-port", ATSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 }
837};
838
839/** TCP/IP transport layer. */
840const ATSTRANSPORT g_TcpTransport =
841{
842 /* .szName = */ "tcp",
843 /* .pszDesc = */ "TCP/IP",
844 /* .cOpts = */ &g_TcpOpts[0],
845 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
846 /* .pfnUsage = */ atsTcpUsage,
847 /* .pfnCreate = */ atsTcpCreate,
848 /* .pfnDestroy = */ atsTcpDestroy,
849 /* .pfnOption = */ atsTcpOption,
850 /* .pfnStart = */ atsTcpStart,
851 /* .pfnTerm = */ atsTcpTerm,
852 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
853 /* .pfnPollIn = */ atsTcpPollIn,
854 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
855 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
856 /* .pfnRecvPkt = */ atsTcpRecvPkt,
857 /* .pfnSendPkt = */ atsTcpSendPkt,
858 /* .pfnBabble = */ atsTcpBabble,
859 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
860 /* .pfnNotifyBye = */ atsTcpNotifyBye,
861 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
862 /* .u32EndMarker = */ UINT32_C(0x12345678)
863};
864
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