Files
commandergenius/project/jni/application/lbreakout2/common/net.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;
}