VirtualBox

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

Last change on this file since 91071 was 91034, checked in by vboxsync, 3 years ago

Audio/Validation Kit: More code for resolving the connection problems. ​bugref:10008

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