diff options
Diffstat (limited to 'src/VBox/GuestHost/OpenGL/util/udptcpip.c')
-rw-r--r-- | src/VBox/GuestHost/OpenGL/util/udptcpip.c | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/OpenGL/util/udptcpip.c b/src/VBox/GuestHost/OpenGL/util/udptcpip.c new file mode 100644 index 00000000..6d883e83 --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/udptcpip.c @@ -0,0 +1,753 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifdef WINDOWS +#define WIN32_LEAN_AND_MEAN +#pragma warning( push, 3 ) +#include <winsock2.h> +#include <ws2tcpip.h> /* for ipv6 */ +#include <time.h> +#pragma warning( pop ) +#pragma warning( disable : 4514 ) +#pragma warning( disable : 4127 ) +# ifndef VBOX +typedef int ssize_t; +# endif +#define write(a,b,c) send(a,b,c,0) +#else +#include <sys/types.h> +#if defined(OSF1) +typedef int socklen_t; +#endif +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#ifdef LINUX +#define IP_MTU 14 +#endif +#include <netinet/in.h> +#if !defined(IRIX) /* ip6.h might not be present on other unix variants either */ +#include <netinet/ip6.h> +#endif +#include <netinet/udp.h> +#ifndef DARWIN +#include <netinet/tcp.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> +#include <unistd.h> +#endif + +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#ifdef AIX +#include <strings.h> +#endif + +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_string.h" +#include "cr_bufpool.h" +#include "cr_net.h" +#include "cr_endian.h" +#include "cr_threads.h" +#include "net_internals.h" + +#ifdef ADDRINFO +#define PF PF_UNSPEC +#endif + +#ifndef MSG_TRUNC +#define MSG_TRUNC 0 +#endif + +#ifdef IRIX +/* IRIX defines this token, but appears to be missing the related types + * and functions. Turn it off. + */ +#undef AF_INET6 +#endif + +#if defined( WINDOWS ) || defined( IRIX ) || defined( IRIX64 ) +typedef int socklen_t; +#endif + +static void +crUDPIPWriteExact( CRConnection *conn, const void *buf, unsigned int len ) +{ + int retval; + if ( len > conn->mtu + sizeof(conn->seq) ) + { + crWarning( "crUDPIPWriteExact(%d): too big a packet for mtu %d, dropping !", len, (int)(conn->mtu + sizeof(conn->seq)) ); + return; + } + retval = sendto( conn->udp_socket, buf, len, 0, + (struct sockaddr *) &(conn->remoteaddr), sizeof(conn->remoteaddr)); + if ( retval <= 0 ) + { + int err = crTCPIPErrno( ); + crWarning( "crUDPIPWriteExact(%d): %s", len, crTCPIPErrorString( err ) ); +#ifdef LINUX + if ( err == EMSGSIZE ) + { + int opt; + socklen_t leno = sizeof(opt); + crDebug( "Too big a UDP packet(%d), looking at new MTU...", len ); + if ( getsockopt( conn->udp_socket, SOL_IP, IP_MTU_DISCOVER, &opt, &leno) == -1) + { + err = crTCPIPErrno( ); + crWarning( "Couldn't determine whether PMTU discovery is used : %s", crTCPIPErrorString( err ) ); + return; + } + if ( leno != sizeof(opt) ) + { + crWarning( "Unexpected length %d for PMTU_DISCOVERY option length", leno ); + return; + } + if ( opt == IP_PMTUDISC_DONT ) + { + crWarning( "Uh, PMTU discovery isn't enabled ?!" ); + return; + } + if ( getsockopt( conn->udp_socket, SOL_IP, IP_MTU, &opt, &leno) == -1 ) + { + err = crTCPIPErrno( ); + crWarning( "Couldn't determine the MTU : %s", crTCPIPErrorString( err ) ); + return; + } + if ( leno != sizeof(opt) ) + { + crWarning( "Unexpected length %d for MTU option length", leno ); + return; + } + opt -= sizeof(conn->seq) + sizeof(struct udphdr) + sizeof(struct ip6_hdr); + if (opt >= (int) conn->mtu) + { + crWarning( "But MTU discovery is still bigger ! Narrowing it by hand to %d", conn->mtu = (conn->mtu * 2 / 3) & ~0x3 ); + } + else + { + crDebug( "new MTU is %d", opt ); + conn->mtu = opt & ~0x3; + } + } +#endif + } +} + +static void +crUDPTCPIPAccept( CRConnection *conn, const char *hostname, unsigned short port ) +{ + int err; + socklen_t addr_length; +#ifndef ADDRINFO + struct sockaddr addr; + struct sockaddr_in udpaddr; +#else + struct sockaddr_storage addr; + struct addrinfo *res,*cur; + struct addrinfo hints; +#endif + + crTCPIPAccept( conn, hostname, port ); + +#ifndef ADDRINFO + conn->udp_socket = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( conn->udp_socket >= 0 ) + { + memset(&udpaddr, 0, sizeof(udpaddr)); + udpaddr.sin_family = AF_INET; + udpaddr.sin_addr.s_addr = INADDR_ANY; + udpaddr.sin_port = htons(0); + if ( bind( conn->udp_socket, (struct sockaddr *) &udpaddr, sizeof(udpaddr) ) ) + { + err = crTCPIPErrno( ); + crWarning( "Couldn't bind socket: %s", crTCPIPErrorString( err ) ); + crCloseSocket( conn->udp_socket ); + conn->udp_socket = -1; + } + } +#else + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = PF; + hints.ai_socktype = SOCK_DGRAM; + + err = getaddrinfo( NULL, "0", &hints, &res); + if ( err ) + crError( "Couldn't find local UDP port: %s", gai_strerror(err)); + + conn->udp_socket = -1; + + for (cur=res;cur;cur=cur->ai_next) + { + conn->udp_socket = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol ); + if ( conn->udp_socket == -1 ) + { + err = crTCPIPErrno( ); + if (err != EAFNOSUPPORT) + crWarning( "Couldn't create socket of family %i: %s, trying another one", cur->ai_family, crTCPIPErrorString( err ) ); + continue; + } + if ( bind( conn->udp_socket, cur->ai_addr, cur->ai_addrlen ) ) + { + err = crTCPIPErrno( ); + crWarning( "Couldn't bind socket: %s", crTCPIPErrorString( err ) ); + crCloseSocket( conn->udp_socket ); + conn->udp_socket = -1; + continue; + } + break; + + } + freeaddrinfo(res); +#endif + + if ( conn->udp_socket < 0 ) + crError( "Couldn't find local UDP port" ); + + addr_length = sizeof(addr); + err = getsockname( conn->udp_socket, (struct sockaddr *) &addr, &addr_length ); + + if ( err == -1 ) + crError( "Couldn't get our local UDP port: %s", crTCPIPErrorString( crTCPIPErrno( ) ) ); + + switch (((struct sockaddr *) &addr)->sa_family) { + case AF_INET: + crTCPIPWriteExact( conn, &((struct sockaddr_in *)&addr)->sin_port, + sizeof(((struct sockaddr_in *)&addr)->sin_port)); + break; +#ifdef AF_INET6 + case AF_INET6: + crTCPIPWriteExact( conn, &((struct sockaddr_in6 *)&addr)->sin6_port, + sizeof(((struct sockaddr_in6 *)&addr)->sin6_port)); + break; +#endif + default: + crError( "Unknown address family: %d", ((struct sockaddr *) &addr)->sa_family); + } +} + + +static unsigned int safelen=0; +static void crUDPTCPIPSend( CRConnection *conn, void **bufp, + const void *start, unsigned int len ) +{ + static unsigned int safedone=0; + CRTCPIPBuffer *udptcpip_buffer; + unsigned int *lenp; + + if ( !conn || conn->type == CR_NO_CONNECTION ) + return; + + if ( safelen+len > safelen ) + { + safelen+=len; + if (safelen-safedone>100000) + { + safedone=safelen; + crDebug( "%dKo safe", safelen/1024 ); + } + } + + conn->seq++; + if ( bufp == NULL ) + { + unsigned int len_swap = conn->swap ? SWAP32(len) : len; + /* we are doing synchronous sends from user memory, so no need + * to get fancy. Simply write the length & the payload and + * return. */ + crTCPIPWriteExact( conn, &len_swap, sizeof(len_swap) ); + if ( conn->type == CR_NO_CONNECTION ) return; + crTCPIPWriteExact( conn, start, len ); + return; + } + + udptcpip_buffer = (CRTCPIPBuffer *)(*bufp) - 1; + + CRASSERT( udptcpip_buffer->magic == CR_TCPIP_BUFFER_MAGIC ); + + /* All of the buffers passed to the send function were allocated + * with crTCPIPAlloc(), which includes a header with a 4 byte + * length field, to insure that we always have a place to write + * the length field, even when start == *bufp. */ + lenp = (unsigned int *) start - 1; + if (conn->swap) + { + *lenp = SWAP32(len); + } + else + { + *lenp = len; + } + + if ( __tcpip_write_exact( conn->tcp_socket, lenp, len + sizeof(*lenp) ) < 0 ) + { + __tcpip_dead_connection( conn ); + } + + /* reclaim this pointer for reuse and try to keep the client from + accidentally reusing it directly */ +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&cr_tcpip.mutex); +#endif + crBufferPoolPush( cr_tcpip.bufpool, udptcpip_buffer, conn->buffer_size ); + /* Since the buffer's now in the 'free' buffer pool, the caller can't + * use it any more. Setting bufp to NULL will make sure the caller + * doesn't try to re-use the buffer. + */ + *bufp = NULL; +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&cr_tcpip.mutex); +#endif +} + +static unsigned int barflen=0; +static void crUDPTCPIPBarf( CRConnection *conn, void **bufp, + const void *start, unsigned int len) +{ + static unsigned int barfdone=0; + static const unsigned int sizes[]={ + 0,10,50,100,500,1000,5000,10000,UINT_MAX + }; + static unsigned int nbs[sizeof(sizes)/sizeof(int)] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static unsigned int nb; + unsigned int *seqp; + CRTCPIPBuffer *udptcpip_buffer; + int i; + + if (!bufp) { + crDebug("writing safely %d bytes because from user memory",len); + crUDPTCPIPSend( conn, bufp, start, len); + return; + } + if (len > conn->mtu) { + crDebug("writing safely %d bytes because that is too much for MTU %d", len, conn->mtu); + crUDPTCPIPSend( conn, bufp, start, len); + return; + } + + if ( barflen+len > barflen ) + { + barflen+=len; + nb++; + for(i=0;;i++) + if (len > sizes[i] && len <= sizes[i+1]) { + nbs[i]++; + break; + } + if (barflen-barfdone>1<<22) { + barfdone=barflen; + crDebug( "send traffic: %d%sMo barfed %dKo safe", barflen/(1024*1024), barflen?"":".0", safelen/1024 ); + if (nb) { + for (i=0; i < (int) (sizeof(sizes)/sizeof(int)-1); i++) + fprintf(stderr,"%u:%u%s%% ",sizes[i],(nbs[i]*100)/nb,nbs[i]==0?".0":""); + fprintf(stderr,"\n"); + } + } + } + + udptcpip_buffer = (CRTCPIPBuffer *)(*bufp) - 1; + + CRASSERT( udptcpip_buffer->magic == CR_TCPIP_BUFFER_MAGIC ); + + seqp = (unsigned int *) start - 1; + if (conn->swap) + { + *seqp = SWAP32(conn->seq); + } + else + { + *seqp = conn->seq; + } + crUDPIPWriteExact( conn, seqp, len + sizeof(*seqp) ); + + /* reclaim this pointer for reuse and try to keep the client from + accidentally reusing it directly */ +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&cr_tcpip.mutex); +#endif + crBufferPoolPush( cr_tcpip.bufpool, udptcpip_buffer, conn->buffer_size ); +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&cr_tcpip.mutex); +#endif + *bufp = NULL; +} + +static void +crUDPTCPIPReceive( CRConnection *conn, CRTCPIPBuffer *buf, int len ) +{ + CRMessage *msg; + CRMessageType cached_type; +#if 0 + crLogRead( len ); +#endif + + conn->recv_credits -= len; + + conn->total_bytes_recv += len; + + msg = (CRMessage *) (buf + 1); + cached_type = msg->header.type; + if (conn->swap) + { + msg->header.type = (CRMessageType) SWAP32( msg->header.type ); + msg->header.conn_id = (CRMessageType) SWAP32( msg->header.conn_id ); + } + + crNetDispatchMessage( cr_tcpip.recv_list, conn, msg, len ); + + /* CR_MESSAGE_OPCODES is freed in + * crserverlib/server_stream.c + * + * OOB messages are the programmer's problem. -- Humper 12/17/01 */ + if (cached_type != CR_MESSAGE_OPCODES && cached_type != CR_MESSAGE_OOB) + { + crTCPIPFree( conn, buf + 1 ); + } +} + +int +crUDPTCPIPRecv( void ) +{ + int num_ready, max_fd; + fd_set read_fds; + int i; + /* ensure we don't get caught with a new thread connecting */ + int num_conns = cr_tcpip.num_conns; + +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&cr_tcpip.recvmutex); +#endif + + max_fd = 0; + FD_ZERO( &read_fds ); + for ( i = 0; i < num_conns; i++ ) + { + CRConnection *conn = cr_tcpip.conns[i]; + if ( !conn || conn->type == CR_NO_CONNECTION ) continue; + if ( conn->recv_credits > 0 || conn->type != CR_UDPTCPIP ) + { + /* + * NOTE: may want to always put the FD in the descriptor + * set so we'll notice broken connections. Down in the + * loop that iterates over the ready sockets only peek + * (MSG_PEEK flag to recv()?) if the connection isn't + * enabled. + */ + CRSocket sock = conn->tcp_socket; + + if (conn->type != CR_UDPTCPIP) + continue; + + if ( (int) sock + 1 > max_fd ) + max_fd = (int) sock + 1; + FD_SET( sock, &read_fds ); + + sock = conn->udp_socket; + if ( (int) sock + 1 > max_fd ) + max_fd = (int) sock + 1; + FD_SET( sock, &read_fds ); + } + } + + if (!max_fd) { +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&cr_tcpip.recvmutex); +#endif + return 0; + } + + if ( num_conns ) + { + num_ready = __crSelect( max_fd, &read_fds, 0, 500 ); + } + else + { + crWarning( "Waiting for first connection..." ); + num_ready = __crSelect( max_fd, &read_fds, 0, 0 ); + } + + if ( num_ready == 0 ) { +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&cr_tcpip.recvmutex); +#endif + return 0; + } + + for ( i = 0; i < num_conns; i++ ) + { + CRConnection *conn = cr_tcpip.conns[i]; + CRTCPIPBuffer *buf; + int len; + int sock; + + if ( !conn || conn->type == CR_NO_CONNECTION ) continue; + + if ( conn->type != CR_UDPTCPIP ) + continue; + + if ( conn->udp_packet ) { + unsigned int *seq; + buf = conn->udp_packet; + seq = (unsigned int *)(buf + 1) - 1; + if ( *seq == conn->ack ) + { + crUDPTCPIPReceive( conn, buf, + conn->udp_packetlen ); + conn->udp_packet = NULL; + i--; /* can now read other packets */ + continue; + } + if ( *seq - conn->ack > (~(0U)) >> 1 ) + { + crError( "%u is older than %u ?!", *seq, conn->ack ); + crTCPIPFree( conn, buf + 1 ); + conn->udp_packet = NULL; + i--; /* can now read other packets */ + continue; + } + /* still too early, wait for TCP data */ + } + else if ( FD_ISSET(conn->udp_socket, &read_fds ) ) + { + unsigned int *seq; + buf = ((CRTCPIPBuffer *) crTCPIPAlloc( conn )) - 1; + seq = ((unsigned int *) (buf + 1)) - 1; + + len = recv( conn->udp_socket, (void *)seq, + buf->allocated + sizeof(*seq), MSG_TRUNC ); + + CRASSERT( len > 0 ); + CRASSERT( (unsigned int)len <= buf->allocated + sizeof(*seq) ); + if ( len < (int) sizeof(*seq) ) + { + crWarning( "too short a UDP packet : %d", len); + crTCPIPFree( conn, buf + 1 ); + continue; + } + + buf->len = len; + + if ( *seq == conn->ack) + { + crUDPTCPIPReceive( conn, buf, len ); + continue; + } + + if ( *seq - conn->ack > (~(0U)) >> 1 ) + { + crWarning( "%u is older than %u, dropping", *seq, conn->ack ); + crTCPIPFree( conn, buf + 1 ); + continue; + } + conn->udp_packet = buf; + conn->udp_packetlen = len; + } + + sock = conn->tcp_socket; + if ( !FD_ISSET( sock, &read_fds ) ) + continue; + + if ( __tcpip_read_exact( sock, &len, sizeof(len)) <= 0 ) + { + __tcpip_dead_connection( conn ); + i--; + continue; + } + + if (conn->swap) + { + len = SWAP32(len); + } + + CRASSERT( len > 0 ); + + if ( (unsigned int)len <= conn->buffer_size ) + { + buf = (CRTCPIPBuffer *) crTCPIPAlloc( conn ) - 1; + } + else + { + buf = (CRTCPIPBuffer *) + crAlloc( sizeof(*buf) + len ); + buf->magic = CR_TCPIP_BUFFER_MAGIC; + buf->kind = CRTCPIPMemoryBig; + buf->pad = 0; + } + + buf->len = len; + + if ( __tcpip_read_exact( sock, buf + 1, len ) <= 0 ) + { + crWarning( "Bad juju: %d %d", buf->allocated, len ); + crFree( buf ); + __tcpip_dead_connection( conn ); + i--; + continue; + } + + crUDPTCPIPReceive( conn, buf, len ); + conn->ack++; + } + +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&cr_tcpip.recvmutex); +#endif + + return 1; +} + +void crUDPTCPIPInit( CRNetReceiveFuncList *rfl, CRNetCloseFuncList *cfl, unsigned int mtu ) +{ + crTCPIPInit( rfl, cfl, mtu ); +} + +/* The function that actually connects. This should only be called by clients + * Servers have another way to set up the socket. */ + +static int crUDPTCPIPDoConnect( CRConnection *conn ) +{ +#ifdef WINDOWS + unsigned short port; +#else + in_port_t port; +#endif +#ifdef LINUX + int opt = IP_PMTUDISC_DO; +#endif +#ifndef ADDRINFO + struct hostent *hp; +#else + int err; + char port_s[NI_MAXSERV]; + struct addrinfo *res,*cur; + struct addrinfo hints; +#endif + + /* first connect to its tcp port */ + if ( !crTCPIPDoConnect( conn ) ) + return 0; + + /* read its UDP port */ + crTCPIPReadExact( conn, &port, sizeof( port ) ); + port = ntohs(port); + + crDebug( "Server's UDP port is %d", port); + + /* and connect to it */ +#ifndef ADDRINFO + hp = gethostbyname( conn->hostname ); + if ( !hp ) + { + crWarning( "Unknown host: \"%s\"", conn->hostname ); + return 0; + } + conn->udp_socket = socket( AF_INET, SOCK_DGRAM, 0 ); + memset(&conn->remoteaddr, 0, sizeof(conn->remoteaddr)); + conn->remoteaddr.sin_family = AF_INET; + conn->remoteaddr.sin_port = htons( (short) port ); + + memcpy( (char *) &conn->remoteaddr.sin_addr, hp->h_addr, + sizeof(conn->remoteaddr.sin_addr) ); + if ( conn->udp_socket >= 0 ) { + if ( connect( conn->udp_socket, (struct sockaddr *) &conn->remoteaddr, + sizeof(conn->remoteaddr) ) == -1 ) + crWarning( "Couldn't connect UDP socket : %s", crTCPIPErrorString( crTCPIPErrno( ) ) ); + } else { +#else + sprintf(port_s, "%u", port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF; + hints.ai_socktype = SOCK_DGRAM; + + err = getaddrinfo( conn->hostname, port_s, &hints, &res); + if ( err ) + { + crWarning( "Unknown host: \"%s\": %s", conn->hostname, gai_strerror(err) ); + return 0; + } + + for (cur=res;cur;) + { + conn->udp_socket = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol ); + if ( conn->udp_socket >= 0 ) + { + if ( connect( conn->udp_socket, cur->ai_addr, cur->ai_addrlen ) == -1 ) + crWarning( "Couldn't connect UDP socket : %s", crTCPIPErrorString( crTCPIPErrno( ) ) ); + break; + } + err = crTCPIPErrno( ); + if (err != EAFNOSUPPORT) + crWarning( "socket error: %s, trying another way", crTCPIPErrorString( err ) ); + cur=cur->ai_next; + } + if (!cur) { + freeaddrinfo(res); +#endif + crWarning( "Couldn't find any suitable way to connect to %s:%d", conn->hostname, port ); + return 0; + } + +#ifdef LINUX + if ( setsockopt(conn->udp_socket, SOL_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)) == -1 ) + { + err = crTCPIPErrno( ); + crWarning( "MTU discovery can't be activated : %s", crTCPIPErrorString( err ) ); + } + else + { + socklen_t len = sizeof(opt); + if ( getsockopt(conn->udp_socket, SOL_IP, IP_MTU, &opt, &len) == -1 ) + crWarning( "MTU couldn't be got : %s", crTCPIPErrorString( crTCPIPErrno ( ) ) ); + else + if ( len != sizeof(opt) ) + crWarning( "Unexpected length %d for MTU option length", len ); + else + { + opt -= sizeof(conn->seq) + sizeof(struct udphdr) + sizeof(struct ip6_hdr); + crDebug( "MTU is (for now) %d", opt ); + conn->mtu = opt; + } + } +#endif +#ifdef ADDRINFO + crMemcpy(&conn->remoteaddr, cur->ai_addr, cur->ai_addrlen); + freeaddrinfo(res); +#endif + return 1; +} + +static void crUDPTCPIPDoDisconnect( CRConnection *conn ) +{ + crCloseSocket( conn->udp_socket ); + crTCPIPDoDisconnect( conn ); +} + +void crUDPTCPIPConnection( CRConnection *conn ) +{ + crTCPIPConnection( conn ); + + conn->type = CR_UDPTCPIP; + conn->Send = crUDPTCPIPSend; + conn->Barf = crUDPTCPIPBarf; + conn->SendExact = NULL; + conn->Recv = NULL; /* none for UDP : *must* multiplex ! */ + conn->Accept = crUDPTCPIPAccept; + conn->Connect = crUDPTCPIPDoConnect; + conn->Disconnect = crUDPTCPIPDoDisconnect; + conn->seq = 0; + conn->ack = 0; + conn->udp_packet = NULL; + conn->mtu -= sizeof(conn->seq); /* some room for seq */ +} |