Created a template for packet handling (#8069)

This commit is contained in:
Lemongrass3110 2024-02-26 22:48:08 +01:00 committed by GitHub
parent eb308dcad2
commit c22906d775
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 165 additions and 130 deletions

View File

@ -4,20 +4,30 @@
#ifndef PACKETS_HPP
#define PACKETS_HPP
#include <functional>
#include <unordered_map>
#include <common/cbasetypes.hpp>
#include <common/mmo.hpp>
#include <common/showmsg.hpp>
#include <common/socket.hpp>
#include <common/utilities.hpp>
#pragma warning( push )
#pragma warning( disable : 4200 )
#define DEFINE_PACKET_HEADER( name, id ) const int16 HEADER_##name = id
#define DEFINE_PACKET_ID( name, id ) DEFINE_PACKET_HEADER( name, id )
// NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 )
#pragma pack( push, 1 )
#endif
struct PACKET{
int16 packetType;
int16 packetLength;
} __attribute__((packed));
struct PACKET_CA_LOGIN{
int16 packetType;
uint32 version;
@ -209,4 +219,88 @@ DEFINE_PACKET_HEADER( TC_RESULT, 0xae3 );
#pragma warning( pop )
template <typename sessiontype> class PacketDatabase{
private:
struct s_packet_info{
bool fixed;
int16 size;
std::function<bool ( int fd, sessiontype& sd )> func;
};
std::unordered_map<int16, s_packet_info> infos;
public:
void add( int16 packetType, bool fixed, int16 size, std::function<bool ( int fd, sessiontype& sd )> func ){
if( fixed ){
if( size < 2 ){
ShowError( "Definition for packet 0x%04x is invalid. Minimum size for a fixed length packet is 2 bytes.\n", packetType );
return;
}
}else{
if( size < 4 ){
ShowError( "Definition for packet 0x%04x is invalid. Minimum size for a dynamic length packet is 2 bytes.\n", packetType );
return;
}
}
s_packet_info& info = infos[packetType];
info.fixed = fixed;
info.size = size;
info.func = func;
}
bool handle( int fd, sessiontype& sd ){
int16 remaining = static_cast<int16>( RFIFOREST( fd ) );
if( remaining < 2 ){
ShowError( "Did not receive enough bytes to process a packet\n" );
set_eof( fd );
return false;
}
PACKET* p = (PACKET*)RFIFOP( fd, 0 );
s_packet_info* info = rathena::util::umap_find( this->infos, p->packetType );
if( info == nullptr ){
ShowError( "Received unknown packet 0x%04x\n", p->packetType );
set_eof( fd );
return false;
}
if( info->fixed ){
if( remaining < info->size ){
ShowError( "Invalid size %hd for packet 0x%04x with fixed size of %hd\n", remaining, p->packetType, info->size );
set_eof( fd );
return false;
}
bool ret = info->func( fd, sd );
RFIFOSKIP( fd, info->size );
return ret;
}else{
if( remaining < info->size ){
ShowError( "Invalid size %hd for packet 0x%04x with dynamic minimum size of %hd\n", remaining, p->packetType, info->size );
set_eof( fd );
return false;
}
if( remaining < p->packetLength ){
ShowError( "Invalid size %hd for packet 0x%04x with dynamic size of %hd\n", remaining, p->packetType, p->packetLength );
set_eof( fd );
return false;
}
bool ret = info->func( fd, sd );
RFIFOSKIP( fd, p->packetLength );
return ret;
}
}
};
#endif /* PACKETS_HPP */

View File

@ -242,16 +242,9 @@ static void logclif_auth_failed(struct login_session_data* sd, int result) {
* @param fd: fd to parse from (client fd)
* @return 0 not enough info transmitted, 1 success
*/
static int logclif_parse_keepalive(int fd){
PACKET_CA_CONNECT_INFO_CHANGED* p = (PACKET_CA_CONNECT_INFO_CHANGED*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p ) ){
return 0;
}
RFIFOSKIP( fd, sizeof( *p ) );
return 1;
static bool logclif_parse_keepalive( int fd, struct login_session_data& ){
// Do nothing
return true;
}
/**
@ -260,28 +253,22 @@ static int logclif_parse_keepalive(int fd){
* @param fd: fd to parse from (client fd)
* @return 0 not enough info transmitted, 1 success
*/
static int logclif_parse_updclhash(int fd, struct login_session_data *sd){
static bool logclif_parse_updclhash( int fd, struct login_session_data& sd ){
PACKET_CA_EXE_HASHCHECK* p = (PACKET_CA_EXE_HASHCHECK*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p ) ){
return 0;
}
sd.has_client_hash = 1;
memcpy( sd.client_hash, p->hash, sizeof( sd.client_hash ) );
sd->has_client_hash = 1;
memcpy( sd->client_hash, p->hash, sizeof( sd->client_hash ) );
RFIFOSKIP( fd, sizeof( *p ) );
return 1;
return true;
}
template <typename P>
int logclif_parse_reqauth_raw( int fd, login_session_data& sd, char* ip ){
static bool logclif_parse_reqauth_raw( int fd, login_session_data& sd ){
P* p = (P*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p ) ){
return 0;
}
char ip[16];
uint32 ipl = session[fd]->client_addr;
ip2str( ipl, ip );
safestrncpy( sd.userid, p->username, sizeof( sd.userid ) );
sd.clienttype = p->clienttype;
@ -295,8 +282,6 @@ int logclif_parse_reqauth_raw( int fd, login_session_data& sd, char* ip ){
sd.passwdenc = 0;
RFIFOSKIP( fd, sizeof( *p ) );
int result = login_mmo_auth( &sd, false );
if( result == -1 ){
@ -305,16 +290,16 @@ int logclif_parse_reqauth_raw( int fd, login_session_data& sd, char* ip ){
logclif_auth_failed( &sd, result );
}
return 1;
return true;
}
template <typename P>
int logclif_parse_reqauth_md5( int fd, login_session_data& sd, char* ip ){
static bool logclif_parse_reqauth_md5( int fd, login_session_data& sd ){
P* p = (P*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p ) ){
return 0;
}
char ip[16];
uint32 ipl = session[fd]->client_addr;
ip2str( ipl, ip );
safestrncpy( sd.userid, p->username, sizeof( sd.userid ) );
sd.clienttype = p->clienttype;
@ -324,11 +309,9 @@ int logclif_parse_reqauth_md5( int fd, login_session_data& sd, char* ip ){
sd.passwdenc = PASSWORDENC;
RFIFOSKIP( fd, sizeof( *p ) );
if( login_config.use_md5_passwds ){
logclif_auth_failed( &sd, 3 ); // send "rejected from server"
return 0;
return false;
}
int result = login_mmo_auth( &sd, false );
@ -339,20 +322,16 @@ int logclif_parse_reqauth_md5( int fd, login_session_data& sd, char* ip ){
logclif_auth_failed( &sd, result );
}
return 1;
return true;
}
template <typename P>
int logclif_parse_reqauth_sso( int fd, login_session_data& sd, char* ip ){
static bool logclif_parse_reqauth_sso( int fd, login_session_data& sd ){
P* p = (P*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p ) ){
return 0;
}
if( static_cast<decltype(p->packetLength)>(RFIFOREST(fd)) < p->packetLength){
return 0;
}
char ip[16];
uint32 ipl = session[fd]->client_addr;
ip2str( ipl, ip );
size_t token_length = p->packetLength - sizeof( *p );
@ -369,8 +348,6 @@ int logclif_parse_reqauth_sso( int fd, login_session_data& sd, char* ip ){
sd.passwdenc = 0;
RFIFOSKIP( fd, p->packetLength );
int result = login_mmo_auth( &sd, false );
if( result == -1 ){
@ -379,7 +356,17 @@ int logclif_parse_reqauth_sso( int fd, login_session_data& sd, char* ip ){
logclif_auth_failed( &sd, result );
}
return 1;
return true;
}
static void logclif_reqkey_result( int fd, struct login_session_data& sd ){
PACKET_AC_ACK_HASH* p = (PACKET_AC_ACK_HASH*)packet_buffer;
p->packetType = HEADER_AC_ACK_HASH;
p->packetLength = sizeof( *p ) + sd.md5keylen;
strncpy( p->salt, sd.md5key, sd.md5keylen );
socket_send( fd, p );
}
/**
@ -388,25 +375,13 @@ int logclif_parse_reqauth_sso( int fd, login_session_data& sd, char* ip ){
* @param sd: client session
* @return 1 success
*/
static int logclif_parse_reqkey(int fd, struct login_session_data *sd){
static bool logclif_parse_reqkey( int fd, struct login_session_data& sd ){
PACKET_CA_REQ_HASH* p_in = (PACKET_CA_REQ_HASH*)RFIFOP( fd, 0 );
if( RFIFOREST( fd ) < sizeof( *p_in ) ){
return 0;
}
sd.md5keylen = sizeof( sd.md5key );
MD5_Salt( sd.md5keylen, sd.md5key );
RFIFOSKIP( fd, sizeof( *p_in ) );
sd->md5keylen = sizeof( sd->md5key );
MD5_Salt( sd->md5keylen, sd->md5key );
PACKET_AC_ACK_HASH* p_out = (PACKET_AC_ACK_HASH*)packet_buffer;
p_out->packetType = HEADER_AC_ACK_HASH;
p_out->packetLength = sizeof( *p_out ) + sd->md5keylen;
strncpy( p_out->salt, sd->md5key, sd->md5keylen );
socket_send( fd, p_out );
logclif_reqkey_result( fd, sd );
return 1;
}
@ -485,28 +460,43 @@ static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, ch
return 1;
}
int logclif_parse_otp_login( int fd, struct login_session_data* sd ){
PACKET_CT_AUTH* p_in = (PACKET_CT_AUTH*)RFIFOP( fd, 0 );
static void logclif_otp_result( int fd ){
PACKET_TC_RESULT p = {};
if( RFIFOREST( fd ) < sizeof( *p_in ) ){
return 0;
}
p.packetType = HEADER_TC_RESULT;
p.packetLength = sizeof( p );
p.type = 0; // normal login
safestrncpy( p.unknown1, "S1000", sizeof( p.unknown1 ) );
safestrncpy( p.unknown2, "token", sizeof( p.unknown2 ) );
RFIFOSKIP( fd, sizeof( *p_in ) );
socket_send( fd, p );
}
PACKET_TC_RESULT p_out = {};
static bool logclif_parse_otp_login( int fd, struct login_session_data& ){
PACKET_CT_AUTH* p = (PACKET_CT_AUTH*)RFIFOP( fd, 0 );
p_out.packetType = HEADER_TC_RESULT;
p_out.packetLength = sizeof( p_out );
p_out.type = 0; // normal login
safestrncpy( p_out.unknown1, "S1000", sizeof( p_out.unknown1 ) );
safestrncpy( p_out.unknown2, "token", sizeof( p_out.unknown2 ) );
socket_send( fd, p_out );
logclif_otp_result( fd );
return 1;
}
class LoginPacketDatabase : public PacketDatabase<login_session_data>{
public:
LoginPacketDatabase(){
this->add( HEADER_CA_CONNECT_INFO_CHANGED, true, sizeof( PACKET_CA_CONNECT_INFO_CHANGED ), logclif_parse_keepalive );
this->add( HEADER_CA_EXE_HASHCHECK, true, sizeof( PACKET_CA_EXE_HASHCHECK ), logclif_parse_updclhash );
this->add( HEADER_CA_LOGIN, true, sizeof( PACKET_CA_LOGIN ), logclif_parse_reqauth_raw<PACKET_CA_LOGIN> );
this->add( HEADER_CA_LOGIN_PCBANG, true, sizeof( PACKET_CA_LOGIN_PCBANG ), logclif_parse_reqauth_raw<PACKET_CA_LOGIN_PCBANG> );
this->add( HEADER_CA_LOGIN_CHANNEL, true, sizeof( PACKET_CA_LOGIN_CHANNEL ), logclif_parse_reqauth_raw<PACKET_CA_LOGIN_CHANNEL> );
this->add( HEADER_CA_LOGIN2, true, sizeof( PACKET_CA_LOGIN2 ), logclif_parse_reqauth_md5<PACKET_CA_LOGIN2> );
this->add( HEADER_CA_LOGIN3, true, sizeof( PACKET_CA_LOGIN3 ), logclif_parse_reqauth_md5<PACKET_CA_LOGIN3> );
this->add( HEADER_CA_LOGIN4, true, sizeof( PACKET_CA_LOGIN4 ), logclif_parse_reqauth_md5<PACKET_CA_LOGIN4> );
this->add( HEADER_CA_SSO_LOGIN_REQ, false, sizeof( PACKET_CA_SSO_LOGIN_REQ ), logclif_parse_reqauth_sso<PACKET_CA_SSO_LOGIN_REQ> );
this->add( HEADER_CA_REQ_HASH, true, sizeof( PACKET_CA_REQ_HASH ), logclif_parse_reqkey );
this->add( HEADER_CT_AUTH, true, sizeof( PACKET_CT_AUTH ), logclif_parse_otp_login );
}
} login_packet_db;
/**
* Entry point from client to log-server.
* Function that checks incoming command, then splits it to the correct handler.
@ -548,70 +538,21 @@ int logclif_parse(int fd) {
while( RFIFOREST(fd) >= 2 )
{
uint16 command = RFIFOW(fd,0);
int next=1;
switch( command ){
// New alive packet: used to verify if client is always alive.
case HEADER_CA_CONNECT_INFO_CHANGED:
next = logclif_parse_keepalive( fd );
break;
// client md5 hash (binary)
case HEADER_CA_EXE_HASHCHECK:
next = logclif_parse_updclhash( fd, sd );
break;
// request client login (raw password)
case HEADER_CA_LOGIN:
// S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
next = logclif_parse_reqauth_raw<PACKET_CA_LOGIN>( fd, *sd, ip );
break;
case HEADER_CA_LOGIN_PCBANG:
// S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
next = logclif_parse_reqauth_raw<PACKET_CA_LOGIN_PCBANG>( fd, *sd, ip );
break;
case HEADER_CA_LOGIN_CHANNEL:
// S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
next = logclif_parse_reqauth_raw<PACKET_CA_LOGIN_CHANNEL>( fd, *sd, ip );
break;
// request client login (md5-hashed password)
case HEADER_CA_LOGIN2:
// S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
next = logclif_parse_reqauth_md5<PACKET_CA_LOGIN2>( fd, *sd, ip );
break;
case HEADER_CA_LOGIN3:
// S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
next = logclif_parse_reqauth_md5<PACKET_CA_LOGIN3>( fd, *sd, ip );
break;
case HEADER_CA_LOGIN4:
// S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <adapter address>.13B
next = logclif_parse_reqauth_md5<PACKET_CA_LOGIN4>( fd, *sd, ip );
break;
case HEADER_CA_SSO_LOGIN_REQ:
// S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.?B
next = logclif_parse_reqauth_sso<PACKET_CA_SSO_LOGIN_REQ>( fd, *sd, ip );
break;
// Sending request of the coding key
case HEADER_CA_REQ_HASH:
next = logclif_parse_reqkey( fd, sd );
break;
// OTP token login
case HEADER_CT_AUTH:
next = logclif_parse_otp_login( fd, sd );
break;
// Connection request of a char-server
case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere
default:
ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
set_eof(fd);
return 0;
if( !login_packet_db.handle( fd, *sd ) ){
return 0;
}
break;
}
if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
}
return 0;
}
/// Constructor destructor
/**