medfall

A super great game engine
Log | Files | Refs

platform_network.cc (7776B)


      1 #include "platform.h"
      2 #include "array.h"
      3 #include "ggformat.h"
      4 #include "log.h"
      5 
      6 #include "platform_network.h"
      7 
      8 struct sockaddr_storage;
      9 
     10 static NetAddress sockaddr_to_netaddress( const struct sockaddr_storage & ss );
     11 static struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr );
     12 static void setsockoptone( OSSocket fd, int level, int opt );
     13 
     14 #if PLATFORM_WINDOWS
     15 #include "win32_network.cc"
     16 #elif PLATFORM_UNIX
     17 #include "unix_network.cc"
     18 #else
     19 #error new platform
     20 #endif
     21 
     22 bool operator==( const NetAddress & lhs, const NetAddress & rhs ) {
     23 	if( lhs.type != rhs.type ) return false;
     24 	if( lhs.port != rhs.port ) return false;
     25 	if( lhs.type == NET_IPV4 ) return memcmp( &lhs.ipv4, &rhs.ipv4, sizeof( lhs.ipv4 ) ) == 0;
     26 	return memcmp( &lhs.ipv6, &rhs.ipv6, sizeof( lhs.ipv6 ) ) == 0;
     27 }
     28 
     29 bool operator!=( const NetAddress & lhs, const NetAddress & rhs ) {
     30 	return !( lhs == rhs );
     31 }
     32 
     33 void format( FormatBuffer * fb, const NetAddress & addr, const FormatOpts & opts ) {
     34 	char buf[ INET6_ADDRSTRLEN ];
     35 	const sockaddr_storage ss = netaddress_to_sockaddr( addr );
     36 
     37 	void * src = ( void * ) &( ( sockaddr_in * ) &ss )->sin_addr;
     38 	if( ss.ss_family == AF_INET6 ) {
     39 		src = ( void * ) &( ( sockaddr_in6 * ) &ss )->sin6_addr;
     40 	}
     41 	inet_ntop( ss.ss_family, src, buf, sizeof( buf ) );
     42 
     43 	format( fb, buf );
     44 	format( fb, ":" );
     45 	format( fb, addr.port, FormatOpts() );
     46 }
     47 
     48 static socklen_t sockaddr_size( const struct sockaddr_storage & ss ) {
     49 	return ss.ss_family == AF_INET ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
     50 }
     51 
     52 static NetAddress sockaddr_to_netaddress( const struct sockaddr_storage & ss ) {
     53 	NetAddress addr;
     54 	if( ss.ss_family == AF_INET ) {
     55 		const struct sockaddr_in & sa4 = ( const struct sockaddr_in & ) ss;
     56 
     57 		addr.type = NET_IPV4;
     58 		addr.port = ntohs( sa4.sin_port );
     59 		memcpy( &addr.ipv4, &sa4.sin_addr.s_addr, sizeof( addr.ipv4 ) );
     60 	}
     61 	else {
     62 		const struct sockaddr_in6 & sa6 = ( const struct sockaddr_in6 & ) ss;
     63 
     64 		addr.type = NET_IPV6;
     65 		addr.port = ntohs( sa6.sin6_port );
     66 		memcpy( &addr.ipv6, &sa6.sin6_addr.s6_addr, sizeof( addr.ipv6 ) );
     67 	}
     68 	return addr;
     69 }
     70 
     71 static struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr ) {
     72 	struct sockaddr_storage ss;
     73 	if( addr.type == NET_IPV4 ) {
     74 		struct sockaddr_in & sa4 = ( struct sockaddr_in & ) ss;
     75 
     76 		ss.ss_family = AF_INET;
     77 		sa4.sin_port = htons( addr.port );
     78 		memcpy( &sa4.sin_addr.s_addr, &addr.ipv4, sizeof( addr.ipv4 ) );
     79 	}
     80 	else {
     81 		struct sockaddr_in6 & sa6 = ( struct sockaddr_in6 & ) ss;
     82 
     83 		ss.ss_family = AF_INET6;
     84 		sa6.sin6_port = htons( addr.port );
     85 		memcpy( &sa6.sin6_addr.s6_addr, &addr.ipv6, sizeof( addr.ipv6 ) );
     86 	}
     87 	return ss;
     88 }
     89 
     90 static void setsockoptone( OSSocket fd, int level, int opt ) {
     91 	int one = 1;
     92 	int ok = setsockopt( fd, level, opt, ( char * ) &one, sizeof( one ) );
     93 	if( ok == -1 ) {
     94 		FATAL( "setsockopt" );
     95 	}
     96 }
     97 
     98 UDPSocket net_new_udp( NonblockingBool nonblocking, u16 port ) {
     99 	UDPSocket sock;
    100 
    101 	sock.ipv4 = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
    102 	if( sock.ipv4 == INVALID_SOCKET ) {
    103 		FATAL( "socket" );
    104 	}
    105 	sock.ipv6 = socket( AF_INET6, SOCK_DGRAM, IPPROTO_UDP );
    106 	if( sock.ipv6 == INVALID_SOCKET ) {
    107 		FATAL( "socket" );
    108 	}
    109 
    110 	if( nonblocking == NET_NONBLOCKING ) {
    111 		make_socket_nonblocking( sock.ipv4 );
    112 		make_socket_nonblocking( sock.ipv6 );
    113 	}
    114 
    115 	if( port != 0 ) {
    116 		setsockoptone( sock.ipv4, SOL_SOCKET, SO_REUSEADDR );
    117 		setsockoptone( sock.ipv6, SOL_SOCKET, SO_REUSEADDR );
    118 		setsockoptone( sock.ipv6, IPPROTO_IPV6, IPV6_V6ONLY );
    119 	}
    120 
    121 	platform_init_sock( sock.ipv4 );
    122 	platform_init_sock( sock.ipv6 );
    123 
    124 	{
    125 		sockaddr_in my_addr4;
    126 		my_addr4.sin_family = AF_INET;
    127 		my_addr4.sin_port = htons( port );
    128 		my_addr4.sin_addr.s_addr = htonl( INADDR_ANY );
    129 		int ok = bind( sock.ipv4, ( struct sockaddr * ) &my_addr4, sizeof( my_addr4 ) );
    130 		if( ok == SOCKET_ERROR ) {
    131 			FATAL( "bind" );
    132 		}
    133 	}
    134 
    135 	{
    136 		sockaddr_in6 my_addr6;
    137 		my_addr6.sin6_family = AF_INET6;
    138 		my_addr6.sin6_port = htons( port );
    139 		my_addr6.sin6_addr = in6addr_any;
    140 		int ok = bind( sock.ipv6, ( struct sockaddr * ) &my_addr6, sizeof( my_addr6 ) );
    141 		if( ok == SOCKET_ERROR ) {
    142 			FATAL( "bind" );
    143 		}
    144 	}
    145 
    146 	return sock;
    147 }
    148 
    149 void net_send( UDPSocket sock, const void * data, size_t len, const NetAddress & addr ) {
    150 	struct sockaddr_storage ss = netaddress_to_sockaddr( addr );
    151 	socklen_t ss_size = sockaddr_size( ss );
    152 	OSSocket fd = addr.type == NET_IPV4 ? sock.ipv4 : sock.ipv6;
    153 	ssize_t ok = sendto( fd, ( const char * ) data, checked_cast< int >( len ), NET_SEND_FLAGS, ( struct sockaddr * ) &ss, ss_size );
    154 	if( ok == SOCKET_ERROR ) {
    155 		FATAL( "sendto" );
    156 	}
    157 }
    158 
    159 void net_destroy( UDPSocket * sock ) {
    160 	int ok4 = closesocket( sock->ipv4 );
    161 	if( ok4 == -1 ) {
    162 		FATAL( "closesocket" );
    163 	}
    164 	int ok6 = closesocket( sock->ipv6 );
    165 	if( ok6 == -1 ) {
    166 		FATAL( "closesocket" );
    167 	}
    168 	sock->ipv4 = INVALID_SOCKET;
    169 	sock->ipv6 = INVALID_SOCKET;
    170 }
    171 
    172 bool net_new_tcp( TCPSocket * sock, const NetAddress & addr, NonblockingBool nonblocking ) {
    173 	struct sockaddr_storage ss = netaddress_to_sockaddr( addr );
    174 	socklen_t ss_size = sockaddr_size( ss );
    175 
    176 	sock->fd = socket( ss.ss_family, SOCK_STREAM, IPPROTO_TCP );
    177 	if( sock->fd == INVALID_SOCKET ) {
    178 		FATAL( "socket" );
    179 	}
    180 
    181 	int ok = connect( sock->fd, ( const sockaddr * ) &ss, ss_size );
    182 	if( ok == -1 ) {
    183 		int ok_close = closesocket( sock->fd );
    184 		if( ok_close == -1 ) {
    185 			FATAL( "closesocket" );
    186 		}
    187 		// TODO: check for actual coding errors too
    188 		return false;
    189 	}
    190 
    191 	if( nonblocking == NET_NONBLOCKING ) {
    192 		make_socket_nonblocking( sock->fd );
    193 	}
    194 
    195 	platform_init_sock( sock->fd );
    196 
    197 	return true;
    198 }
    199 
    200 bool net_send( TCPSocket sock, const void * data, size_t len ) {
    201 	ssize_t sent = send( sock.fd, ( const char * ) data, len, NET_SEND_FLAGS );
    202 	if( sent < 0 ) return false;
    203 	return checked_cast< size_t >( sent ) == len;
    204 }
    205 
    206 TCPRecvResult net_recv( TCPSocket sock, void * buf, size_t buf_size, size_t * bytes_read, u32 timeout_ms ) {
    207 	while( true ) {
    208 		if( timeout_ms > 0 ) {
    209 			fd_set fds;
    210 			FD_ZERO( &fds );
    211 			FD_SET( sock.fd, &fds );
    212 
    213 			struct timeval tv;
    214 			tv.tv_sec = timeout_ms / 1000;
    215 			tv.tv_usec = ( timeout_ms % 1000 ) * 1000;
    216 
    217 			int ok = select( sock.fd + 1, &fds, NULL, NULL, &tv );
    218 			// TODO: update timeout
    219 			if( ok == 0 ) {
    220 				return TCP_TIMEOUT;
    221 			}
    222 			if( ok == -1 ) {
    223 				if( errno != EINTR ) continue;
    224 				FATAL( "select" );
    225 			}
    226 		}
    227 
    228 		ssize_t r = recv( sock.fd, ( char * ) buf, buf_size, 0 );
    229 		// TODO: this is not right on windows
    230 		if( r == -1 ) {
    231 			if( errno == EINTR ) continue;
    232 			if( errno == ECONNRESET ) return TCP_ERROR;
    233 			FATAL( "recv" );
    234 		}
    235 
    236 		*bytes_read = checked_cast< size_t >( r );
    237 		return r == 0 ? TCP_CLOSED : TCP_OK;
    238 	}
    239 }
    240 
    241 void net_destroy( TCPSocket * sock ) {
    242 	int ok = closesocket( sock->fd );
    243 	if( ok == -1 ) {
    244 		FATAL( "closesocket" );
    245 	}
    246 	sock->fd = INVALID_SOCKET;
    247 }
    248 
    249 array< NetAddress > dns( const char * host, array< NetAddress > out ) {
    250 	struct addrinfo hints;
    251 	memset( &hints, 0, sizeof( struct addrinfo ) );
    252 	hints.ai_family = AF_UNSPEC;
    253 	// TODO: figure out why ivp6 doesn't work on windows
    254 	hints.ai_family = AF_INET;
    255 	hints.ai_socktype = SOCK_STREAM;
    256 	hints.ai_protocol = IPPROTO_TCP;
    257 
    258 	struct addrinfo * addresses;
    259 	int ok = getaddrinfo( host, "http", &hints, &addresses );
    260 	if( ok != 0 ) {
    261 		return out.slice( 0, 0 );
    262 	}
    263 
    264 	struct addrinfo * cursor = addresses;
    265 	size_t i = 0;
    266 	while( cursor != NULL && i < out.n ) {
    267 		out[ i ] = sockaddr_to_netaddress( *( struct sockaddr_storage * ) cursor->ai_addr );
    268 		cursor = cursor->ai_next;
    269 		i++;
    270 	}
    271 	freeaddrinfo( addresses );
    272 
    273 	return out.slice( 0, i );
    274 }
    275 
    276 bool dns_first( const char * host, NetAddress * address ) {
    277 	StaticArray< NetAddress, 1 > storage;
    278 	array< NetAddress > addresses = dns( host, storage );
    279 	if( addresses.n == 0 ) return false;
    280 	*address = addresses[ 0 ];
    281 	return true;
    282 }