VirtualBox

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

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

rebranding: IPRT files again.

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