2019-07-17 13:37:18 -04:00
# include <unistd.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <algorithm>
# include <arpa/inet.h>
# include <log/LogUtils.h>
# include <misc/endianness.h>
# include <LicenseRequest.pb.h>
# include <shared/License.h>
# include <shared/crypt.h>
# include <misc/base64.h>
# include "LicenseServer.h"
# include "crypt.h"
# include "UserManager.h"
using namespace std ;
using namespace std : : chrono ;
using namespace license ;
using namespace ts ;
LicenseServer : : LicenseServer ( const sockaddr_in & addr ,
const std : : shared_ptr < server : : LicenseManager > & manager ,
const shared_ptr < license : : stats : : StatisticManager > & stats ,
const shared_ptr < license : : web : : WebStatistics > & wstats ,
const std : : shared_ptr < UserManager > & user_manager ) : manager ( manager ) , statistics ( stats ) , web_statistics ( wstats ) , user_manager ( user_manager ) {
this - > localAddr = new sockaddr_in { } ;
memcpy ( this - > localAddr , & addr , sizeof ( addr ) ) ;
}
LicenseServer : : ~ LicenseServer ( ) {
this - > stopServer ( ) ;
delete this - > localAddr ;
}
# define SFAIL(message) \
do { \
logError ( lstream < < ( message ) < < " Message: " < < errno < < " / " < < strerror ( errno ) ) ; \
this - > stopServer ( ) ; \
return false ; \
} while ( 0 )
static int enabled = 1 ;
static int disabled = 0 ;
bool LicenseServer : : startServer ( ) {
{
lock_guard lock ( this - > lock ) ;
if ( this - > running ) return false ;
this - > running = true ;
}
fileDescriptor = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( fileDescriptor < 0 ) SFAIL ( " Could not create new socket " ) ;
if ( setsockopt ( this - > fileDescriptor , SOL_SOCKET , SO_REUSEADDR , & enabled , sizeof ( enabled ) ) < 0 ) SFAIL ( " could not set reuse address " ) ;
if ( setsockopt ( this - > fileDescriptor , IPPROTO_TCP , TCP_CORK , & disabled , sizeof ( disabled ) ) < 0 ) SFAIL ( " could not set no push " ) ;
if ( bind ( this - > fileDescriptor , ( struct sockaddr * ) this - > localAddr , sizeof ( sockaddr_in ) ) < 0 ) SFAIL ( " Could not bind socket on " + string ( inet_ntoa ( this - > localAddr - > sin_addr ) ) ) ;
if ( listen ( this - > fileDescriptor , 32 ) < 0 ) SFAIL ( " Could not listen on socket " ) ;
this - > evBase = event_base_new ( ) ;
this - > acceptEvent = event_new ( this - > evBase , this - > fileDescriptor , EV_READ | EV_PERSIST , LicenseServer : : handleEventAccept , this ) ;
this - > client_cleanup = evtimer_new ( this - > evBase , LicenseServer : : handleEventCleanup , this ) ;
event_add ( this - > acceptEvent , nullptr ) ;
{
timeval now { 1 , 0 } ;
evtimer_add ( this - > client_cleanup , & now ) ;
}
evBaseDispatch = new threads : : Thread ( THREAD_SAVE_OPERATIONS , [ & ] ( ) {
signal ( SIGPIPE , SIG_IGN ) ;
event_base_dispatch ( this - > evBase ) ;
} ) ;
return true ;
}
void LicenseServer : : stopServer ( ) {
{
lock_guard lock ( this - > lock ) ;
if ( ! this - > running ) return ;
this - > running = false ;
}
for ( const auto & client : this - > getClients ( ) )
this - > closeConnection ( client ) ;
if ( this - > acceptEvent ) {
event_del ( this - > acceptEvent ) ;
event_free ( this - > acceptEvent ) ;
}
this - > acceptEvent = nullptr ;
if ( this - > client_cleanup ) {
event_del_block ( this - > client_cleanup ) ;
event_free ( this - > client_cleanup ) ;
this - > client_cleanup = nullptr ;
}
if ( this - > evBase )
event_base_loopbreak ( this - > evBase ) ;
if ( this - > evBaseDispatch )
this - > evBaseDispatch - > join ( ) ;
delete this - > evBaseDispatch ;
this - > evBaseDispatch = nullptr ;
if ( this - > evBase ) {
event_base_loopbreak ( this - > evBase ) ; /* again for some reason */
event_base_free ( this - > evBase ) ;
}
this - > evBase = nullptr ;
if ( this - > fileDescriptor ! = 0 ) {
shutdown ( this - > fileDescriptor , SHUT_RDWR ) ;
close ( this - > fileDescriptor ) ;
this - > fileDescriptor = 0 ;
}
}
void LicenseServer : : handleEventCleanup ( int , short , void * ptrServer ) {
auto server = static_cast < LicenseServer * > ( ptrServer ) ;
server - > cleanup_clients ( ) ;
timeval next { 1 , 0 } ;
if ( server - > client_cleanup )
event_add ( server - > client_cleanup , & next ) ;
}
//Basic IO
void LicenseServer : : handleEventWrite ( int fd , short , void * ptrServer ) {
auto server = static_cast < LicenseServer * > ( ptrServer ) ;
auto client = server - > findClient ( fd ) ;
if ( ! client ) return ;
buffer : : RawBuffer * buffer = nullptr ;
{
threads : : MutexLock lock ( client - > network . lock ) ;
buffer = TAILQ_FIRST ( & client - > network . writeQueue ) ;
if ( ! buffer ) return ;
auto writtenBytes = send ( fd , & buffer - > buffer [ buffer - > index ] , buffer - > length - buffer - > index , 0 ) ;
2019-11-22 14:51:00 -05:00
if ( writtenBytes < = 0 ) {
if ( writtenBytes = = - 1 & & errno = = EAGAIN )
return ;
logError ( LOG_LICENSE_CONTROLL , " Invalid write. Disconnecting remote client. Message: {}/{} " , errno , strerror ( errno ) ) ;
} else {
buffer - > index + = writtenBytes ;
}
2019-07-17 13:37:18 -04:00
if ( buffer - > index > = buffer - > length ) {
TAILQ_REMOVE ( & client - > network . writeQueue , buffer , tail ) ;
delete buffer ;
}
if ( ! TAILQ_EMPTY ( & client - > network . writeQueue ) )
event_add ( client - > network . writeEvent , nullptr ) ;
}
}
void ConnectedClient : : sendPacket ( const protocol : : packet & packet ) {
packet . prepare ( ) ;
auto buffer = new buffer : : RawBuffer ( packet . data . length ( ) + sizeof ( packet . header ) ) ;
memcpy ( buffer - > buffer , & packet . header , sizeof ( packet . header ) ) ;
memcpy ( & buffer - > buffer [ sizeof ( packet . header ) ] , packet . data . data ( ) , packet . data . length ( ) ) ;
if ( ! this - > protocol . cryptKey . empty ( ) )
xorBuffer ( & buffer - > buffer [ sizeof ( packet . header ) ] , packet . data . length ( ) , this - > protocol . cryptKey . data ( ) , this - > protocol . cryptKey . length ( ) ) ;
{
threads : : MutexLock lock ( this - > network . lock ) ;
TAILQ_INSERT_TAIL ( & this - > network . writeQueue , buffer , tail ) ;
}
event_add ( this - > network . writeEvent , nullptr ) ;
}
void ConnectedClient : : init ( ) {
protocol . last_read = std : : chrono : : system_clock : : now ( ) ;
TAILQ_INIT ( & network . writeQueue ) ;
}
void ConnectedClient : : uninit ( ) {
{
threads : : MutexLock lock ( this - > network . lock ) ;
ts : : buffer : : RawBuffer * buffer ;
while ( ( buffer = TAILQ_FIRST ( & this - > network . writeQueue ) ) ) {
TAILQ_REMOVE ( & this - > network . writeQueue , buffer , tail ) ;
delete buffer ;
}
}
if ( network . fileDescriptor > 0 ) {
shutdown ( this - > network . fileDescriptor , SHUT_RDWR ) ;
close ( this - > network . fileDescriptor ) ;
network . fileDescriptor = 0 ;
}
if ( this - > network . readEvent ) event_del ( this - > network . readEvent ) ;
this - > network . readEvent = nullptr ;
if ( this - > network . writeEvent ) event_del ( this - > network . writeEvent ) ;
this - > network . writeEvent = nullptr ;
}
void LicenseServer : : handleEventRead ( int fd , short , void * ptrServer ) {
auto server = static_cast < LicenseServer * > ( ptrServer ) ;
auto client = server - > findClient ( fd ) ;
if ( ! client ) return ;
char buffer [ 1024 ] ;
sockaddr_in remoteAddress { } ;
socklen_t remoteAddressSize = sizeof ( remoteAddress ) ;
auto read = recvfrom ( fd , buffer , 1024 , 0 , reinterpret_cast < sockaddr * > ( & remoteAddress ) , & remoteAddressSize ) ;
if ( read < 0 ) {
if ( errno = = EWOULDBLOCK ) return ;
2019-11-22 14:51:00 -05:00
logError ( LOG_LICENSE_CONTROLL , " Invalid read. Disconnecting remote client. Message: {}/{} " , errno , strerror ( errno ) ) ;
2019-07-17 13:37:18 -04:00
event_del_noblock ( client - > network . readEvent ) ;
server - > closeConnection ( client ) ;
return ;
} else if ( read = = 0 ) {
logError ( LOG_LICENSE_CONTROLL , " Invalid read. Disconnecting remote client " ) ;
event_del_noblock ( client - > network . readEvent ) ;
server - > closeConnection ( client ) ;
return ;
}
client - > protocol . last_read = std : : chrono : : system_clock : : now ( ) ;
server - > handleMessage ( client , string ( buffer , read ) ) ;
}
void LicenseServer : : handleEventAccept ( int fd , short , void * ptrServer ) {
auto server = static_cast < LicenseServer * > ( ptrServer ) ;
auto client = make_shared < ConnectedClient > ( ) ;
client - > init ( ) ;
socklen_t client_len = sizeof ( client - > network . remoteAddr ) ;
client - > network . fileDescriptor = accept ( fd , ( struct sockaddr * ) & client - > network . remoteAddr , & client_len ) ;
if ( setsockopt ( client - > network . fileDescriptor , SOL_SOCKET , SO_REUSEADDR , & enabled , sizeof ( enabled ) ) < 0 ) ; // CERR("could not set reuse addr");
if ( setsockopt ( client - > network . fileDescriptor , IPPROTO_TCP , TCP_CORK , & disabled , sizeof ( disabled ) ) < 0 ) ; // CERR("could not set no push");
if ( client - > network . fileDescriptor < 0 ) {
logCritical ( " Could not accept new client! ( " + to_string ( client - > network . fileDescriptor ) + " | " + to_string ( errno ) + " | " + strerror ( errno ) + " ) " ) ;
return ;
}
client - > protocol . state = protocol : : HANDSCAKE ;
{
lock_guard lock ( server - > lock ) ;
server - > currentClients . push_back ( client ) ;
}
client - > network . readEvent = event_new ( server - > evBase , client - > network . fileDescriptor , EV_READ | EV_PERSIST , LicenseServer : : handleEventRead , server ) ;
client - > network . writeEvent = event_new ( server - > evBase , client - > network . fileDescriptor , EV_WRITE , LicenseServer : : handleEventWrite , server ) ;
event_add ( client - > network . readEvent , nullptr ) ;
logMessage ( lstream < < " Got new client from " < < inet_ntoa ( client - > network . remoteAddr . sin_addr ) ) ;
}
void LicenseServer : : disconnectClient ( const std : : shared_ptr < ConnectedClient > & client , const std : : string & reason ) {
client - > sendPacket ( { protocol : : PACKET_DISCONNECT , reason } ) ;
}
void LicenseServer : : closeConnection ( const std : : shared_ptr < ConnectedClient > & client , bool blocking ) {
if ( this - > evBaseDispatch & & threads : : self : : id ( ) = = * this - > evBaseDispatch ) {
std : : thread ( std : : bind ( & LicenseServer : : closeConnection , this , client , true ) ) . detach ( ) ;
return ;
}
{
unique_lock lock ( client - > network . lock ) ;
if ( ! TAILQ_EMPTY ( & client - > network . writeQueue ) ) {
lock . unlock ( ) ;
if ( ! blocking ) {
std : : thread ( std : : bind ( & LicenseServer : : closeConnection , this , client , true ) ) . detach ( ) ;
return ;
}
auto start = system_clock : : now ( ) ;
while ( system_clock : : now ( ) - start < seconds ( 5 ) ) {
{
lock . lock ( ) ;
if ( TAILQ_EMPTY ( & client - > network . writeQueue ) ) break ;
lock . unlock ( ) ;
}
threads : : self : : sleep_for ( milliseconds ( 5 ) ) ;
}
}
}
this - > unregisterClient ( client ) ;
}
void LicenseServer : : unregisterClient ( const std : : shared_ptr < ConnectedClient > & client ) {
{
lock_guard lock ( this - > lock ) ;
auto it = find ( this - > currentClients . begin ( ) , this - > currentClients . end ( ) , client ) ;
if ( it ! = this - > currentClients . end ( ) )
this - > currentClients . erase ( it ) ;
}
client - > protocol . state = protocol : : UNCONNECTED ;
client - > uninit ( ) ;
}
void LicenseServer : : cleanup_clients ( ) {
unique_lock lock ( this - > lock ) ;
auto clients = this - > currentClients ;
lock . unlock ( ) ;
for ( const auto & client : clients ) {
if ( client - > protocol . last_read + minutes ( 1 ) < system_clock : : now ( ) ) {
if ( client - > protocol . state ! = protocol : : DISCONNECTING & & client - > protocol . state ! = protocol : : UNCONNECTED ) {
this - > disconnectClient ( client , " timeout " ) ;
this - > closeConnection ( client ) ;
client - > protocol . state = protocol : : DISCONNECTING ;
} else {
auto it = find ( this - > currentClients . begin ( ) , this - > currentClients . end ( ) , client ) ;
if ( it ! = this - > currentClients . end ( ) )
this - > currentClients . erase ( it ) ;
}
}
}
debugMessage ( " Client's cleaned up " ) ;
}
std : : shared_ptr < ConnectedClient > LicenseServer : : findClient ( int fileDescriptor ) {
lock_guard lock ( this - > lock ) ;
for ( const auto & cl : this - > currentClients )
if ( cl - > network . fileDescriptor = = fileDescriptor )
return cl ;
return nullptr ;
}
# define ERR(message) \
do { \
logError ( lstream < < message ) ; \
this - > closeConnection ( client ) ; \
return ; \
} while ( 0 )
void LicenseServer : : handleMessage ( shared_ptr < ConnectedClient > & client , const std : : string & message ) {
if ( message . length ( ) < sizeof ( protocol : : packet : : header ) ) ERR ( " A client tried to send a invalid packet (too small header) " ) ;
protocol : : packet packet { protocol : : PACKET_DISCONNECT , " " } ;
memcpy ( & packet . header , message . data ( ) , sizeof ( protocol : : packet : : header ) ) ;
packet . data = message . substr ( sizeof ( protocol : : packet : : header ) , packet . header . length ) ;
if ( ! client - > protocol . cryptKey . empty ( ) )
xorBuffer ( ( char * ) packet . data . data ( ) , packet . data . length ( ) , client - > protocol . cryptKey . data ( ) , client - > protocol . cryptKey . length ( ) ) ;
bool success = false ;
string error ;
try {
if ( packet . header . packetId = = protocol : : PACKET_CLIENT_HANDSHAKE ) {
success = this - > handleHandshake ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_DISCONNECT ) {
success = this - > handleDisconnect ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_SERVER_VALIDATION ) {
success = this - > handleServerValidation ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_PROPERTY_ADJUSTMENT ) {
success = this - > handlePacketPropertyUpdate ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_AUTH_REQUEST ) {
success = this - > handlePacketAuth ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_LICENSE_CREATE_REQUEST ) {
success = this - > handlePacketLicenseCreate ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_LIST_REQUEST ) {
success = this - > handlePacketLicenseList ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_CLIENT_DELETE_REQUEST ) {
success = this - > handlePacketLicenseDelete ( client , packet , error ) ;
} else if ( packet . header . packetId = = protocol : : PACKET_PING ) {
/* nothing todo */
} else error = " Invalid packet id! " ;
} catch ( std : : exception & ex ) {
success = false ;
error = " Caught exception while handle packet: " + string ( ex . what ( ) ) ;
}
if ( ! success ) {
logError ( " [CLIENT][ " + client - > address ( ) + " ] Failed to handle packet. message: " + error ) ;
this - > disconnectClient ( client , error ) ;
}
}