+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
+#include <csignal>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <stdexcept>
+#include <algorithm>
#include "stg/common.h"
#include "stg/ia_packets.h"
+#include "stg/locker.h"
#include "proto.h"
+class HasIP : public std::unary_function<std::pair<uint32_t, USER>, bool> {
+ public:
+ explicit HasIP(uint32_t i) : ip(i) {}
+ bool operator()(const std::pair<uint32_t, USER> & 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)
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<PROTO *>(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);
return true;
}
-void PROTO::AddUser(const USER & user)
+void PROTO::AddUser(const USER & user, bool connect)
{
- users.push_back(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::vector<std::pair<uint32_t, USER> >::const_iterator it;
-it = users.find(ip);
+std::list<std::pair<uint32_t, USER> >::iterator it;
+STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+it = std::find_if(users.begin(), users.end(), HasIP(ip));
if (it == users.end())
- return false;*/
+ return false;
// Do something
-return true;
+return RealConnect(&it->second);
}
bool PROTO::Disconnect(uint32_t ip)
{
-/*std::vector<std::pair<uint32_t, USER> >::const_iterator it;
-it = users.find(ip);
+std::list<std::pair<uint32_t, USER> >::iterator it;
+STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+it = std::find_if(users.begin(), users.end(), HasIP(ip));
if (it == users.end())
- return false;*/
+ 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<std::pair<uint32_t, USER> >::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<struct pollfd>::iterator it;
-std::vector<std::pair<uint32_t, USER> >::iterator userIt;
+std::list<std::pair<uint32_t, USER> >::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)
continue;
}
- result = result && HandlePacket(buffer, &(userIt->second));
+ result = result && HandlePacket(buffer, res, &(userIt->second));
}
}
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<std::string, PacketProcessor>::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;
}
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);
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;
}
{
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);
{
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)
{
errorStr = "Unexpected FIN";
printfd(__FILE__, "PROTO::FIN_Proc() - wrong phase: %d\n", user->GetPhase());
+ return false;
}
user->SetPhase(1);
const ERR_8 * packet = static_cast<const ERR_8 *>(buffer);
const char * ptr = static_cast<const char *>(buffer);
-for (int i = 0; i < sizeof(packet) / 8; i++)
- Blowfish_Decrypt(user->GetCtx(), (uint32_t *)(ptr + i * 8), (uint32_t *)(ptr + i * 8 + 4));
-
//uint32_t len = packet->len;
#ifdef ARCH_BE
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;
{
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] = 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++)
(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());
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);
+}