VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/tcp.cpp@ 5999

Last change on this file since 5999 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.8 KB
Line 
1/* $Id: tcp.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - TCP/IP.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#ifdef RT_OS_WINDOWS
32#include <winsock.h>
33#else /* !RT_OS_WINDOWS */
34#include <errno.h>
35#include <sys/stat.h>
36#include <sys/socket.h>
37#include <netinet/in.h>
38#include <netinet/tcp.h>
39#include <arpa/inet.h>
40#include <sys/un.h>
41#include <netdb.h>
42#include <unistd.h>
43#endif /* !RT_OS_WINDOWS */
44
45#include <iprt/tcp.h>
46#include <iprt/thread.h>
47#include <iprt/alloc.h>
48#include <iprt/assert.h>
49#include <iprt/asm.h>
50#include <iprt/err.h>
51#include <iprt/string.h>
52
53
54/* non-standard linux stuff (it seems). */
55#ifndef MSG_NOSIGNAL
56# define MSG_NOSIGNAL 0
57#endif
58#ifndef SHUT_RDWR
59# ifdef SD_BOTH
60# define SHUT_RDWR SD_BOTH
61# else
62# define SHUT_RDWR 2
63# endif
64#endif
65
66/* fixup backlevel OSes. */
67#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
68# define socklen_t int
69#endif
70
71
72/*******************************************************************************
73* Defined Constants And Macros *
74*******************************************************************************/
75#define BACKLOG 10 /* how many pending connections queue will hold */
76
77
78/*******************************************************************************
79* Structures and Typedefs *
80*******************************************************************************/
81/**
82 * TCP Server state.
83 */
84typedef enum RTTCPSERVERSTATE
85{
86 /** Invalid. */
87 RTTCPSERVERSTATE_INVALID = 0,
88 /** Created. */
89 RTTCPSERVERSTATE_CREATED,
90 /** Listener thread is starting up. */
91 RTTCPSERVERSTATE_STARTING,
92 /** Accepting client connections. */
93 RTTCPSERVERSTATE_ACCEPTING,
94 /** Serving a client. */
95 RTTCPSERVERSTATE_SERVING,
96 /** Listener terminating. */
97 RTTCPSERVERSTATE_STOPPING,
98 /** Listener terminated. */
99 RTTCPSERVERSTATE_STOPPED,
100 /** Destroying signaling to the listener, listener will wait. */
101 RTTCPSERVERSTATE_SIGNALING,
102 /** Listener cleans up. */
103 RTTCPSERVERSTATE_DESTROYING,
104 /** Freed. */
105 RTTCPSERVERSTATE_FREED
106} RTTCPSERVERSTATE;
107
108/*
109 * Internal representation of the TCP Server handle.
110 */
111typedef struct RTTCPSERVER
112{
113 /** The server state. */
114 RTTCPSERVERSTATE volatile enmState;
115 /** The server thread. */
116 RTTHREAD Thread;
117 /** The server socket. */
118 RTSOCKET volatile SockServer;
119 /** The socket to the client currently being serviced.
120 * This is NIL_RTSOCKET when no client is serviced. */
121 RTSOCKET volatile SockClient;
122 /** The connection function. */
123 PFNRTTCPSERVE pfnServe;
124 /** Argument to pfnServer. */
125 void *pvUser;
126} RTTCPSERVER;
127
128
129/*******************************************************************************
130* Internal Functions *
131*******************************************************************************/
132static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer);
133static int rtTcpServerListen(PRTTCPSERVER pServer);
134static void rcTcpServerListenCleanup(PRTTCPSERVER pServer);
135static void rtTcpServerDestroyServerSock(RTSOCKET SockServer, const char *pszMsg);
136static int rtTcpClose(RTSOCKET Sock, const char *pszMsg);
137
138
139
140/**
141 * Get the last error as an iprt status code.
142 * @returns iprt status code.
143 */
144DECLINLINE(int) rtTcpError(void)
145{
146#ifdef RT_OS_WINDOWS
147 return RTErrConvertFromWin32(WSAGetLastError());
148#else
149 return RTErrConvertFromErrno(errno);
150#endif
151}
152
153
154/**
155 * Atomicly updates a socket variable.
156 * @returns The old value.
157 * @param pSock The socket variable to update.
158 * @param Sock The new value.
159 */
160DECLINLINE(RTSOCKET) rtTcpAtomicXchgSock(RTSOCKET volatile *pSock, const RTSOCKET Sock)
161{
162 switch (sizeof(RTSOCKET))
163 {
164 case 4: return (RTSOCKET)ASMAtomicXchgS32((int32_t volatile *)pSock, (int32_t)Sock);
165 default:
166 AssertReleaseFailed();
167 return NIL_RTSOCKET;
168 }
169}
170
171
172/**
173 * Changes the TCP server state.
174 */
175DECLINLINE(bool) rtTcpServerSetState(PRTTCPSERVER pServer, RTTCPSERVERSTATE enmStateNew, RTTCPSERVERSTATE enmStateOld)
176{
177 bool fRc;
178 ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
179 return fRc;
180}
181
182
183/**
184 * Create single connection at a time TCP Server in a separate thread.
185 *
186 * The thread will loop accepting connections and call pfnServe for
187 * each of the incoming connections in turn. The pfnServe function can
188 * return VERR_TCP_SERVER_STOP too terminate this loop. RTTcpServerDestroy()
189 * should be used to terminate the server.
190 *
191 * @returns iprt status code.
192 * @param pszAddress The address for creating a listening socket.
193 * If NULL or empty string the server is bound to all interfaces.
194 * @param uPort The port for creating a listening socket.
195 * @param enmType The thread type.
196 * @param pszThrdName The name of the worker thread.
197 * @param pfnServe The function which will serve a new client connection.
198 * @param pvUser User argument passed to pfnServe.
199 * @param ppServer Where to store the serverhandle.
200 */
201RTR3DECL(int) RTTcpServerCreate(const char *pszAddress, unsigned uPort, RTTHREADTYPE enmType, const char *pszThrdName,
202 PFNRTTCPSERVE pfnServe, void *pvUser, PPRTTCPSERVER ppServer)
203{
204 /*
205 * Do params checking
206 */
207 if (!uPort || !pfnServe || !pszThrdName || !ppServer)
208 {
209 AssertMsgFailed(("Invalid params\n"));
210 return VERR_INVALID_PARAMETER;
211 }
212
213 /*
214 * Create the server.
215 */
216 PRTTCPSERVER pServer;
217 int rc = RTTcpServerCreateEx(pszAddress, uPort, &pServer);
218 if (RT_SUCCESS(rc))
219 {
220 /*
221 * Create the listener thread.
222 */
223 pServer->enmState = RTTCPSERVERSTATE_STARTING;
224 pServer->pvUser = pvUser;
225 pServer->pfnServe = pfnServe;
226 rc = RTThreadCreate(&pServer->Thread, rtTcpServerThread, pServer, 0, enmType, /*RTTHREADFLAGS_WAITABLE*/0, pszThrdName);
227 if (RT_SUCCESS(rc))
228 {
229 /* done */
230 if (ppServer)
231 *ppServer = pServer;
232 return rc;
233 }
234
235 /*
236 * Destroy the server.
237 */
238 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_CREATED, RTTCPSERVERSTATE_STARTING);
239 RTTcpServerDestroy(pServer);
240 }
241
242 return rc;
243}
244
245
246/**
247 * Server thread, loops accepting connections until it's terminated.
248 *
249 * @returns iprt status code. (ignored).
250 * @param ThreadSelf Thread handle.
251 * @param pvServer Server handle.
252 */
253static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer)
254{
255 PRTTCPSERVER pServer = (PRTTCPSERVER)pvServer;
256 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_STARTING))
257 return rtTcpServerListen(pServer);
258 rcTcpServerListenCleanup(pServer);
259 NOREF(ThreadSelf);
260 return VINF_SUCCESS;
261}
262
263
264/**
265 * Create single connection at a time TCP Server.
266 * The caller must call RTTcpServerListen() to actually start the server.
267 *
268 * @returns iprt status code.
269 * @param pszAddress The address for creating a listening socket.
270 * If NULL the server is bound to all interfaces.
271 * @param uPort The port for creating a listening socket.
272 * @param ppServer Where to store the serverhandle.
273 */
274RTR3DECL(int) RTTcpServerCreateEx(const char *pszAddress, uint32_t uPort, PPRTTCPSERVER ppServer)
275{
276 int rc;
277
278 /*
279 * Do params checking
280 */
281 if (!uPort || !ppServer)
282 {
283 AssertMsgFailed(("Invalid params\n"));
284 return VERR_INVALID_PARAMETER;
285 }
286
287#ifdef RT_OS_WINDOWS
288 /*
289 * Initialize WinSock and check version.
290 */
291 WORD wVersionRequested = MAKEWORD(1, 1);
292 WSADATA wsaData;
293 rc = WSAStartup(wVersionRequested, &wsaData);
294 if (wsaData.wVersion != wVersionRequested)
295 {
296 AssertMsgFailed(("Wrong winsock version\n"));
297 return VERR_NOT_SUPPORTED;
298 }
299#endif
300
301 /*
302 * Get host listening address.
303 */
304 struct hostent *pHostEnt = NULL;
305 if (pszAddress != NULL && *pszAddress)
306 {
307 pHostEnt = gethostbyname(pszAddress);
308 if (!pHostEnt)
309 {
310 struct in_addr InAddr;
311 InAddr.s_addr = inet_addr(pszAddress);
312 pHostEnt = gethostbyaddr((char *)&InAddr, 4, AF_INET);
313 if (!pHostEnt)
314 {
315 rc = rtTcpError();
316 AssertMsgFailed(("Could not get host address rc=%Vrc\n", rc));
317 return rc;
318 }
319 }
320 }
321
322 /*
323 * Setting up socket.
324 */
325 RTSOCKET WaitSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
326 if (WaitSock != -1)
327 {
328 /*
329 * Set socket options.
330 */
331 int fFlag = 1;
332 if (!setsockopt(WaitSock, SOL_SOCKET, SO_REUSEADDR, (const char *)&fFlag, sizeof(fFlag)))
333 {
334 /*
335 * Set socket family, address and port.
336 */
337 struct sockaddr_in LocalAddr = {0};
338 LocalAddr.sin_family = AF_INET;
339 LocalAddr.sin_port = htons(uPort);
340 /* if address not specified, use INADDR_ANY. */
341 if (!pHostEnt)
342 LocalAddr.sin_addr.s_addr = INADDR_ANY;
343 else
344 LocalAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr);
345
346 /*
347 * Bind a name to a socket.
348 */
349 if (bind(WaitSock, (struct sockaddr *)&LocalAddr, sizeof(LocalAddr)) != -1)
350 {
351 /*
352 * Listen for connections on a socket.
353 */
354 if (listen(WaitSock, BACKLOG) != -1)
355 {
356 /*
357 * Create the server handle.
358 */
359 PRTTCPSERVER pServer = (PRTTCPSERVER)RTMemAllocZ(sizeof(*pServer));
360 if (pServer)
361 {
362 pServer->SockServer = WaitSock;
363 pServer->SockClient = NIL_RTSOCKET;
364 pServer->Thread = NIL_RTTHREAD;
365 pServer->enmState = RTTCPSERVERSTATE_CREATED;
366 *ppServer = pServer;
367 return VINF_SUCCESS;
368 }
369 else
370 rc = VERR_NO_MEMORY;
371 }
372 else
373 {
374 rc = rtTcpError();
375 AssertMsgFailed(("listen() %Vrc\n", rc));
376 }
377 }
378 else
379 {
380 rc = rtTcpError();
381 }
382 }
383 else
384 {
385 rc = rtTcpError();
386 AssertMsgFailed(("setsockopt() %Vrc\n", rc));
387 }
388 rtTcpClose(WaitSock, "RTServerCreateEx");
389 }
390 else
391 {
392 rc = rtTcpError();
393 AssertMsgFailed(("socket() %Vrc\n", rc));
394 }
395
396 return rc;
397}
398
399
400/**
401 * Listen for incoming connections.
402 *
403 * The function will loop accepting connections and call pfnServe for
404 * each of the incoming connections in turn. The pfnServe function can
405 * return VERR_TCP_SERVER_STOP too terminate this loop. A stopped server
406 * can only be destroyed.
407 *
408 * @returns iprt status code.
409 * @param pServer The server handle as returned from RTTcpServerCreateEx().
410 * @param pfnServe The function which will serve a new client connection.
411 * @param pvUser User argument passed to pfnServe.
412 */
413RTR3DECL(int) RTTcpServerListen(PRTTCPSERVER pServer, PFNRTTCPSERVE pfnServe, void *pvUser)
414{
415 /*
416 * Validate input.
417 */
418 if (!pfnServe || !pServer)
419 {
420 AssertMsgFailed(("pfnServer=%p pServer=%p\n", pfnServe, pServer));
421 return VERR_INVALID_PARAMETER;
422 }
423 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_CREATED))
424 {
425 Assert(!pServer->pfnServe);
426 Assert(!pServer->pvUser);
427 Assert(pServer->Thread == NIL_RTTHREAD);
428 Assert(pServer->SockClient == NIL_RTSOCKET);
429
430 pServer->pfnServe = pfnServe;
431 pServer->pvUser = pvUser;
432 pServer->Thread = RTThreadSelf();
433 Assert(pServer->Thread != NIL_RTTHREAD);
434 return rtTcpServerListen(pServer);
435 }
436 AssertMsgFailed(("pServer->enmState=%d\n", pServer->enmState));
437 return VERR_INVALID_PARAMETER;
438}
439
440
441/**
442 * Closes the client socket.
443 */
444static int rtTcpServerDestroyClientSock(RTSOCKET volatile *pSock, const char *pszMsg)
445{
446 RTSOCKET Sock = rtTcpAtomicXchgSock(pSock, NIL_RTSOCKET);
447 if (Sock != NIL_RTSOCKET)
448 shutdown(Sock, SHUT_RDWR);
449 return rtTcpClose(Sock, pszMsg);
450}
451
452
453/**
454 * Internal worker common for RTTcpServerListen and the thread created by RTTcpServerCreate().
455 */
456static int rtTcpServerListen(PRTTCPSERVER pServer)
457{
458 /*
459 * Accept connection loop.
460 */
461 int rc = VINF_SUCCESS;
462 for (;;)
463 {
464 /*
465 * Change state.
466 */
467 RTTCPSERVERSTATE enmState = pServer->enmState;
468 if ( enmState != RTTCPSERVERSTATE_ACCEPTING
469 && enmState != RTTCPSERVERSTATE_SERVING)
470 break;
471 if (!rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, enmState))
472 continue;
473
474 /*
475 * Accept connection.
476 */
477 struct sockaddr_in RemoteAddr = {0};
478 socklen_t Len = sizeof(RemoteAddr);
479 RTSOCKET Socket = accept(pServer->SockServer, (struct sockaddr *)&RemoteAddr, &Len);
480 if (Socket == -1)
481 {
482#ifndef RT_OS_WINDOWS
483 /* These are typical for what can happen during destruction. */
484 if (errno == EBADF || errno == EINVAL || errno == ENOTSOCK)
485 break;
486#endif
487 continue;
488 }
489
490 /*
491 * Run a pfnServe callback.
492 */
493 if (!rtTcpServerSetState(pServer, RTTCPSERVERSTATE_SERVING, RTTCPSERVERSTATE_ACCEPTING))
494 break;
495 rtTcpAtomicXchgSock(&pServer->SockClient, Socket);
496 rc = pServer->pfnServe(Socket, pServer->pvUser);
497 rtTcpServerDestroyClientSock(&pServer->SockClient, "Listener: client");
498
499 /*
500 * Stop the server?
501 */
502 if (rc == VERR_TCP_SERVER_STOP)
503 {
504 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPING, RTTCPSERVERSTATE_SERVING))
505 {
506 /*
507 * Reset the server socket and change the state to stopped. After that state change
508 * we cannot safely access the handle so we'll have to return here.
509 */
510 RTSOCKET SockServer = rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET);
511 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPED, RTTCPSERVERSTATE_STOPPING);
512 rtTcpClose(SockServer, "Listener: server stopped");
513 return rc;
514 }
515 break;
516 }
517 }
518
519 /*
520 * Perform any pending clean and be gone.
521 */
522 rcTcpServerListenCleanup(pServer);
523 return rc;
524}
525
526
527/**
528 * Clean up after listener.
529 */
530static void rcTcpServerListenCleanup(PRTTCPSERVER pServer)
531{
532 /*
533 * Wait for any destroyers to finish signaling us.
534 */
535 for (unsigned cTries = 99; cTries > 0; cTries--)
536 {
537 RTTCPSERVERSTATE enmState = pServer->enmState;
538 switch (enmState)
539 {
540 /*
541 * Intermediate state while the destroyer closes the client socket.
542 */
543 case RTTCPSERVERSTATE_SIGNALING:
544 if (!RTThreadYield())
545 RTThreadSleep(1);
546 break;
547
548 /*
549 * Free the handle.
550 */
551 case RTTCPSERVERSTATE_DESTROYING:
552 {
553 rtTcpClose(rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET), "Listener-cleanup: server");
554 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_FREED, RTTCPSERVERSTATE_DESTROYING);
555 RTMemFree(pServer);
556 return;
557 }
558
559 /*
560 * Everything else means failure.
561 */
562 default:
563 AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
564 return;
565 }
566 }
567 AssertMsgFailed(("Timed out when trying to clean up after listener. pServer=%p enmState=%d\n", pServer, pServer->enmState));
568}
569
570
571/**
572 * Terminate the open connection to the server.
573 *
574 * @returns iprt status code.
575 * @param pServer Handle to the server.
576 */
577RTR3DECL(int) RTTcpServerDisconnectClient(PRTTCPSERVER pServer)
578{
579 /*
580 * Validate input.
581 */
582 if ( !pServer
583 || pServer->enmState <= RTTCPSERVERSTATE_INVALID
584 || pServer->enmState >= RTTCPSERVERSTATE_FREED)
585 {
586 AssertMsgFailed(("Invalid parameter!\n"));
587 return VERR_INVALID_PARAMETER;
588 }
589
590 return rtTcpServerDestroyClientSock(&pServer->SockClient, "DisconnectClient: client");
591}
592
593
594/**
595 * Closes down and frees a TCP Server.
596 * This will also terminate any open connections to the server.
597 *
598 * @returns iprt status code.
599 * @param pServer Handle to the server.
600 */
601RTR3DECL(int) RTTcpServerDestroy(PRTTCPSERVER pServer)
602{
603 /*
604 * Validate input.
605 */
606 if ( !pServer
607 || pServer->enmState <= RTTCPSERVERSTATE_INVALID
608 || pServer->enmState >= RTTCPSERVERSTATE_FREED)
609 {
610 AssertMsgFailed(("Invalid parameter!\n"));
611 return VERR_INVALID_PARAMETER;
612 }
613
614/** @todo r=bird: Some of this horrible code can probably be exchanged with a RTThreadWait(). (It didn't exist when this code was written.) */
615
616 /*
617 * Move it to the destroying state.
618 */
619 RTSOCKET SockServer = rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET);
620 for (unsigned cTries = 99; cTries > 0; cTries--)
621 {
622 RTTCPSERVERSTATE enmState = pServer->enmState;
623 switch (enmState)
624 {
625 /*
626 * Try move it to the destroying state.
627 */
628 case RTTCPSERVERSTATE_STARTING:
629 case RTTCPSERVERSTATE_ACCEPTING:
630 case RTTCPSERVERSTATE_SERVING:
631 {
632 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_SIGNALING, enmState))
633 {
634 /* client */
635 rtTcpServerDestroyClientSock(&pServer->SockClient, "Destroyer: client");
636
637 bool fRc = rtTcpServerSetState(pServer, RTTCPSERVERSTATE_DESTROYING, RTTCPSERVERSTATE_SIGNALING);
638 Assert(fRc); NOREF(fRc);
639
640 /* server */
641 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server destroying");
642 RTThreadYield();
643
644 return VINF_SUCCESS;
645 }
646 break;
647 }
648
649
650 /*
651 * Intermediate state.
652 */
653 case RTTCPSERVERSTATE_STOPPING:
654 if (!RTThreadYield())
655 RTThreadSleep(1);
656 break;
657
658 /*
659 * Just release the handle.
660 */
661 case RTTCPSERVERSTATE_CREATED:
662 case RTTCPSERVERSTATE_STOPPED:
663 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_FREED, enmState))
664 {
665 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server freeing");
666 RTMemFree(pServer);
667 return VINF_TCP_SERVER_STOP;
668 }
669 break;
670
671 /*
672 * Everything else means failure.
673 */
674 default:
675 AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
676 return VERR_INTERNAL_ERROR;
677 }
678 }
679
680 AssertMsgFailed(("Giving up! pServer=%p enmState=%d\n", pServer, pServer->enmState));
681 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server timeout");
682 return VERR_INTERNAL_ERROR;
683}
684
685
686/**
687 * Shutdowns the server socket.
688 */
689static void rtTcpServerDestroyServerSock(RTSOCKET SockServer, const char *pszMsg)
690{
691 if (SockServer == NIL_RTSOCKET)
692 return;
693 shutdown(SockServer, SHUT_RDWR);
694 rtTcpClose(SockServer, "Destroyer: server destroying");
695}
696
697
698
699RTR3DECL(int) RTTcpRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
700{
701 /*
702 * Do params checking
703 */
704 if (!pvBuffer || !cbBuffer)
705 {
706 AssertMsgFailed(("Invalid params\n"));
707 return VERR_INVALID_PARAMETER;
708 }
709
710 /*
711 * Read loop.
712 * If pcbRead is NULL we have to fill the entire buffer!
713 */
714 size_t cbRead = 0;
715 size_t cbToRead = cbBuffer;
716 for (;;)
717 {
718 ssize_t cbBytesRead = recv(Sock, (char *)pvBuffer + cbRead, cbToRead, MSG_NOSIGNAL);
719 if (cbBytesRead < 0)
720 return rtTcpError();
721 if (cbBytesRead == 0 && rtTcpError())
722 return rtTcpError();
723 if (pcbRead)
724 {
725 /* return partial data */
726 *pcbRead = cbBytesRead;
727 break;
728 }
729
730 /* read more? */
731 cbRead += cbBytesRead;
732 if (cbRead == cbBuffer)
733 break;
734
735 /* next */
736 cbToRead = cbBuffer - cbRead;
737 }
738
739 return VINF_SUCCESS;
740}
741
742
743RTR3DECL(int) RTTcpWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
744{
745 do
746 {
747 ssize_t cbWritten = send(Sock, (const char *)pvBuffer, cbBuffer, MSG_NOSIGNAL);
748 if (cbWritten < 0)
749 return rtTcpError();
750 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d rtTcpError()=%d\n",
751 cbWritten, cbBuffer, rtTcpError()));
752 cbBuffer -= cbWritten;
753 pvBuffer = (char *)pvBuffer + cbWritten;
754 } while (cbBuffer);
755
756 return VINF_SUCCESS;
757}
758
759
760RTR3DECL(int) RTTcpFlush(RTSOCKET Sock)
761{
762 int fFlag = 1;
763 setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&fFlag, sizeof(fFlag));
764 fFlag = 0;
765 setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&fFlag, sizeof(fFlag));
766
767 return VINF_SUCCESS;
768}
769
770
771RTR3DECL(int) RTTcpSelectOne(RTSOCKET Sock, unsigned cMillies)
772{
773 fd_set fdsetR;
774 FD_ZERO(&fdsetR);
775 FD_SET(Sock, &fdsetR);
776
777 fd_set fdsetE = fdsetR;
778
779 int rc;
780 if (cMillies == RT_INDEFINITE_WAIT)
781 rc = select(Sock + 1, &fdsetR, NULL, &fdsetE, NULL);
782 else
783 {
784 struct timeval timeout;
785 timeout.tv_sec = cMillies / 1000;
786 timeout.tv_usec = (cMillies % 1000) * 1000;
787 rc = select(Sock + 1, &fdsetR, NULL, &fdsetE, &timeout);
788 }
789 if (rc > 0)
790 return VINF_SUCCESS;
791 if (rc == 0)
792 return VERR_TIMEOUT;
793 return rtTcpError();
794}
795
796
797RTR3DECL(int) RTTcpClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
798{
799 int rc;
800
801 /*
802 * Do params checking
803 */
804 AssertReturn(uPort, VERR_INVALID_PARAMETER);
805 AssertReturn(VALID_PTR(pszAddress), VERR_INVALID_PARAMETER);
806
807#ifdef RT_OS_WINDOWS
808 /*
809 * Initialize WinSock and check version.
810 */
811 WORD wVersionRequested = MAKEWORD(1, 1);
812 WSADATA wsaData;
813 rc = WSAStartup(wVersionRequested, &wsaData);
814 if (wsaData.wVersion != wVersionRequested)
815 {
816 AssertMsgFailed(("Wrong winsock version\n"));
817 return VERR_NOT_SUPPORTED;
818 }
819#endif
820
821 /*
822 * Resolve the address.
823 */
824 struct hostent *pHostEnt = NULL;
825 pHostEnt = gethostbyname(pszAddress);
826 if (!pHostEnt)
827 {
828 struct in_addr InAddr;
829 InAddr.s_addr = inet_addr(pszAddress);
830 pHostEnt = gethostbyaddr((char *)&InAddr, 4, AF_INET);
831 if (!pHostEnt)
832 {
833 rc = rtTcpError();
834 AssertMsgFailed(("Could not resolve '%s', rc=%Vrc\n", pszAddress, rc));
835 return rc;
836 }
837 }
838
839 /*
840 * Create the socket and connect.
841 */
842 RTSOCKET Sock = socket(PF_INET, SOCK_STREAM, 0);
843 if (Sock != -1)
844 {
845 struct sockaddr_in InAddr = {0};
846 InAddr.sin_family = AF_INET;
847 InAddr.sin_port = htons(uPort);
848 InAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr);
849 if (!connect(Sock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
850 {
851 *pSock = Sock;
852 return VINF_SUCCESS;
853 }
854 rc = rtTcpError();
855 rtTcpClose(Sock, "RTTcpClientConnect");
856 }
857 else
858 rc = rtTcpError();
859 return rc;
860}
861
862
863RTR3DECL(int) RTTcpClientClose(RTSOCKET Sock)
864{
865 return rtTcpClose(Sock, "RTTcpClientClose");
866}
867
868
869/**
870 * Internal close function which does all the proper bitching.
871 */
872static int rtTcpClose(RTSOCKET Sock, const char *pszMsg)
873{
874 /* ignore nil handles. */
875 if (Sock == NIL_RTSOCKET)
876 return VINF_SUCCESS;
877
878 /*
879 * Attempt to close it.
880 */
881#ifdef RT_OS_WINDOWS
882 int rc = closesocket(Sock);
883#else
884 int rc = close(Sock);
885#endif
886 if (!rc)
887 return VINF_SUCCESS;
888 rc = rtTcpError();
889 AssertMsgFailed(("\"%s\": close(%d) -> %Vrc\n", pszMsg, Sock, rc));
890 return rc;
891}
892
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