medfall

A super great game engine
Log | Files | Refs

commit 5c65ef6dd31964c0a97cf67544122dfeb48047d4
parent ab76b763c7874ba8a54b9414ace02dd66d296540
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Fri Aug  4 09:32:39 -0700

Continue sockets overhaul

Diffstat:
hm.cc | 5+----
http.cc | 70+++++++++++++++++++++++++++++++++++++++++-----------------------------
http.h | 12++++++------
launcher/main.cc | 10+++++-----
platform_network.h | 122++++++++++++++++++++++++++++++++++++-------------------------------------------
5 files changed, 108 insertions(+), 111 deletions(-)
diff --git a/hm.cc b/hm.cc @@ -120,12 +120,9 @@ GAME_INIT( game_init ) { sock = sock_new_udp( NET_IPV4, NET_NONBLOCKING ); - struct sockaddr_storage sa = { }; - if( !dns_first( "localhost", &sa ) ) { + if( !dns_first( "localhost", &server_addr ) ) { FATAL( "dns_first" ); } - - server_addr = sockaddr_to_netaddress( sa ); server_addr.port = 13337; } diff --git a/http.cc b/http.cc @@ -6,14 +6,17 @@ #include <string> #include "intrinsics.h" +#include "array.h" #include "platform_io.h" #include "platform_network.h" #include "http.h" -bool dns_first( const char * host, struct sockaddr_storage * address ) { +bool dns( const char * host, array< NetAddress > out, size_t * n ) { + *n = 0; + struct addrinfo hints; memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_family = AF_INET; // TODO + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; @@ -23,40 +26,50 @@ bool dns_first( const char * host, struct sockaddr_storage * address ) { return false; } - memmove( address, addresses->ai_addr, addresses->ai_addrlen ); + struct addrinfo * cursor = addresses; + size_t i = 0; + while( cursor != NULL && i < out.n ) { + out[ i ] = sockaddr_to_netaddress( *( struct sockaddr_storage * ) cursor->ai_addr ); + cursor = cursor->ai_next; + i++; + } freeaddrinfo( addresses ); + *n = i; + return true; } -GetResult http_get( const struct sockaddr_storage & address, const char * host, const char * path, std::string * body, HttpProgressCallback progress_cb, void * cb_data ) { - int sock = socket( address.ss_family, SOCK_STREAM, 0 ); - if( sock == -1 ) { - return GET_ERROR_SOCKET; - } - SCOPE_EXIT( closesocket( sock ) ); +bool dns_first( const char * host, NetAddress * address ) { + StaticArray< NetAddress, 1 > addresses; + size_t n; + bool ok = dns( host, addresses, &n ); + if( !ok ) return false; + *address = addresses[ 0 ]; + return true; +} + +GetResult http_get( const NetAddress & addr, const char * host, const char * path, std::string * body, HttpProgressCallback progress_cb, void * cb_data ) { + TCPSocket sock = sock_new_tcp( addr.type, NET_BLOCKING ); + SCOPE_EXIT( sock_destroy( &sock ) ); - int address_size = address.ss_family == AF_INET ? sizeof( sockaddr_in ) : sizeof( sockaddr_in6 ); - if( connect( sock, ( const sockaddr * ) &address, address_size ) == -1 ) { - return GET_ERROR_CONNECT; + if( !sock_connect( sock, addr ) ) { + return GET_ERROR_CANT_CONNECT; } - char request[ 2048 ]; - int request_len = snprintf( request, sizeof( request ), - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" + // TODO: should just be a dynamic string + char request[ 1024 ]; + size_t request_len = ggformat( request, sizeof( request ), + "GET {} HTTP/1.1\r\n" + "Host: {}\r\n" "Connection: close\r\n" "User-Agent: medfall\r\n" // "Range: bytes=%llu-\r\n" "\r\n\r\n", path, host ); + ASSERT( request_len <= sizeof( request ) - 1 ); - if( request_len < 0 || checked_cast< size_t >( request_len ) >= sizeof( request ) ) { - return GET_ERROR_IO; - } - - ssize_t bytes_written = send( sock, request, request_len, 0 ); - if( bytes_written == -1 ) { - return GET_ERROR_IO; + if( !sock_send_tcp( sock, request, request_len ) ) { + return GET_ERROR_DISCONNECTED; } std::string response; @@ -65,28 +78,27 @@ GetResult http_get( const struct sockaddr_storage & address, const char * host, { fd_set select_fds; FD_ZERO( &select_fds ); - FD_SET( checked_cast< unsigned int >( sock ), &select_fds ); + FD_SET( checked_cast< unsigned int >( sock.fd ), &select_fds ); struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; - int ok = select( sock + 1, &select_fds, NULL, NULL, &timeout ); + int ok = select( sock.fd + 1, &select_fds, NULL, NULL, &timeout ); if( ok == 0 ) { return GET_ERROR_TIMEOUT; } if( ok == -1 ) { if( errno != EINTR ) continue; - return GET_ERROR_IO; + return GET_ERROR_DISCONNECTED; } } - ssize_t bytes_read = recv( sock, buf, sizeof( buf ), 0 ); + ssize_t bytes_read = sock_recv_tcp( sock, buf, sizeof( buf ) ); if( bytes_read == -1 ) { if( errno == EINTR ) continue; - return GET_ERROR_IO; + return GET_ERROR_DISCONNECTED; } - if( bytes_read == 0 ) { break; } diff --git a/http.h b/http.h @@ -3,15 +3,14 @@ #include <string> #include "intrinsics.h" +#include "array.h" #include "platform_network.h" enum GetResult { GET_OK, - GET_ERROR_DNS, - GET_ERROR_SOCKET, - GET_ERROR_CONNECT, - GET_ERROR_IO, + GET_ERROR_CANT_CONNECT, GET_ERROR_TIMEOUT, + GET_ERROR_DISCONNECTED, GET_ERROR_HTTP_BAD_REQUEST, GET_ERROR_HTTP_NOT_FOUND, GET_ERROR_HTTP_INTERNAL_SERVER_ERROR, @@ -21,5 +20,6 @@ enum GetResult { #define HTTP_PROGRESS_CALLBACK( f ) void f( size_t bytes_downloaded, void * data ) typedef HTTP_PROGRESS_CALLBACK( HttpProgressCallback ); -bool dns_first( const char * host, struct sockaddr_storage * address ); -GetResult http_get( const struct sockaddr_storage & address, const char * host, const char * path, std::string * body, HttpProgressCallback progress_cb = NULL, void * cb_data = NULL ); +bool dns( const char * host, array< NetAddress > out, size_t * n ); +bool dns_first( const char * host, NetAddress * address ); +GetResult http_get( const NetAddress & address, const char * host, const char * path, std::string * body, HttpProgressCallback progress_cb = NULL, void * cb_data = NULL ); diff --git a/launcher/main.cc b/launcher/main.cc @@ -292,7 +292,7 @@ struct Updater { UpdaterState state = UPDATER_DNS_RETRY; double retry_at; - struct sockaddr_storage address; + NetAddress address; Version local_version, remote_version; std::map< std::string, ManifestEntry > local_manifest, remote_manifest; @@ -311,7 +311,7 @@ static WorkQueue download_queue; #define HOST "medfall.mikejsavage.co.uk" static WORK_QUEUE_CALLBACK( lookup_update_server ) { - struct sockaddr_storage address; + NetAddress address; bool ok = dns_first( HOST, &address ); Updater * updater = SCOPED_ACQUIRE( locked_updater ); @@ -327,7 +327,7 @@ static WORK_QUEUE_CALLBACK( lookup_update_server ) { } static WORK_QUEUE_CALLBACK( download_remote_version ) { - struct sockaddr_storage address; + NetAddress address; { Updater * updater = SCOPED_ACQUIRE( locked_updater ); address = updater->address; @@ -355,7 +355,7 @@ static WORK_QUEUE_CALLBACK( download_remote_version ) { } static WORK_QUEUE_CALLBACK( download_manifest ) { - struct sockaddr_storage address; + NetAddress address; Version version; { Updater * updater = SCOPED_ACQUIRE( locked_updater ); @@ -431,7 +431,7 @@ static WORK_QUEUE_CALLBACK( download_file ) { std::string file_name; ManifestEntry entry; - struct sockaddr_storage address; + NetAddress address; Version version; { Updater * updater = SCOPED_ACQUIRE( locked_updater ); diff --git a/platform_network.h b/platform_network.h @@ -65,7 +65,11 @@ inline NetAddress sockaddr_to_netaddress( const sockaddr_storage & ss ) { return addr; } -inline struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr ) { +inline struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr, socklen_t * size = NULL ) { + if( size != NULL ) { + *size = addr.type == NET_IPV4 ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); + } + struct sockaddr_storage ss; if( addr.type == NET_IPV4 ) { struct sockaddr_in & sa4 = ( struct sockaddr_in & ) ss; @@ -92,45 +96,6 @@ inline struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr ) #error new platform #endif -inline void sock_bind( int fd, IPvX ipvx, u16 port ) { - { - int one = 1; - int ok = setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof( one ) ); - if( ok == -1 ) { - FATAL( "setsockopt" ); - } - } - - if( ipvx == NET_IPV4 ) { - sockaddr_in my_addr; - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons( port ); - my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); - int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); - if( ok == SOCKET_ERROR ) { - FATAL( "bind" ); - } - } - else { - sockaddr_in6 my_addr; - my_addr.sin6_family = AF_INET6; - my_addr.sin6_port = htons( port ); - my_addr.sin6_addr = in6addr_any; - int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); - if( ok == SOCKET_ERROR ) { - FATAL( "bind" ); - } - } -} - -inline void sock_bind( UDPSocket sock, u16 port ) { - sock_bind( sock.fd, sock.ipvx, port ); -} - -inline void sock_bind( TCPSocket sock, u16 port ) { - sock_bind( sock.fd, sock.ipvx, port ); -} - inline UDPSocket sock_new_udp( IPvX ipvx, NonblockingBool nonblocking ) { UDPSocket sock; @@ -165,52 +130,75 @@ inline TCPSocket sock_new_tcp( IPvX ipvx, NonblockingBool nonblocking ) { return sock; } -inline void sock_init( Socket * sock, IPvX ipvx, TransportProtocol transport, NonblockingBool nonblocking ) { - int domain = AF_INET; - if( ipvx == NET_IPV6 ) { - domain = AF_INET6; - } - - int type = SOCK_DGRAM; - int protocol = IPPROTO_UDP; - if( transport == NET_TCP ) { - type = SOCK_STREAM; - protocol = IPPROTO_TCP; - } - - sock->fd = socket( domain, type, protocol ); - sock->transport = transport; - sock->ipvx = ipvx; - if( sock->fd == INVALID_SOCKET ) { - FATAL( "socket" ); +inline void sock_bind( int fd, IPvX ipvx, u16 port ) { + { + int one = 1; + int ok = setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof( one ) ); + if( ok == -1 ) { + FATAL( "setsockopt" ); + } } - if( transport == NET_UDP ) { - // TODO IPv6 + if( ipvx == NET_IPV4 ) { sockaddr_in my_addr; my_addr.sin_family = AF_INET; - my_addr.sin_port = 0; + my_addr.sin_port = htons( port ); my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); - int ok = bind( sock->fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); + int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); + if( ok == SOCKET_ERROR ) { + FATAL( "bind" ); + } + } + else { + sockaddr_in6 my_addr; + my_addr.sin6_family = AF_INET6; + my_addr.sin6_port = htons( port ); + my_addr.sin6_addr = in6addr_any; + int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); if( ok == SOCKET_ERROR ) { FATAL( "bind" ); } } +} - if( nonblocking == NET_NONBLOCKING ) { - sock_fd_nonblocking( sock->fd ); +inline void sock_bind( UDPSocket sock, u16 port ) { + sock_bind( sock.fd, sock.ipvx, port ); +} + +inline void sock_bind( TCPSocket sock, u16 port ) { + sock_bind( sock.fd, sock.ipvx, port ); +} + +inline bool sock_connect( TCPSocket sock, const NetAddress & addr ) { + socklen_t sa_size; + struct sockaddr_storage sa = netaddress_to_sockaddr( addr, &sa_size ); + int ok = connect( sock.fd, ( const sockaddr * ) &sa, sa_size ); + if( ok == -1 ) { + // TODO: check for actual coding errors too + return false; } + return true; } -inline void sock_send_udp( UDPSocket sock, const char * data, size_t len, const NetAddress & addr ) { - struct sockaddr_storage sa = netaddress_to_sockaddr( addr ); - socklen_t sa_size = sock.ipvx == NET_IPV4 ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); +inline void sock_send_udp( UDPSocket sock, const void * data, size_t len, const NetAddress & addr ) { + socklen_t sa_size; + struct sockaddr_storage sa = netaddress_to_sockaddr( addr, &sa_size ); ssize_t ok = sendto( sock.fd, data, checked_cast< int >( len ), 0, ( struct sockaddr * ) &sa, sa_size ); if( ok == SOCKET_ERROR ) { FATAL( "sendto" ); } } +inline bool sock_send_tcp( TCPSocket sock, const void * data, size_t len ) { + ssize_t sent = send( sock.fd, data, len, 0 ); + if( sent < 0 ) return false; + return checked_cast< size_t >( sent ) == len; +} + +inline ssize_t sock_recv_tcp( TCPSocket sock, void * buf, size_t buf_size ) { + return recv( sock.fd, buf, buf_size, 0 ); +} + inline void sock_destroy( UDPSocket * sock ) { int ok = closesocket( sock->fd ); if( ok == -1 ) {