mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

platform_network.cc (5047B)


      1 #include "common.h"
      2 
      3 #include "platform.h"
      4 #include "platform_network.h"
      5 
      6 struct sockaddr_storage;
      7 
      8 static NetAddress sockaddr_to_netaddress( const struct sockaddr_storage & ss );
      9 static struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr );
     10 static void setsockoptone( PlatformSocket fd, int level, int opt );
     11 
     12 #if PLATFORM_WINDOWS
     13 #include "win32_network.cc"
     14 #elif PLATFORM_UNIX
     15 #include "unix_network.cc"
     16 #else
     17 #error new platform
     18 #endif
     19 
     20 bool operator==( const NetAddress & lhs, const NetAddress & rhs ) {
     21 	if( lhs.type != rhs.type ) return false;
     22 	if( lhs.port != rhs.port ) return false;
     23 	if( lhs.type == NET_IPV4 ) return memcmp( &lhs.ipv4, &rhs.ipv4, sizeof( lhs.ipv4 ) ) == 0;
     24 	return memcmp( &lhs.ipv6, &rhs.ipv6, sizeof( lhs.ipv6 ) ) == 0;
     25 }
     26 
     27 bool operator!=( const NetAddress & lhs, const NetAddress & rhs ) {
     28 	return !( lhs == rhs );
     29 }
     30 
     31 static socklen_t sockaddr_size( const struct sockaddr_storage & ss ) {
     32 	return ss.ss_family == AF_INET ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
     33 }
     34 
     35 static NetAddress sockaddr_to_netaddress( const struct sockaddr_storage & ss ) {
     36 	NetAddress addr;
     37 	if( ss.ss_family == AF_INET ) {
     38 		const struct sockaddr_in & sa4 = ( const struct sockaddr_in & ) ss;
     39 
     40 		addr.type = NET_IPV4;
     41 		addr.port = ntohs( sa4.sin_port );
     42 		memcpy( &addr.ipv4, &sa4.sin_addr.s_addr, sizeof( addr.ipv4 ) );
     43 	}
     44 	else {
     45 		const struct sockaddr_in6 & sa6 = ( const struct sockaddr_in6 & ) ss;
     46 
     47 		addr.type = NET_IPV6;
     48 		addr.port = ntohs( sa6.sin6_port );
     49 		memcpy( &addr.ipv6, &sa6.sin6_addr.s6_addr, sizeof( addr.ipv6 ) );
     50 	}
     51 	return addr;
     52 }
     53 
     54 static struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr ) {
     55 	struct sockaddr_storage ss;
     56 	if( addr.type == NET_IPV4 ) {
     57 		struct sockaddr_in & sa4 = ( struct sockaddr_in & ) ss;
     58 
     59 		ss.ss_family = AF_INET;
     60 		sa4.sin_port = htons( addr.port );
     61 		memcpy( &sa4.sin_addr.s_addr, &addr.ipv4, sizeof( addr.ipv4 ) );
     62 	}
     63 	else {
     64 		struct sockaddr_in6 & sa6 = ( struct sockaddr_in6 & ) ss;
     65 
     66 		ss.ss_family = AF_INET6;
     67 		sa6.sin6_port = htons( addr.port );
     68 		memcpy( &sa6.sin6_addr.s6_addr, &addr.ipv6, sizeof( addr.ipv6 ) );
     69 	}
     70 	return ss;
     71 }
     72 
     73 static void setsockoptone( PlatformSocket fd, int level, int opt ) {
     74 	int one = 1;
     75 	int ok = setsockopt( fd, level, opt, ( char * ) &one, sizeof( one ) );
     76 	if( ok == -1 ) {
     77 		FATAL( "setsockopt" );
     78 	}
     79 }
     80 
     81 #if PLATFORM_WINDOWS
     82 // TODO: horrible
     83 char last_error_str[ 1024 ];
     84 #endif
     85 
     86 bool net_new_tcp( TCPSocket * sock, const NetAddress & addr, const char ** err ) {
     87 	struct sockaddr_storage ss = netaddress_to_sockaddr( addr );
     88 	socklen_t ss_size = sockaddr_size( ss );
     89 
     90 	sock->fd = socket( ss.ss_family, SOCK_STREAM, IPPROTO_TCP );
     91 	if( sock->fd == INVALID_SOCKET )
     92 		FATAL( "socket" );
     93 
     94 	int ok = connect( sock->fd, ( const sockaddr * ) &ss, ss_size );
     95 	if( ok == -1 ) {
     96 		if( err != NULL ) {
     97 #if PLATFORM_WINDOWS
     98 			int error = WSAGetLastError();
     99 			FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
    100 				MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), last_error_str, sizeof( last_error_str ), NULL );
    101 
    102 			*err = last_error_str;
    103 #else
    104 			*err = strerror( errno );
    105 #endif
    106 		}
    107 		int ok_close = closesocket( sock->fd );
    108 		if( ok_close == -1 )
    109 			FATAL( "closesocket" );
    110 		// TODO: check for actual coding errors too
    111 		return false;
    112 	}
    113 
    114 	setsockoptone( sock->fd, SOL_SOCKET, SO_KEEPALIVE );
    115 	setsockoptone( sock->fd, IPPROTO_TCP, TCP_NODELAY );
    116 
    117 	platform_init_sock( sock->fd );
    118 
    119 	return true;
    120 }
    121 
    122 bool net_send( TCPSocket sock, const void * data, size_t len ) {
    123 	ssize_t sent = send( sock.fd, ( const char * ) data, len, NET_SEND_FLAGS );
    124 	if( sent < 0 ) return false;
    125 	return checked_cast< size_t >( sent ) == len;
    126 }
    127 
    128 TCPRecvResult net_recv( TCPSocket sock, void * buf, size_t buf_size, size_t * bytes_read ) {
    129 	while( true ) {
    130 		ssize_t r = recv( sock.fd, ( char * ) buf, buf_size, 0 );
    131 		// TODO: this is not right on windows
    132 		if( r == -1 ) {
    133 			if( errno == EINTR ) continue;
    134 			if( errno == ECONNRESET ) return TCP_ERROR;
    135 			FATAL( "recv" );
    136 		}
    137 
    138 		*bytes_read = checked_cast< size_t >( r );
    139 		return r == 0 ? TCP_CLOSED : TCP_OK;
    140 	}
    141 }
    142 
    143 void net_destroy( TCPSocket * sock ) {
    144 	int ok = closesocket( sock->fd );
    145 	if( ok == -1 ) {
    146 		FATAL( "closesocket" );
    147 	}
    148 	sock->fd = INVALID_SOCKET;
    149 }
    150 
    151 size_t dns( const char * host, NetAddress * out, size_t n ) {
    152 	struct addrinfo hints;
    153 	memset( &hints, 0, sizeof( struct addrinfo ) );
    154 	hints.ai_family = AF_UNSPEC;
    155 	// TODO: figure out why ivp6 doesn't work on windows
    156 	hints.ai_family = AF_INET;
    157 	hints.ai_socktype = SOCK_STREAM;
    158 	hints.ai_protocol = IPPROTO_TCP;
    159 
    160 	struct addrinfo * addresses;
    161 	int ok = getaddrinfo( host, "http", &hints, &addresses );
    162 	if( ok != 0 )
    163 		return 0;
    164 
    165 	struct addrinfo * cursor = addresses;
    166 	size_t i = 0;
    167 	while( cursor != NULL && i < n ) {
    168 		out[ i ] = sockaddr_to_netaddress( *( struct sockaddr_storage * ) cursor->ai_addr );
    169 		cursor = cursor->ai_next;
    170 		i++;
    171 	}
    172 	freeaddrinfo( addresses );
    173 
    174 	return i;
    175 }
    176 
    177 bool dns_first( const char * host, NetAddress * address ) {
    178 	return dns( host, address, 1 ) == 1;
    179 }