X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/34ef822e81b9f236b2f5edf52d351a0f82d59a0c..a9ae4715900f90548082930e69ecafe44e70ff37:/projects/sgauthstress/proto.cpp?ds=sidebyside diff --git a/projects/sgauthstress/proto.cpp b/projects/sgauthstress/proto.cpp index 7c547daf..dc2bf08c 100644 --- a/projects/sgauthstress/proto.cpp +++ b/projects/sgauthstress/proto.cpp @@ -1,22 +1,34 @@ #include #include +#include #include #include #include #include +#include #include "stg/common.h" +#include "stg/ia_packets.h" +#include "stg/locker.h" #include "proto.h" +class HasIP : public std::unary_function, bool> { + public: + explicit HasIP(uint32_t i) : ip(i) {} + bool operator()(const std::pair & value) { return value.first == ip; } + private: + uint32_t ip; +}; + PROTO::PROTO(const std::string & server, uint16_t port, uint16_t localPort, int to) - : running(false), - stopped(true), - timeout(to) + : timeout(to), + running(false), + stopped(true) { uint32_t ip = inet_addr(server.c_str()); if (ip == INADDR_NONE) @@ -55,23 +67,31 @@ processors["DISCONN_SYN_ACK"] = &PROTO::DISCONN_SYN_ACK_Proc; processors["FIN"] = &PROTO::FIN_Proc; processors["INFO"] = &PROTO::INFO_Proc; // ERR_Proc will be handled explicitly + +pthread_mutex_init(&mutex, NULL); } PROTO::~PROTO() { +pthread_mutex_destroy(&mutex); } void * PROTO::Runner(void * data) { +sigset_t signalSet; +sigfillset(&signalSet); +pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + PROTO * protoPtr = static_cast(data); protoPtr->Run(); +return NULL; } bool PROTO::Start() { stopped = false; running = true; -if (pthread_create(&tid, NULL, &Runner, NULL)) +if (pthread_create(&tid, NULL, &Runner, this)) { errorStr = "Failed to create listening thread: '"; errorStr += strerror(errno); @@ -109,62 +129,106 @@ if (pthread_join(tid, NULL)) return true; } -void PROTO::AddUser(const USER & user) +void PROTO::AddUser(const USER & user, bool connect) { - users.insert(std::make_pair(user.GetIP(), user)); - struct pollfd pfd; - pfd.fd = user.GetSocket(); - pfd.events = POLLIN; - pfd.revents = 0; - pollFds.push_back(pfd); +STG_LOCKER lock(&mutex, __FILE__, __LINE__); +users.push_back(std::make_pair(user.GetIP(), user)); +users.back().second.InitNetwork(); + +struct pollfd pfd; +pfd.fd = users.back().second.GetSocket(); +pfd.events = POLLIN; +pfd.revents = 0; +pollFds.push_back(pfd); + +if (connect) + { + RealConnect(&users.back().second); + } } bool PROTO::Connect(uint32_t ip) { -std::map::const_iterator it; -it = users.find(ip); +std::list >::iterator it; +STG_LOCKER lock(&mutex, __FILE__, __LINE__); +it = std::find_if(users.begin(), users.end(), HasIP(ip)); if (it == users.end()) return false; // Do something -return true; +return RealConnect(&it->second); } bool PROTO::Disconnect(uint32_t ip) { -std::map::const_iterator it; -it = users.find(ip); +std::list >::iterator it; +STG_LOCKER lock(&mutex, __FILE__, __LINE__); +it = std::find_if(users.begin(), users.end(), HasIP(ip)); if (it == users.end()) return false; // Do something -return true; +return RealDisconnect(&it->second); } void PROTO::Run() { while (running) { - int res = poll(&pollFds.front(), pollFds.size(), timeout); + int res; + { + STG_LOCKER lock(&mutex, __FILE__, __LINE__); + res = poll(&pollFds.front(), pollFds.size(), timeout); + } if (res < 0) break; if (!running) break; if (res) + { + printfd(__FILE__, "PROTO::Run() - events: %d\n", res); RecvPacket(); + } + else + { + CheckTimeouts(); + } } stopped = true; } +void PROTO::CheckTimeouts() +{ +STG_LOCKER lock(&mutex, __FILE__, __LINE__); +std::list >::iterator it; +for (it = users.begin(); it != users.end(); ++it) + { + int delta = difftime(time(NULL), it->second.GetPhaseChangeTime()); + if ((it->second.GetPhase() == 3) && + (delta > it->second.GetUserTimeout())) + { + printfd(__FILE__, "PROTO::CheckTimeouts() - user alive timeout (ip: %s, login: '%s', delta: %d > %d)\n", inet_ntostring(it->second.GetIP()).c_str(), it->second.GetLogin().c_str(), delta, it->second.GetUserTimeout()); + it->second.SetPhase(1); + } + if ((it->second.GetPhase() == 2) && + (delta > it->second.GetAliveTimeout())) + { + printfd(__FILE__, "PROTO::CheckTimeouts() - user connect timeout (ip: %s, login: '%s', delta: %d > %d)\n", inet_ntostring(it->second.GetIP()).c_str(), it->second.GetLogin().c_str(), delta, it->second.GetAliveTimeout()); + it->second.SetPhase(1); + } + } +} + bool PROTO::RecvPacket() { bool result = true; std::vector::iterator it; -std::map::iterator userIt(users.begin()); -for (it = pollFds.begin(); it != pollFds.end(); ++it) +std::list >::iterator userIt; +STG_LOCKER lock(&mutex, __FILE__, __LINE__); +for (it = pollFds.begin(), userIt = users.begin(); it != pollFds.end() && userIt != users.end(); ++it, ++userIt) { if (it->revents) { @@ -173,36 +237,42 @@ for (it = pollFds.begin(); it != pollFds.end(); ++it) struct sockaddr_in addr; socklen_t fromLen = sizeof(addr); char buffer[2048]; - int res = recvfrom(userIt->second.GetSocket(), buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &fromLen); + int res = recvfrom(userIt->second.GetSocket(), buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &fromLen); if (res == -1) { result = false; - ++userIt; continue; } - result = result && HandlePacket(buffer, &(userIt->second)); + result = result && HandlePacket(buffer, res, &(userIt->second)); } - ++userIt; } return result; } -bool PROTO::HandlePacket(const char * buffer, USER * user) +bool PROTO::HandlePacket(const char * buffer, size_t length, USER * user) { -if (strcmp(buffer + 4 + sizeof(HDR_8), "ERR")) +if (!strncmp(buffer + 4 + sizeof(HDR_8), "ERR", 3)) { return ERR_Proc(buffer, user); } +for (size_t i = 0; i < length / 8; i++) + Blowfish_Decrypt(user->GetCtx(), + (uint32_t *)(buffer + i * 8), + (uint32_t *)(buffer + i * 8 + 4)); + std::string packetName(buffer + 12); + std::map::const_iterator it; it = processors.find(packetName); if (it != processors.end()) return (this->*it->second)(buffer, user); +printfd(__FILE__, "PROTO::HandlePacket() - invalid packet signature: '%s'\n", packetName.c_str()); + return false; } @@ -220,12 +290,11 @@ SwapBytes(userTimeout); SwapBytes(aliveDelay); #endif -Send_CONN_ACK(user); - if (user->GetPhase() != 2) { errorStr = "Unexpected CONN_SYN_ACK"; printfd(__FILE__, "PROTO::CONN_SYN_ACK_Proc() - wrong phase: %d\n", user->GetPhase()); + return false; } user->SetPhase(3); @@ -233,6 +302,10 @@ user->SetAliveTimeout(aliveTimeout); user->SetUserTimeout(userTimeout); user->SetRnd(rnd); +Send_CONN_ACK(user); + +printfd(__FILE__, "PROTO::CONN_SYN_ACK_Proc() - user '%s' successfully logged in from IP %s\n", user->GetLogin().c_str(), inet_ntostring(user->GetIP()).c_str()); + return true; } @@ -250,16 +323,11 @@ if (user->GetPhase() != 3) { errorStr = "Unexpected ALIVE_SYN"; printfd(__FILE__, "PROTO::ALIVE_SYN_Proc() - wrong phase: %d\n", user->GetPhase()); - } - -if (user->GetRnd() + 1 != rnd) - { - errorStr = "Wrong control value at ALIVE_SYN"; - printfd(__FILE__, "PROTO::ALIVE_SYN_Proc() - wrong control value: %d, expected: %d\n", rnd, user->GetRnd() + 1); + return false; } user->SetPhase(3); -user->SetRnd(rnd); +user->SetRnd(rnd); // Set new rnd value for ALIVE_ACK Send_ALIVE_ACK(user); @@ -280,6 +348,7 @@ if (user->GetPhase() != 4) { errorStr = "Unexpected DISCONN_SYN_ACK"; printfd(__FILE__, "PROTO::DISCONN_SYN_ACK_Proc() - wrong phase: %d\n", user->GetPhase()); + return false; } if (user->GetRnd() + 1 != rnd) @@ -302,6 +371,7 @@ if (user->GetPhase() != 5) { errorStr = "Unexpected FIN"; printfd(__FILE__, "PROTO::FIN_Proc() - wrong phase: %d\n", user->GetPhase()); + return false; } user->SetPhase(1); @@ -319,9 +389,7 @@ return true; bool PROTO::ERR_Proc(const void * buffer, USER * user) { const ERR_8 * packet = static_cast(buffer); - -for (int i = 0; i < len/8; i++) - Blowfish_Decrypt(&ctxPass, (uint32_t*)(buffer + i*8), (uint32_t*)(buffer + i*8 + 4)); +const char * ptr = static_cast(buffer); //uint32_t len = packet->len; @@ -349,6 +417,7 @@ packet.len = sizeof(packet); SwapBytes(packet.len); #endif +strncpy((char *)packet.loginS, user->GetLogin().c_str(), sizeof(packet.loginS)); strncpy((char *)packet.type, "CONN_SYN", sizeof(packet.type)); strncpy((char *)packet.login, user->GetLogin().c_str(), sizeof(packet.login)); packet.dirs = 0xFFffFFff; @@ -431,15 +500,16 @@ bool PROTO::SendPacket(const void * packet, size_t length, USER * user) { HDR_8 hdr; -assert(sizeof(hdr) + length < 2048 && "Packet length must not exceed 2048 bytes"); +assert(length < 2048 && "Packet length must not exceed 2048 bytes"); -strncpy((char *)hdr.magic, IA_ID, 6); +strncpy((char *)hdr.magic, IA_ID, sizeof(hdr.magic)); hdr.protoVer[0] = 0; -hdr.protoVer[1] = IA_PROTO_VER; +hdr.protoVer[1] = 8; // IA_PROTO_VER unsigned char buffer[2048]; +memset(buffer, 0, sizeof(buffer)); +memcpy(buffer, packet, length); memcpy(buffer, &hdr, sizeof(hdr)); -memcpy(buffer + sizeof(hdr), packet, length); size_t offset = sizeof(HDR_8); for (size_t i = 0; i < IA_LOGIN_LEN / 8; i++) @@ -458,18 +528,18 @@ for (size_t i = 0; i < encLen; i++) (uint32_t*)(buffer + offset + i * 8 + 4)); } -int res = sendto(user->GetSocket(), buffer, sizeof(buffer), 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); +int res = sendto(user->GetSocket(), buffer, length, 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (res < 0) { errorStr = "Failed to send packet: '"; errorStr += strerror(errno); errorStr += "'"; - printfd(__FILE__, "PROTO::SendPacket() - %s\n", errorStr.c_str()); + printfd(__FILE__, "PROTO::SendPacket() - %s, fd: %d\n", errorStr.c_str(), user->GetSocket()); return false; } -if (res < sizeof(buffer)) +if (res < length) { errorStr = "Packet sent partially"; printfd(__FILE__, "PROTO::SendPacket() - %s\n", errorStr.c_str()); @@ -478,3 +548,28 @@ if (res < sizeof(buffer)) return true; } + +bool PROTO::RealConnect(USER * user) +{ +if (user->GetPhase() != 1 && + user->GetPhase() != 5) + { + errorStr = "Unexpected connect"; + printfd(__FILE__, "PROTO::RealConnect() - wrong phase: %d\n", user->GetPhase()); + } +user->SetPhase(2); + +return Send_CONN_SYN(user); +} + +bool PROTO::RealDisconnect(USER * user) +{ +if (user->GetPhase() != 3) + { + errorStr = "Unexpected disconnect"; + printfd(__FILE__, "PROTO::RealDisconnect() - wrong phase: %d\n", user->GetPhase()); + } +user->SetPhase(4); + +return Send_DISCONN_SYN(user); +}