766 lines
20 KiB
C
766 lines
20 KiB
C
#include "net.h"
|
|
#include "../client/lbreakout.h"
|
|
#include <SDL.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
|
|
/* Basic layer for network communication. A single UDP socket
|
|
* is bound to a port. Received packages are stored in one
|
|
* global buffer.
|
|
* Socket addresses are wrapped in a struct called NetAddr to
|
|
* which raw data may be sent as unreliable datagrams.
|
|
*
|
|
* Error/message stream: sys_printf()
|
|
*/
|
|
|
|
int (*sys_printf)(const char *format, ...) = printf;
|
|
|
|
/* ********** UDP SOCKET ********** */
|
|
|
|
#ifdef SDL_NET_ENABLED
|
|
UDPsocket SDL_socket = 0;
|
|
UDPpacket *SDL_packet = 0;
|
|
#else
|
|
int net_socket = -1;
|
|
#endif
|
|
NetAddr net_local_addr;
|
|
NetAddr net_sender_addr;
|
|
char net_buffer[MAX_MSG_SIZE + PACKET_HEADER_SIZE];
|
|
int net_buffer_cur_size = 0;
|
|
|
|
int net_show_drops = 1;
|
|
#ifdef NET_DEBUG_MSG
|
|
int net_show_packets = 1;
|
|
#else
|
|
int net_show_packets = 0;
|
|
#endif
|
|
|
|
#ifdef NETWORK_ENABLED
|
|
#ifndef SDL_NET_ENABLED
|
|
static void sockaddr_to_netaddr( struct sockaddr *sa, NetAddr *addr )
|
|
{
|
|
struct sockaddr_in *sin;
|
|
addr->sa = *sa;
|
|
/* if AF_INET get ip */
|
|
if ( sa->sa_family == AF_INET ) {
|
|
sin = (struct sockaddr_in*)sa;
|
|
*(int *)&addr->inet_ip = *(int *)&sin->sin_addr; /* wow */
|
|
}
|
|
}
|
|
|
|
static void netaddr_to_sockaddr( NetAddr *addr, struct sockaddr *sa )
|
|
{
|
|
*sa = addr->sa;
|
|
}
|
|
|
|
/* resolve name but leave port 0 */
|
|
static bool resolve_hostname( char *host, struct sockaddr *sa, int *sa_len )
|
|
{
|
|
struct sockaddr_in *sin;
|
|
struct sockaddr_in6 *sin6;
|
|
struct hostent *hostlist;
|
|
|
|
/* resolve the host's address via DNS lookup */
|
|
if ( ( hostlist = gethostbyname( host ) ) == 0 ) {
|
|
sys_printf( _("unable to resolve %s: %s\n"), host, strerror( errno) );
|
|
return 0;
|
|
}
|
|
|
|
/* put host address to sa struct. simply use the first resolved address */
|
|
*sa_len = sizeof(*sa);
|
|
memset( sa, 0, *sa_len );
|
|
switch ( hostlist->h_addrtype ) {
|
|
case AF_INET:
|
|
sin = (struct sockaddr_in*)sa;
|
|
sin->sin_family = AF_INET;
|
|
memcpy( &sin->sin_addr, hostlist->h_addr_list[0], hostlist->h_length );
|
|
break;
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6*)sa;
|
|
sin6->sin6_family = AF_INET6;
|
|
memcpy( &sin6->sin6_addr, hostlist->h_addr_list[0], hostlist->h_length );
|
|
break;
|
|
default:
|
|
sys_printf( "unable to resolve: address family %i not supported\n",
|
|
hostlist->h_addrtype );
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void net_get_local_addr( int port )
|
|
{
|
|
char buf[MAXHOSTNAMELEN];
|
|
struct sockaddr_in sa;
|
|
int sa_len;
|
|
|
|
gethostname(buf, MAXHOSTNAMELEN);
|
|
buf[MAXHOSTNAMELEN-1] = 0;
|
|
|
|
resolve_hostname( buf, (struct sockaddr*)&sa, &sa_len ); /* port is missing */
|
|
sa.sin_port = htons((short)port);
|
|
|
|
sockaddr_to_netaddr( (struct sockaddr *)&sa, &net_local_addr );
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/* open UDP socket */
|
|
bool net_init( int port )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
#ifdef SDL_NET_ENABLED
|
|
if(SDLNet_Init()==-1) {
|
|
sys_printf("SDLNet_Init: %s\n", SDLNet_GetError());
|
|
return 0;
|
|
}
|
|
SDL_socket=SDLNet_UDP_Open(port);
|
|
if(!SDL_socket) {
|
|
sys_printf("SDLNet_UDP_Open: %s\n", SDLNet_GetError());
|
|
return 0;
|
|
}
|
|
SDL_packet = SDLNet_AllocPacket(MAX_MSG_SIZE + PACKET_HEADER_SIZE);
|
|
if(SDL_packet==0) {
|
|
sys_printf("cannot allocate packet: out of memory\n" );
|
|
return 0;
|
|
}
|
|
/* net_local_addr is unset */
|
|
net_local_addr.SDL_address.host = 0;
|
|
net_local_addr.SDL_address.port = 0;
|
|
return 1;
|
|
#else
|
|
struct sockaddr_in sa;
|
|
int sa_len;
|
|
int fcntl_args, i, new_args;
|
|
|
|
if ( (net_socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
|
|
sys_printf( "couldn't create socket: %s\n", strerror(errno) );
|
|
else
|
|
if ( fcntl( net_socket, F_GETFL, &fcntl_args) < 0 )
|
|
sys_printf( "couldn't get fcntl args: %s\n", strerror(errno) );
|
|
else
|
|
{
|
|
new_args = O_NONBLOCK;
|
|
/* no idea, no idea... fcntl seems to work differently since kernel 2.6
|
|
if (fcntl_args&O_APPEND) new_args|=O_APPEND;
|
|
if (fcntl_args&O_ASYNC) new_args|=O_ASYNC;
|
|
if (fcntl_args&O_DIRECT) new_args|=O_DIRECT;*/
|
|
if ( fcntl( net_socket, F_SETFL, new_args ) < 0 )
|
|
sys_printf( "couldn't set fcntl args: %s\n", strerror(errno) );
|
|
else {
|
|
sa_len = sizeof(sa);
|
|
memset( &sa, 0, sa_len );
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons((short)port);
|
|
sa.sin_addr.s_addr = htonl(INADDR_ANY); /* all interfaces */
|
|
|
|
i = 10; /* try ten successive ports */
|
|
while ( bind( net_socket, (struct sockaddr*)&sa, sa_len ) < 0 ) {
|
|
sys_printf( "binding to port %i failed: %s\n", port, strerror(errno) );
|
|
sa.sin_port = htons((short)port++);
|
|
if ( --i == 0 ) {
|
|
close( net_socket );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
net_get_local_addr( port );
|
|
sys_printf( _("UDP socket bound to %s:%i\n"),
|
|
net_addr_to_string( &net_local_addr ), port );
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
#endif
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void net_shutdown( void )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
#ifdef SDL_NET_ENABLED
|
|
SDLNet_UDP_Close(SDL_socket); SDL_socket = 0;
|
|
SDLNet_FreePacket(SDL_packet); SDL_packet = 0;
|
|
SDLNet_Quit();
|
|
#else
|
|
close( net_socket );
|
|
sys_printf( "UDP socket closed\n" );
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void net_send_packet( NetAddr *to, int len, void *data )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
#ifdef SDL_NET_ENABLED
|
|
SDL_packet->channel = -1;
|
|
memcpy( SDL_packet->data, data, len );
|
|
SDL_packet->len = len;
|
|
SDL_packet->address = to->SDL_address;
|
|
SDLNet_UDP_Send(SDL_socket,-1,SDL_packet);
|
|
#else
|
|
struct sockaddr sa;
|
|
|
|
/* empty packets are not send */
|
|
if ( len == 0 ) {
|
|
sys_printf( "net_send_packet: null length packet\n" );
|
|
return;
|
|
}
|
|
|
|
netaddr_to_sockaddr( to, &sa );
|
|
|
|
if ( sendto( net_socket, data, len, 0, &sa, sizeof(sa) ) < 0 ) {
|
|
if (errno == EWOULDBLOCK)
|
|
return;
|
|
if (errno == ECONNREFUSED)
|
|
return;
|
|
sys_printf( "net_send_packet: %s\n", strerror(errno) );
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
bool net_recv_packet( void )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
int ret;
|
|
#ifdef SDL_NET_ENABLED
|
|
ret = SDLNet_UDP_Recv(SDL_socket, SDL_packet);
|
|
if ( ret == -1 ) {
|
|
sys_printf("SDLNet_UDP_Recv: %s\n", SDLNet_GetError());
|
|
return 0;
|
|
}
|
|
if ( ret == 1 ) {
|
|
memcpy( net_buffer, SDL_packet->data, SDL_packet->len );
|
|
net_buffer_cur_size = SDL_packet->len;
|
|
net_sender_addr.SDL_address = SDL_packet->address;
|
|
return 1;
|
|
}
|
|
net_buffer_cur_size = 0;
|
|
return 0;
|
|
#else
|
|
struct sockaddr sa;
|
|
int sa_len = sizeof(sa);
|
|
|
|
ret = recvfrom( net_socket,
|
|
net_buffer, sizeof(net_buffer),
|
|
0,
|
|
&sa, &sa_len);
|
|
if (ret == -1) {
|
|
if (errno == EWOULDBLOCK)
|
|
return 0;
|
|
if (errno == ECONNREFUSED)
|
|
return 0;
|
|
sys_printf( "net_recv_packet: %s\n", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
net_buffer_cur_size = ret;
|
|
|
|
sockaddr_to_netaddr( &sa, &net_sender_addr );
|
|
|
|
return (ret>0);
|
|
#endif
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/* if host contains trailing :xxxxx 'port' is overwritten */
|
|
bool net_build_addr( NetAddr *addr, char *host, int port )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
char *ptr;
|
|
#ifndef SDL_NET_ENABLED
|
|
struct sockaddr sa;
|
|
int sa_len;
|
|
#endif
|
|
if ( (ptr = strchr( host, ':' )) ) {
|
|
*ptr = 0;
|
|
port = atoi( ptr+1 );
|
|
}
|
|
|
|
#ifdef SDL_NET_ENABLED
|
|
if ( SDLNet_ResolveHost(&addr->SDL_address, host, port ) == -1 )
|
|
{
|
|
sys_printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
|
|
return 0;
|
|
}
|
|
return 1;
|
|
#else
|
|
memset( addr, 0, sizeof( NetAddr ) );
|
|
if ( resolve_hostname( host, &sa, &sa_len ) ) {
|
|
switch ( sa.sa_family ) {
|
|
case AF_INET:
|
|
((struct sockaddr_in*)&sa)->sin_port = htons((short)port);
|
|
break;
|
|
case AF_INET6:
|
|
((struct sockaddr_in6*)&sa)->sin6_port = htons((short)port);
|
|
break;
|
|
default:
|
|
/* not reached as resolve_hostname allows the same types */
|
|
return 0;
|
|
}
|
|
sockaddr_to_netaddr( &sa, addr );
|
|
return 1;
|
|
}
|
|
return 0;
|
|
#endif
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/* compare host and port */
|
|
bool net_compare_addr( NetAddr *a1, NetAddr *a2 )
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
#ifdef SDL_NET_ENABLED
|
|
if ( a1->SDL_address.host == a2->SDL_address.host )
|
|
if ( a1->SDL_address.port == a2->SDL_address.port )
|
|
return 1;
|
|
return 0;
|
|
#else
|
|
/* by now only AF_INET addresses may be compared */
|
|
if ( a1->sa.sa_family == AF_INET && a2->sa.sa_family == AF_INET ) {
|
|
if ( a1->inet_ip[0] == a2->inet_ip[0] )
|
|
if ( a1->inet_ip[1] == a2->inet_ip[1] )
|
|
if ( a1->inet_ip[2] == a2->inet_ip[2] )
|
|
if ( a1->inet_ip[3] == a2->inet_ip[3] )
|
|
if ( ((struct sockaddr_in*)&a1->sa)->sin_port ==
|
|
((struct sockaddr_in*)&a2->sa)->sin_port )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
sys_printf( "only AF_INET addresses may be compared yet\n" );
|
|
return 0;
|
|
#endif
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
char *net_addr_to_string( NetAddr *_addr ) /* not allocated but static */
|
|
{
|
|
#ifdef NETWORK_ENABLED
|
|
#ifdef SDL_NET_ENABLED
|
|
int ip[4];
|
|
static char host_ip[64];
|
|
char *host;
|
|
if(!(host=SDLNet_ResolveIP(&_addr->SDL_address)))
|
|
{
|
|
//printf("SDLNet_ResolveIP: %s\n", SDLNet_GetError());
|
|
ip[0] = _addr->SDL_address.host & 0xff;
|
|
ip[1] = (_addr->SDL_address.host>>8) & 0xff;
|
|
ip[2] = (_addr->SDL_address.host>>16) & 0xff;
|
|
ip[3] = (_addr->SDL_address.host>>24) & 0xff;
|
|
snprintf( host_ip, 64, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3] );
|
|
return host_ip;
|
|
}
|
|
return host;
|
|
#else
|
|
static char str[64];
|
|
int len = 64;
|
|
struct sockaddr *addr = &_addr->sa;
|
|
struct sockaddr_in *sin;
|
|
struct sockaddr_in6 *sin6;
|
|
/*struct sockaddr_un *sun;*/
|
|
|
|
str[len-1] = 0;
|
|
snprintf( str, len, "undefined" );
|
|
|
|
switch ( addr->sa_family ) {
|
|
case AF_INET:
|
|
sin = (struct sockaddr_in*)addr;
|
|
if ( !inet_ntop( AF_INET, &sin->sin_addr, str, len ) ) {
|
|
sys_printf( "inet_ntop failed: %s\n", strerror( errno ) );
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6*)addr;
|
|
if ( !inet_ntop( AF_INET6, &sin6->sin6_addr, str, len ) ) {
|
|
sys_printf( "inet_ntop failed: %s\n", strerror( errno ) );
|
|
}
|
|
break;
|
|
case AF_UNIX:
|
|
/*sun = (struct sockaddr_un *)addr;
|
|
if( sun->sun_path[0] == 0 ) {
|
|
snprintf( str, len, "unknown" );
|
|
sys_printf( "net_query_ip: unknown unix path\n" );
|
|
}
|
|
else
|
|
snprintf( str, sun->sun_path, len );*/
|
|
break;
|
|
default:
|
|
snprintf( str, len, "unknown" );
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
#endif
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/* ********** MESSAGE ********** */
|
|
|
|
char *msg_buf = 0;
|
|
int *msg_buf_cur_size = 0;
|
|
int msg_buf_max_size = 0;
|
|
int msg_write_overflow = 0;
|
|
|
|
#ifdef NET_DEBUG_MSG
|
|
static void msg_print_raw( int len, char *buf )
|
|
{
|
|
int i;
|
|
for ( i = 0; i < len; i++ )
|
|
printf( "%02x ", (unsigned char) buf[i] );
|
|
printf( "\n" );
|
|
}
|
|
#endif
|
|
|
|
static char* msg_get_writeable_space( int size )
|
|
{
|
|
if ( *msg_buf_cur_size + size > msg_buf_max_size ) {
|
|
msg_write_overflow = 1;
|
|
*msg_buf_cur_size = 0;
|
|
}
|
|
return msg_buf + *msg_buf_cur_size;
|
|
}
|
|
|
|
void msg_begin_writing( char *buf, int *cur_size, int max_size )
|
|
{
|
|
msg_buf = buf;
|
|
msg_buf_cur_size = cur_size;
|
|
msg_buf_max_size = max_size;
|
|
*msg_buf_cur_size = 0;
|
|
msg_write_overflow = 0;
|
|
}
|
|
void msg_write_int8 ( int c )
|
|
{
|
|
unsigned char *ptr = msg_get_writeable_space( 1 );
|
|
ptr[0] = (char)c;
|
|
*msg_buf_cur_size += 1;
|
|
}
|
|
void msg_write_int16 ( int s )
|
|
{
|
|
unsigned char *ptr = msg_get_writeable_space( 2 );
|
|
ptr[0] = s & 0xff;
|
|
ptr[1] = (s>>8) & 0xff;
|
|
*msg_buf_cur_size += 2;
|
|
}
|
|
void msg_write_int32 ( int i )
|
|
{
|
|
unsigned char *ptr = msg_get_writeable_space( 4 );
|
|
ptr[0] = i & 0xff;
|
|
ptr[1] = (i>>8) & 0xff;
|
|
ptr[2] = (i>>16) & 0xff;
|
|
ptr[3] = (i>>24) & 0xff;
|
|
*msg_buf_cur_size += 4;
|
|
}
|
|
void msg_write_string( char *str )
|
|
{
|
|
char *ptr = msg_get_writeable_space( strlen(str)+1 );
|
|
if ( strlen(str)+1 > msg_buf_max_size ) return; /* would cause segfault */
|
|
strcpy( ptr, str );
|
|
*msg_buf_cur_size += strlen(str)+1;
|
|
}
|
|
void msg_printf ( char *format, ... )
|
|
{
|
|
char buf[MAX_MSG_SIZE];
|
|
va_list args;
|
|
|
|
va_start( args, format );
|
|
vsnprintf( buf, MAX_MSG_SIZE, format, args ); buf[MAX_MSG_SIZE-1] = 0;
|
|
va_end( args );
|
|
msg_write_string( buf );
|
|
}
|
|
void msg_write ( int len, void *data )
|
|
{
|
|
unsigned char *ptr = msg_get_writeable_space( len );
|
|
if ( len > msg_buf_max_size ) return; /* would cause segfault */
|
|
memcpy( ptr, data, len );
|
|
*msg_buf_cur_size += len;
|
|
}
|
|
int msg_get_max_size()
|
|
{
|
|
return msg_buf_max_size;
|
|
}
|
|
bool msg_write_failed()
|
|
{
|
|
return msg_write_overflow;
|
|
}
|
|
|
|
int msg_read_exceeded = 0;
|
|
int msg_read_pos = 0;
|
|
|
|
static char* msg_get_readable_space( int size )
|
|
{
|
|
if ( msg_read_pos + size > net_buffer_cur_size ) {
|
|
msg_read_exceeded = 1;
|
|
msg_read_pos = 0;
|
|
}
|
|
return net_buffer + msg_read_pos;
|
|
|
|
}
|
|
|
|
bool msg_is_connectionless()
|
|
{
|
|
return ( *(int*)net_buffer == 0 );
|
|
}
|
|
void msg_begin_reading()
|
|
{
|
|
msg_read_exceeded = 0;
|
|
msg_read_pos = 0;
|
|
}
|
|
void msg_begin_connectionless_reading()
|
|
{
|
|
msg_read_exceeded = 0;
|
|
msg_read_pos = PACKET_HEADER_SIZE;
|
|
}
|
|
int msg_read_int8 ( void )
|
|
{
|
|
unsigned char *ptr = msg_get_readable_space( 1 );
|
|
msg_read_pos += 1;
|
|
return ptr[0];
|
|
}
|
|
int msg_read_int16( void )
|
|
{
|
|
unsigned char *ptr = msg_get_readable_space( 2 );
|
|
msg_read_pos += 2;
|
|
return ptr[0] + (ptr[1]<<8);
|
|
}
|
|
int msg_read_int32( void )
|
|
{
|
|
unsigned char *ptr = msg_get_readable_space( 4 );
|
|
msg_read_pos += 4;
|
|
return ptr[0] + (ptr[1]<<8) + (ptr[2]<<16) + (ptr[3]<<24);
|
|
}
|
|
char* msg_read_string( void )
|
|
{
|
|
static char buf[MAX_MSG_SIZE]; /* can't be bigger including trailing \0 */
|
|
char c;
|
|
int i = 0;
|
|
|
|
while ( 1 ) {
|
|
c = msg_read_int8();
|
|
buf[i++] = c;
|
|
if ( i == MAX_MSG_SIZE ) break; /* to be sure */
|
|
if ( c == 0 ) break;
|
|
}
|
|
buf[MAX_MSG_SIZE-1] = 0;
|
|
|
|
return buf;
|
|
}
|
|
void msg_read( int len, char *buf )
|
|
{
|
|
unsigned char *ptr = msg_get_readable_space( len );
|
|
msg_read_pos += len;
|
|
memcpy( buf, ptr, len );
|
|
}
|
|
|
|
bool msg_read_failed( void )
|
|
{
|
|
return msg_read_exceeded;
|
|
}
|
|
|
|
/* ********** TRANSMISSION ********** */
|
|
|
|
/* transmit a connectionless message */
|
|
void net_transmit_connectionless( NetAddr *addr, int len, char *data )
|
|
{
|
|
int cur_size;
|
|
char packet[PACKET_HEADER_SIZE + MAX_MSG_SIZE];
|
|
|
|
memset( packet, 0, PACKET_HEADER_SIZE ); /* connectionless header */
|
|
msg_begin_writing( packet + PACKET_HEADER_SIZE, &cur_size, MAX_MSG_SIZE );
|
|
msg_write( len, data );
|
|
if ( msg_write_failed() ) return;
|
|
|
|
net_send_packet( addr, PACKET_HEADER_SIZE + cur_size, packet );
|
|
|
|
if ( net_show_packets )
|
|
sys_printf( "--> %s: connectionless size=%i\n",
|
|
net_addr_to_string( addr ),
|
|
cur_size + PACKET_HEADER_SIZE );
|
|
}
|
|
|
|
void socket_init( NetSocket *sock, NetAddr *addr )
|
|
{
|
|
memset( sock, 0, sizeof( NetSocket ) );
|
|
|
|
sock->remote_addr = *addr;
|
|
sock->outgoing_id = 1; /* as id 0 means connectionless */
|
|
sock->idle_since = time(0);
|
|
|
|
//sys_printf( "%s: socket initiated\n", net_addr_to_string( addr ) );
|
|
}
|
|
|
|
void socket_reinit( NetSocket *socket ) /* with same address */
|
|
{
|
|
NetAddr addr = socket->remote_addr;
|
|
socket_init( socket, &addr );
|
|
}
|
|
|
|
void socket_print_stats( NetSocket *sock )
|
|
{
|
|
sys_printf ( "%s: STATISTICS\n drops: %i(%i packets)\n %i good packets\n",
|
|
net_addr_to_string( &sock->remote_addr ),
|
|
sock->drop_count, sock->dropped_packet_count,
|
|
sock->good_packet_count );
|
|
}
|
|
|
|
/* transmit CODE_RED/BLUE message and re-transmit dropped CODE_RED messages.
|
|
* length 0 messages are not transmitted. re-transmission of CODE_RED messages
|
|
* is handled in any case */
|
|
void socket_transmit( NetSocket *sock, int code, int len, char *data )
|
|
{
|
|
char packet[PACKET_HEADER_SIZE + MAX_MSG_SIZE];
|
|
int is_code_red = 0, cur_size = 0;
|
|
|
|
/* do not use sockets with fatal error */
|
|
if ( sock->fatal_error ) return;
|
|
|
|
/* add code red message to buffer */
|
|
if ( code == CODE_RED ) {
|
|
if ( sock->code_red_buf_cur_size + len > MAX_MSG_SIZE ) {
|
|
sock->fatal_error = 1; /* overflowed */
|
|
return;
|
|
}
|
|
memcpy( sock->code_red_buf + sock->code_red_buf_cur_size, data, len );
|
|
sock->code_red_buf_cur_size += len;
|
|
/* do not send the message if waiting for a receipt */
|
|
if ( sock->outgoing_code_red > 0 && !sock->retransmit_code_red_buf )
|
|
return;
|
|
else
|
|
is_code_red = 1;
|
|
}
|
|
|
|
if ( sock->retransmit_code_red_buf )
|
|
is_code_red = 1;
|
|
|
|
msg_begin_writing( packet, &cur_size, PACKET_HEADER_SIZE + MAX_MSG_SIZE );
|
|
msg_write_int32( sock->outgoing_id | (is_code_red<<31) );
|
|
msg_write_int32( sock->incoming_code_red );
|
|
|
|
if ( is_code_red )
|
|
sock->outgoing_code_red = sock->outgoing_id;
|
|
|
|
sock->outgoing_id++;
|
|
|
|
/* if code red buffer was not received resend it and clear the flag.
|
|
* if not waiting for a receipt (thus an initial code red message)
|
|
* send it as well */
|
|
if ( is_code_red ) {
|
|
msg_write( sock->code_red_buf_cur_size, sock->code_red_buf );
|
|
sock->retransmit_code_red_buf = 0;
|
|
}
|
|
|
|
/* add code blue packet if space is left */
|
|
if ( code == CODE_BLUE && cur_size + len <= msg_get_max_size() )
|
|
msg_write( len, data );
|
|
|
|
/* no data? */
|
|
if ( cur_size == PACKET_HEADER_SIZE ) return;
|
|
|
|
/* send packet */
|
|
net_send_packet( &sock->remote_addr, cur_size, packet );
|
|
#ifdef NET_DEBUG_MSG
|
|
printf( "OUT: "); msg_print_raw( cur_size, packet );
|
|
#endif
|
|
|
|
if ( net_show_packets )
|
|
sys_printf( "--> %s: out=%i(%s) size=%i in=%i(RED:%i)\n",
|
|
net_addr_to_string( &sock->remote_addr ),
|
|
sock->outgoing_id-1, is_code_red?"RED":"BLUE",
|
|
cur_size,
|
|
sock->incoming_id, sock->incoming_code_red );
|
|
}
|
|
|
|
/* update socker by net_packet header if net_sender_addr is sock.remote_addr
|
|
* and set read pointer to packet payload. if false is returned packet is
|
|
* not parsed. */
|
|
bool socket_process_header( NetSocket *sock )
|
|
{
|
|
int id, is_code_red, last_ack_code_red, drops = 0;
|
|
|
|
/* do not process fatal socks */
|
|
if ( sock->fatal_error ) return 0;
|
|
|
|
/* correct sock? */
|
|
if ( !net_compare_addr( &net_sender_addr, &sock->remote_addr ) )
|
|
return 0;
|
|
|
|
#ifdef NET_DEBUG_MSG
|
|
printf( " IN: " ); msg_print_raw( net_buffer_cur_size, net_buffer );
|
|
#endif
|
|
|
|
/* read header */
|
|
msg_begin_reading();
|
|
id = msg_read_int32(); /* new incoming id, the difference to sock->incoming_id is
|
|
the number of dropped packets */
|
|
is_code_red = id >> 31; /* highest bit tells wether code red packet */
|
|
id &= ~(1<<31);
|
|
last_ack_code_red = msg_read_int32(); /* last code red packet remote received */
|
|
|
|
/* duplicated packets are not handled */
|
|
if ( id <= sock->incoming_id ) {
|
|
if ( net_show_drops )
|
|
sys_printf( "%s: out of order packet %i at %i\n",
|
|
net_addr_to_string( &sock->remote_addr ),
|
|
id, sock->incoming_id );
|
|
return 0;
|
|
}
|
|
|
|
/* despite dropped packets this packet is handled */
|
|
if ( id > sock->incoming_id+1 ) {
|
|
drops = id - (sock->incoming_id+1);
|
|
sock->dropped_packet_count += drops;
|
|
sock->drop_count++;
|
|
if ( net_show_drops )
|
|
sys_printf( "%s: dropped %i packets at %i\n",
|
|
net_addr_to_string( &sock->remote_addr ),
|
|
drops, sock->incoming_id+1 );
|
|
}
|
|
sock->good_packet_count++;
|
|
|
|
sock->incoming_id = id; /* last incoming packet (no matter wether red or blue) */
|
|
if ( is_code_red )
|
|
sock->incoming_code_red = id; /* last code red packet socket received */
|
|
|
|
if ( sock->outgoing_code_red ) {
|
|
if ( sock->outgoing_code_red == last_ack_code_red ) {
|
|
/* was received so we can clear code_red buffer */
|
|
sock->outgoing_code_red = 0;
|
|
sock->code_red_buf_cur_size = 0;
|
|
}
|
|
else
|
|
if ( sock->outgoing_code_red > last_ack_code_red ) {
|
|
/* resend code_red buffer as it was dropped */
|
|
sock->retransmit_code_red_buf = 1;
|
|
}
|
|
else {
|
|
/* fatal error occured as remote received a newer code red packet
|
|
* although we had no receipt for the current one */
|
|
sock->fatal_error = 1;
|
|
}
|
|
}
|
|
|
|
sock->idle_since = time(0); /* reset idle time stamp */
|
|
|
|
/* if packet is empty mark it as unreadable */
|
|
if ( msg_read_pos == net_buffer_cur_size )
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|