@ -16,10 +16,14 @@
# include "../../stdafx.h"
# include "../../date_func.h"
# include "../../debug.h"
# include "../../core/random_func.hpp"
# include "../../fios.h"
# include "udp.h"
# include "../../safeguards.h"
extern const uint8 _out_of_band_grf_md5 [ 16 ] ;
/**
* Create an UDP socket but don ' t listen yet .
* @ param bind the addresses to bind to .
@ -37,6 +41,8 @@ NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind)
* this - > bind . Append ( ) = NetworkAddress ( NULL , 0 , AF_INET ) ;
* this - > bind . Append ( ) = NetworkAddress ( NULL , 0 , AF_INET6 ) ;
}
this - > fragment_token = ( ( uint64 ) InteractiveRandom ( ) ) | ( ( ( uint64 ) InteractiveRandom ( ) ) < < 32 ) ;
}
@ -84,6 +90,33 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
{
if ( this - > sockets . Length ( ) = = 0 ) this - > Listen ( ) ;
if ( p - > size > SEND_MTU ) {
p - > PrepareToSend ( ) ;
uint64 token = this - > fragment_token + + ;
const uint PAYLOAD_MTU = SEND_MTU - ( 1 + 2 + 8 + 1 + 1 + 2 ) ;
const uint8 frag_count = ( p - > size + PAYLOAD_MTU - 1 ) / PAYLOAD_MTU ;
Packet frag ( PACKET_UDP_EX_MULTI ) ;
uint8 current_frag = 0 ;
uint16 offset = 0 ;
while ( offset < p - > size ) {
uint16 payload_size = min < uint > ( PAYLOAD_MTU , p - > size - offset ) ;
frag . Send_uint64 ( token ) ;
frag . Send_uint8 ( current_frag ) ;
frag . Send_uint8 ( frag_count ) ;
frag . Send_uint16 ( payload_size ) ;
frag . Send_binary ( ( const char * ) p - > buffer + offset , payload_size ) ;
current_frag + + ;
offset + = payload_size ;
this - > SendPacket ( & frag , recv , all , broadcast ) ;
frag . ResetState ( PACKET_UDP_EX_MULTI ) ;
}
assert_msg ( current_frag = = frag_count , " %u, %u " , current_frag , frag_count ) ;
return ;
}
for ( SocketList : : iterator s = this - > sockets . Begin ( ) ; s ! = this - > sockets . End ( ) ; s + + ) {
/* Make a local copy because if we resolve it we cannot
* easily unresolve it so we can resolve it later again . */
@ -142,7 +175,7 @@ void NetworkUDPSocketHandler::ReceivePackets()
/* If the size does not match the packet must be corrupted.
* Otherwise it will be marked as corrupted later on . */
if ( nbytes ! = p . size ) {
DEBUG ( net , 1 , " received a packet with mismatching size from %s " , address . GetAddressAsString ( ) ) ;
DEBUG ( net , 1 , " received a packet with mismatching size from %s , (%u, %u) " , address . GetAddressAsString ( ) , nbytes , p . size ) ;
continue ;
}
@ -183,11 +216,24 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
for ( c = info - > grfconfig ; c ! = NULL ; c = c - > next ) {
if ( ! HasBit ( c - > flags , GCF_STATIC ) ) count + + ;
}
p - > Send_uint8 ( count ) ; // Send number of GRFs
p - > Send_uint8 ( min < uint > ( count , NETWORK_MAX_GRF_COUNT ) ) ; // Send number of GRFs
/* Send actual GRF Identifications */
uint index = 0 ;
for ( c = info - > grfconfig ; c ! = NULL ; c = c - > next ) {
if ( ! HasBit ( c - > flags , GCF_STATIC ) ) this - > SendGRFIdentifier ( p , & c - > ident ) ;
if ( ! HasBit ( c - > flags , GCF_STATIC ) ) {
if ( index = = NETWORK_MAX_GRF_COUNT - 1 & & count > NETWORK_MAX_GRF_COUNT ) {
/* Send fake GRF ID */
p - > Send_uint32 ( 0x56D2B000 ) ;
p - > Send_binary ( ( const char * ) _out_of_band_grf_md5 , 16 ) ;
} else if ( index > = NETWORK_MAX_GRF_COUNT ) {
break ;
} else {
this - > SendGRFIdentifier ( p , & c - > ident ) ;
}
index + + ;
}
}
}
@ -202,7 +248,7 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
/* NETWORK_GAME_INFO_VERSION = 1 */
p - > Send_string ( info - > server_name ) ;
p - > Send_string ( info - > s erver_revision) ;
p - > Send_string ( info - > s hort_s erver_revision) ;
p - > Send_uint8 ( info - > server_lang ) ;
p - > Send_bool ( info - > use_password ) ;
p - > Send_uint8 ( info - > clients_max ) ;
@ -215,6 +261,56 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
p - > Send_bool ( info - > dedicated ) ;
}
/**
* Serializes the NetworkGameInfo struct to the packet
* @ param p the packet to write the data to
* @ param info the NetworkGameInfo struct to serialize
*/
void NetworkUDPSocketHandler : : SendNetworkGameInfoExtended ( Packet * p , const NetworkGameInfo * info , uint16 version )
{
p - > Send_uint8 ( 0 ) ; // version num
p - > Send_uint32 ( info - > game_date ) ;
p - > Send_uint32 ( info - > start_date ) ;
p - > Send_uint8 ( info - > companies_max ) ;
p - > Send_uint8 ( info - > companies_on ) ;
p - > Send_uint8 ( info - > spectators_max ) ;
p - > Send_string ( info - > server_name ) ;
p - > Send_string ( info - > server_revision ) ;
p - > Send_uint8 ( info - > server_lang ) ;
p - > Send_bool ( info - > use_password ) ;
p - > Send_uint8 ( info - > clients_max ) ;
p - > Send_uint8 ( info - > clients_on ) ;
p - > Send_uint8 ( info - > spectators_on ) ;
p - > Send_string ( info - > map_name ) ;
p - > Send_uint32 ( info - > map_width ) ;
p - > Send_uint32 ( info - > map_height ) ;
p - > Send_uint8 ( info - > map_set ) ;
p - > Send_bool ( info - > dedicated ) ;
{
/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
* the GRFs that are needed , i . e . the ones that the server has
* selected in the NewGRF GUI and not the ones that are used due
* to the fact that they are in [ newgrf - static ] in openttd . cfg */
const GRFConfig * c ;
uint count = 0 ;
/* Count number of GRFs to send information about */
for ( c = info - > grfconfig ; c ! = NULL ; c = c - > next ) {
if ( ! HasBit ( c - > flags , GCF_STATIC ) ) count + + ;
}
p - > Send_uint32 ( count ) ; // Send number of GRFs
/* Send actual GRF Identifications */
for ( c = info - > grfconfig ; c ! = NULL ; c = c - > next ) {
if ( ! HasBit ( c - > flags , GCF_STATIC ) ) {
this - > SendGRFIdentifier ( p , & c - > ident ) ;
}
}
}
}
/**
* Deserializes the NetworkGameInfo struct from the packet
* @ param p the packet to read the data from
@ -288,6 +384,60 @@ void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo
if ( info - > map_set > = NETWORK_NUM_LANDSCAPES ) info - > map_set = 0 ;
}
}
/**
* Deserializes the NetworkGameInfo struct from the packet
* @ param p the packet to read the data from
* @ param info the NetworkGameInfo to deserialize into
*/
void NetworkUDPSocketHandler : : ReceiveNetworkGameInfoExtended ( Packet * p , NetworkGameInfo * info )
{
static const Date MAX_DATE = ConvertYMDToDate ( MAX_YEAR , 11 , 31 ) ; // December is month 11
const uint8 version = p - > Recv_uint8 ( ) ;
if ( version > 0 ) return ; // Unknown version
info - > game_info_version = 255 ;
info - > game_date = Clamp ( p - > Recv_uint32 ( ) , 0 , MAX_DATE ) ;
info - > start_date = Clamp ( p - > Recv_uint32 ( ) , 0 , MAX_DATE ) ;
info - > companies_max = p - > Recv_uint8 ( ) ;
info - > companies_on = p - > Recv_uint8 ( ) ;
info - > spectators_max = p - > Recv_uint8 ( ) ;
p - > Recv_string ( info - > server_name , sizeof ( info - > server_name ) ) ;
p - > Recv_string ( info - > server_revision , sizeof ( info - > server_revision ) ) ;
info - > server_lang = p - > Recv_uint8 ( ) ;
info - > use_password = p - > Recv_bool ( ) ;
info - > clients_max = p - > Recv_uint8 ( ) ;
info - > clients_on = p - > Recv_uint8 ( ) ;
info - > spectators_on = p - > Recv_uint8 ( ) ;
p - > Recv_string ( info - > map_name , sizeof ( info - > map_name ) ) ;
info - > map_width = p - > Recv_uint32 ( ) ;
info - > map_height = p - > Recv_uint32 ( ) ;
info - > map_set = p - > Recv_uint8 ( ) ;
info - > dedicated = p - > Recv_bool ( ) ;
{
GRFConfig * * dst = & info - > grfconfig ;
uint i ;
uint num_grfs = p - > Recv_uint32 ( ) ;
/* Broken/bad data. It cannot have that many NewGRFs. */
if ( num_grfs > MAX_NEWGRFS ) return ;
for ( i = 0 ; i < num_grfs ; i + + ) {
GRFConfig * c = new GRFConfig ( ) ;
this - > ReceiveGRFIdentifier ( p , & c - > ident ) ;
this - > HandleIncomingNetworkGameInfoGRFConfig ( c ) ;
/* Append GRFConfig to the list */
* dst = c ;
dst = & c - > next ;
}
}
if ( info - > server_lang > = NETWORK_NUM_LANGUAGES ) info - > server_lang = 0 ;
if ( info - > map_set > = NETWORK_NUM_LANDSCAPES ) info - > map_set = 0 ;
}
/**
* Handle an incoming packets by sending it to the correct function .
@ -317,6 +467,9 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_
case PACKET_UDP_SERVER_NEWGRFS : this - > Receive_SERVER_NEWGRFS ( p , client_addr ) ; break ;
case PACKET_UDP_MASTER_SESSION_KEY : this - > Receive_MASTER_SESSION_KEY ( p , client_addr ) ; break ;
case PACKET_UDP_EX_MULTI : this - > Receive_EX_MULTI ( p , client_addr ) ; break ;
case PACKET_UDP_EX_SERVER_RESPONSE : this - > Receive_EX_SERVER_RESPONSE ( p , client_addr ) ; break ;
default :
if ( this - > HasClientQuit ( ) ) {
DEBUG ( net , 0 , " [udp] received invalid packet type %d from %s " , type , client_addr - > GetAddressAsString ( ) ) ;
@ -327,6 +480,74 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_
}
}
void NetworkUDPSocketHandler : : Receive_EX_MULTI ( Packet * p , NetworkAddress * client_addr )
{
uint64 token = p - > Recv_uint64 ( ) ;
uint8 index = p - > Recv_uint8 ( ) ;
uint8 total = p - > Recv_uint8 ( ) ;
uint16 payload_size = p - > Recv_uint16 ( ) ;
DEBUG ( net , 6 , " [udp] received multi-part packet from %s: " OTTD_PRINTFHEX64 " , %u/%u, %u bytes " ,
client_addr - > GetAddressAsString ( ) , token , index , total , payload_size ) ;
if ( total = = 0 | | index > = total ) return ;
if ( ! p - > CanReadFromPacket ( payload_size ) ) return ;
time_t cur_time = time ( NULL ) ;
auto add_to_fragment = [ & ] ( FragmentSet & fs ) {
fs . fragments [ index ] . assign ( ( const char * ) p - > buffer + p - > pos , payload_size ) ;
uint total_payload = 0 ;
for ( auto & frag : fs . fragments ) {
if ( ! frag . size ( ) ) return ;
total_payload + = frag . size ( ) ;
}
DEBUG ( net , 6 , " [udp] merged multi-part packet from %s: " OTTD_PRINTFHEX64 " , %u bytes " ,
client_addr - > GetAddressAsString ( ) , token , total_payload ) ;
Packet merged ( this ) ;
for ( auto & frag : fs . fragments ) {
merged . Send_binary ( frag . data ( ) , frag . size ( ) ) ;
}
merged . PrepareToRead ( ) ;
/* If the size does not match the packet must be corrupted.
* Otherwise it will be marked as corrupted later on . */
if ( total_payload ! = merged . size ) {
DEBUG ( net , 1 , " received an extended packet with mismatching size from %s, (%u, %u) " ,
client_addr - > GetAddressAsString ( ) , total_payload , merged . size ) ;
} else {
this - > HandleUDPPacket ( & merged , client_addr ) ;
}
fs = this - > fragments . back ( ) ;
this - > fragments . pop_back ( ) ;
} ;
uint i = 0 ;
while ( i < this - > fragments . size ( ) ) {
FragmentSet & fs = this - > fragments [ i ] ;
if ( fs . create_time < cur_time - 10 ) {
fs = this - > fragments . back ( ) ;
this - > fragments . pop_back ( ) ;
continue ;
}
if ( fs . token = = token & & fs . address = = * client_addr & & fs . fragments . size ( ) = = total ) {
add_to_fragment ( fs ) ;
return ;
}
i + + ;
}
this - > fragments . push_back ( { token , * client_addr , cur_time , { } } ) ;
this - > fragments . back ( ) . fragments . resize ( total ) ;
add_to_fragment ( this - > fragments . back ( ) ) ;
}
/**
* Helper for logging receiving invalid packets .
* @ param type The received packet type .
@ -339,6 +560,7 @@ void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type, NetworkAd
void NetworkUDPSocketHandler : : Receive_CLIENT_FIND_SERVER ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_CLIENT_FIND_SERVER , client_addr ) ; }
void NetworkUDPSocketHandler : : Receive_SERVER_RESPONSE ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_SERVER_RESPONSE , client_addr ) ; }
void NetworkUDPSocketHandler : : Receive_EX_SERVER_RESPONSE ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_EX_SERVER_RESPONSE , client_addr ) ; }
void NetworkUDPSocketHandler : : Receive_CLIENT_DETAIL_INFO ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_CLIENT_DETAIL_INFO , client_addr ) ; }
void NetworkUDPSocketHandler : : Receive_SERVER_DETAIL_INFO ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_SERVER_DETAIL_INFO , client_addr ) ; }
void NetworkUDPSocketHandler : : Receive_SERVER_REGISTER ( Packet * p , NetworkAddress * client_addr ) { this - > ReceiveInvalidPacket ( PACKET_UDP_SERVER_REGISTER , client_addr ) ; }