VirtualBox

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

Last change on this file since 90911 was 90887, checked in by vboxsync, 3 years ago

Audio/VKAT: More code for TCP/IP connection mode handling; don't do things less implicitly but let the caller (or rather test driver) choose how to connect to the guest/host instead. ​bugref:10008

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