]> git.stg.codes - stg.git/blobdiff - projects/stargazer/plugins/other/rscript/rscript.cpp
Move projects back into subfolder.
[stg.git] / projects / stargazer / plugins / other / rscript / rscript.cpp
diff --git a/projects/stargazer/plugins/other/rscript/rscript.cpp b/projects/stargazer/plugins/other/rscript/rscript.cpp
new file mode 100644 (file)
index 0000000..764e536
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
+ *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
+ */
+
+#include "rscript.h"
+
+#include "ur_functor.h"
+#include "send_functor.h"
+
+#include "stg/common.h"
+#include "stg/locker.h"
+#include "stg/users.h"
+#include "stg/user_property.h"
+#include "stg/logger.h"
+
+#include <algorithm>
+
+#include <csignal>
+#include <cassert>
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+
+#include <sys/time.h>
+#include <netinet/ip.h>
+
+#define RS_DEBUG (1)
+#define MAX_SHORT_PCKT  (3)
+
+extern volatile time_t stgTime;
+
+using RS::REMOTE_SCRIPT;
+
+namespace {
+
+template<typename T>
+struct USER_IS
+{
+    explicit USER_IS(RS::UserPtr u) : user(u) {}
+    bool operator()(const T & notifier) { return notifier.GetUser() == user; }
+
+    RS::UserPtr user;
+};
+
+} // namespace anonymous
+
+extern "C" STG::Plugin* GetPlugin()
+{
+    static REMOTE_SCRIPT plugin;
+    return &plugin;
+}
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+RS::SETTINGS::SETTINGS()
+    : sendPeriod(0),
+      port(0)
+{
+}
+//-----------------------------------------------------------------------------
+int RS::SETTINGS::ParseSettings(const STG::ModuleSettings & s)
+{
+int p;
+STG::ParamValue pv;
+std::vector<STG::ParamValue>::const_iterator pvi;
+netRouters.clear();
+///////////////////////////
+pv.param = "Port";
+pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
+if (pvi == s.moduleParams.end() || pvi->value.empty())
+    {
+    errorStr = "Parameter \'Port\' not found.";
+    printfd(__FILE__, "Parameter 'Port' not found\n");
+    return -1;
+    }
+if (ParseIntInRange(pvi->value[0], 2, 65535, &p))
+    {
+    errorStr = "Cannot parse parameter \'Port\': " + errorStr;
+    printfd(__FILE__, "Cannot parse parameter 'Port'\n");
+    return -1;
+    }
+port = static_cast<uint16_t>(p);
+///////////////////////////
+pv.param = "SendPeriod";
+pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
+if (pvi == s.moduleParams.end() || pvi->value.empty())
+    {
+    errorStr = "Parameter \'SendPeriod\' not found.";
+    printfd(__FILE__, "Parameter 'SendPeriod' not found\n");
+    return -1;
+    }
+
+if (ParseIntInRange(pvi->value[0], 5, 600, &sendPeriod))
+    {
+    errorStr = "Cannot parse parameter \'SendPeriod\': " + errorStr;
+    printfd(__FILE__, "Cannot parse parameter 'SendPeriod'\n");
+    return -1;
+    }
+///////////////////////////
+pv.param = "UserParams";
+pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
+if (pvi == s.moduleParams.end() || pvi->value.empty())
+    {
+    errorStr = "Parameter \'UserParams\' not found.";
+    printfd(__FILE__, "Parameter 'UserParams' not found\n");
+    return -1;
+    }
+userParams = pvi->value;
+///////////////////////////
+pv.param = "Password";
+pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
+if (pvi == s.moduleParams.end() || pvi->value.empty())
+    {
+    errorStr = "Parameter \'Password\' not found.";
+    printfd(__FILE__, "Parameter 'Password' not found\n");
+    return -1;
+    }
+password = pvi->value[0];
+///////////////////////////
+pv.param = "SubnetFile";
+pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
+if (pvi == s.moduleParams.end() || pvi->value.empty())
+    {
+    errorStr = "Parameter \'SubnetFile\' not found.";
+    printfd(__FILE__, "Parameter 'SubnetFile' not found\n");
+    return -1;
+    }
+subnetFile = pvi->value[0];
+
+NRMapParser nrMapParser;
+
+if (!nrMapParser.ReadFile(subnetFile))
+    {
+    netRouters = nrMapParser.GetMap();
+    }
+else
+    {
+        STG::PluginLogger::get("rscript")("mod_rscript: error opening subnets file '%s'", subnetFile.c_str());
+    }
+
+return 0;
+}
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+REMOTE_SCRIPT::REMOTE_SCRIPT()
+    : sendPeriod(15),
+      halfPeriod(8),
+      nonstop(false),
+      isRunning(false),
+      users(NULL),
+      sock(0),
+      onAddUserNotifier(*this),
+      onDelUserNotifier(*this),
+      logger(STG::PluginLogger::get("rscript"))
+{
+pthread_mutex_init(&mutex, NULL);
+}
+//-----------------------------------------------------------------------------
+REMOTE_SCRIPT::~REMOTE_SCRIPT()
+{
+pthread_mutex_destroy(&mutex);
+}
+//-----------------------------------------------------------------------------
+void * REMOTE_SCRIPT::Run(void * d)
+{
+sigset_t signalSet;
+sigfillset(&signalSet);
+pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
+
+REMOTE_SCRIPT * rs = static_cast<REMOTE_SCRIPT *>(d);
+
+rs->isRunning = true;
+
+while (rs->nonstop)
+    {
+    rs->PeriodicSend();
+    sleep(2);
+    }
+
+rs->isRunning = false;
+return NULL;
+}
+//-----------------------------------------------------------------------------
+int REMOTE_SCRIPT::ParseSettings()
+{
+int ret = rsSettings.ParseSettings(settings);
+if (ret)
+    errorStr = rsSettings.GetStrError();
+
+sendPeriod = rsSettings.GetSendPeriod();
+halfPeriod = sendPeriod / 2;
+
+return ret;
+}
+//-----------------------------------------------------------------------------
+int REMOTE_SCRIPT::Start()
+{
+netRouters = rsSettings.GetSubnetsMap();
+
+InitEncrypt(&ctx, rsSettings.GetPassword());
+
+users->AddNotifierUserAdd(&onAddUserNotifier);
+users->AddNotifierUserDel(&onDelUserNotifier);
+
+nonstop = true;
+
+if (GetUsers())
+    {
+    return -1;
+    }
+
+if (PrepareNet())
+    {
+    return -1;
+    }
+
+if (!isRunning)
+    {
+    if (pthread_create(&thread, NULL, Run, this))
+        {
+        errorStr = "Cannot create thread.";
+        logger("Cannot create thread.");
+        printfd(__FILE__, "Cannot create thread\n");
+        return -1;
+        }
+    }
+
+errorStr = "";
+return 0;
+}
+//-----------------------------------------------------------------------------
+int REMOTE_SCRIPT::Stop()
+{
+if (!IsRunning())
+    return 0;
+
+nonstop = false;
+
+std::for_each(
+        authorizedUsers.begin(),
+        authorizedUsers.end(),
+        DisconnectUser(*this)
+        );
+
+FinalizeNet();
+
+if (isRunning)
+    {
+    //5 seconds to thread stops itself
+    for (int i = 0; i < 25 && isRunning; i++)
+        {
+        struct timespec ts = {0, 200000000};
+        nanosleep(&ts, NULL);
+        }
+    }
+
+users->DelNotifierUserDel(&onDelUserNotifier);
+users->DelNotifierUserAdd(&onAddUserNotifier);
+
+if (isRunning)
+    {
+    logger("Cannot stop thread.");
+    return -1;
+    }
+
+return 0;
+}
+//-----------------------------------------------------------------------------
+int REMOTE_SCRIPT::Reload(const STG::ModuleSettings & /*ms*/)
+{
+NRMapParser nrMapParser;
+
+if (nrMapParser.ReadFile(rsSettings.GetMapFileName()))
+    {
+    errorStr = nrMapParser.GetErrorStr();
+    logger("Map file reading error: %s", errorStr.c_str());
+    return -1;
+    }
+
+    {
+    STG_LOCKER lock(&mutex);
+
+    printfd(__FILE__, "REMOTE_SCRIPT::Reload()\n");
+
+    netRouters = nrMapParser.GetMap();
+    }
+
+std::for_each(authorizedUsers.begin(),
+              authorizedUsers.end(),
+              UpdateRouter(*this));
+
+logger("%s reloaded successfully.", rsSettings.GetMapFileName().c_str());
+printfd(__FILE__, "REMOTE_SCRIPT::Reload() %s reloaded successfully.\n");
+
+return 0;
+}
+//-----------------------------------------------------------------------------
+bool REMOTE_SCRIPT::PrepareNet()
+{
+sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+if (sock < 0)
+    {
+    errorStr = "Cannot create socket.";
+    logger("Canot create a socket: %s", strerror(errno));
+    printfd(__FILE__, "Cannot create socket\n");
+    return true;
+    }
+
+return false;
+}
+//-----------------------------------------------------------------------------
+bool REMOTE_SCRIPT::FinalizeNet()
+{
+close(sock);
+return false;
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::PeriodicSend()
+{
+STG_LOCKER lock(&mutex);
+
+std::map<uint32_t, RS::USER>::iterator it(authorizedUsers.begin());
+while (it != authorizedUsers.end())
+    {
+    if (difftime(stgTime, it->second.lastSentTime) - (rand() % halfPeriod) > sendPeriod)
+        {
+        Send(it->second);
+        }
+    ++it;
+    }
+}
+//-----------------------------------------------------------------------------
+#ifdef NDEBUG
+bool REMOTE_SCRIPT::PreparePacket(char * buf, size_t, RS::USER & rsu, bool forceDisconnect) const
+#else
+bool REMOTE_SCRIPT::PreparePacket(char * buf, size_t bufSize, RS::USER & rsu, bool forceDisconnect) const
+#endif
+{
+RS::PACKET_HEADER packetHead;
+
+memset(packetHead.padding, 0, sizeof(packetHead.padding));
+strcpy((char*)packetHead.magic, RS_ID);
+packetHead.protoVer[0] = '0';
+packetHead.protoVer[1] = '2';
+if (forceDisconnect)
+    {
+    packetHead.packetType = RS_DISCONNECT_PACKET;
+    printfd(__FILE__, "RSCRIPT: force disconnect for '%s'\n", rsu.user->GetLogin().c_str());
+    }
+else
+    {
+    if (rsu.shortPacketsCount % MAX_SHORT_PCKT == 0)
+        {
+        //SendLong
+        packetHead.packetType = rsu.user->IsInetable() ? RS_CONNECT_PACKET : RS_DISCONNECT_PACKET;
+        if (rsu.user->IsInetable())
+            printfd(__FILE__, "RSCRIPT: connect for '%s'\n", rsu.user->GetLogin().c_str());
+        else
+            printfd(__FILE__, "RSCRIPT: disconnect for '%s'\n", rsu.user->GetLogin().c_str());
+        }
+    else
+        {
+        //SendShort
+        packetHead.packetType = rsu.user->IsInetable() ? RS_ALIVE_PACKET : RS_DISCONNECT_PACKET;
+        if (rsu.user->IsInetable())
+            printfd(__FILE__, "RSCRIPT: alive for '%s'\n", rsu.user->GetLogin().c_str());
+        else
+            printfd(__FILE__, "RSCRIPT: disconnect for '%s'\n", rsu.user->GetLogin().c_str());
+        }
+    }
+rsu.shortPacketsCount++;
+rsu.lastSentTime = stgTime;
+
+packetHead.ip = htonl(rsu.ip);
+packetHead.id = htonl(rsu.user->GetID());
+strncpy((char*)packetHead.login, rsu.user->GetLogin().c_str(), RS_LOGIN_LEN);
+packetHead.login[RS_LOGIN_LEN - 1] = 0;
+
+memcpy(buf, &packetHead, sizeof(packetHead));
+
+if (packetHead.packetType == RS_ALIVE_PACKET)
+    {
+    return false;
+    }
+
+RS::PACKET_TAIL packetTail;
+
+memset(packetTail.padding, 0, sizeof(packetTail.padding));
+strcpy((char*)packetTail.magic, RS_ID);
+std::vector<std::string>::const_iterator it;
+std::string params;
+for(it = rsSettings.GetUserParams().begin();
+    it != rsSettings.GetUserParams().end();
+    ++it)
+    {
+    std::string parameter(rsu.user->GetParamValue(it->c_str()));
+    if (params.length() + parameter.length() > RS_PARAMS_LEN - 1)
+    {
+        logger("Script params string length %d exceeds the limit of %d symbols.", params.length() + parameter.length(), RS_PARAMS_LEN);
+        break;
+    }
+    params += parameter + " ";
+    }
+strncpy((char *)packetTail.params, params.c_str(), RS_PARAMS_LEN);
+packetTail.params[RS_PARAMS_LEN - 1] = 0;
+
+assert(sizeof(packetHead) + sizeof(packetTail) <= bufSize && "Insufficient buffer space");
+
+Encrypt(&ctx, buf + sizeof(packetHead), (char *)&packetTail, sizeof(packetTail) / 8);
+
+return false;
+}
+//-----------------------------------------------------------------------------
+bool REMOTE_SCRIPT::Send(RS::USER & rsu, bool forceDisconnect) const
+{
+char buffer[RS_MAX_PACKET_LEN];
+
+memset(buffer, 0, sizeof(buffer));
+
+if (PreparePacket(buffer, sizeof(buffer), rsu, forceDisconnect))
+    {
+    printfd(__FILE__, "REMOTE_SCRIPT::Send() - Invalid packet length!\n");
+    return true;
+    }
+
+std::for_each(
+        rsu.routers.begin(),
+        rsu.routers.end(),
+        PacketSender(sock, buffer, sizeof(buffer), static_cast<uint16_t>(htons(rsSettings.GetPort())))
+        );
+
+return false;
+}
+//-----------------------------------------------------------------------------
+bool REMOTE_SCRIPT::SendDirect(RS::USER & rsu, uint32_t routerIP, bool forceDisconnect) const
+{
+char buffer[RS_MAX_PACKET_LEN];
+
+if (PreparePacket(buffer, sizeof(buffer), rsu, forceDisconnect))
+    {
+    printfd(__FILE__, "REMOTE_SCRIPT::SendDirect() - Invalid packet length!\n");
+    return true;
+    }
+
+struct sockaddr_in sendAddr;
+
+sendAddr.sin_family = AF_INET;
+sendAddr.sin_port = static_cast<uint16_t>(htons(rsSettings.GetPort()));
+sendAddr.sin_addr.s_addr = routerIP;
+
+ssize_t res = sendto(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
+
+if (res < 0)
+    logger("sendto error: %s", strerror(errno));
+
+return (res != sizeof(buffer));
+}
+//-----------------------------------------------------------------------------
+bool REMOTE_SCRIPT::GetUsers()
+{
+UserPtr u;
+
+int h = users->OpenSearch();
+assert(h && "USERS::OpenSearch is always correct");
+
+while (!users->SearchNext(h, &u))
+    {
+    SetUserNotifiers(u);
+    }
+
+users->CloseSearch(h);
+return false;
+}
+//-----------------------------------------------------------------------------
+std::vector<uint32_t> REMOTE_SCRIPT::IP2Routers(uint32_t ip)
+{
+STG_LOCKER lock(&mutex);
+for (size_t i = 0; i < netRouters.size(); ++i)
+    {
+    if ((ip & netRouters[i].subnetMask) == (netRouters[i].subnetIP & netRouters[i].subnetMask))
+        {
+        return netRouters[i].routers;
+        }
+    }
+return std::vector<uint32_t>();
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::SetUserNotifiers(UserPtr u)
+{
+ipNotifierList.push_front(RS::IP_NOTIFIER(*this, u));
+connNotifierList.push_front(RS::CONNECTED_NOTIFIER(*this, u));
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::UnSetUserNotifiers(UserPtr u)
+{
+ipNotifierList.erase(std::remove_if(ipNotifierList.begin(),
+                                    ipNotifierList.end(),
+                                    USER_IS<IP_NOTIFIER>(u)),
+                     ipNotifierList.end());
+connNotifierList.erase(std::remove_if(connNotifierList.begin(),
+                                      connNotifierList.end(),
+                                      USER_IS<CONNECTED_NOTIFIER>(u)),
+                       connNotifierList.end());
+
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::AddRSU(UserPtr user)
+{
+RS::USER rsu(IP2Routers(user->GetCurrIP()), user);
+Send(rsu);
+
+STG_LOCKER lock(&mutex);
+authorizedUsers.insert(std::make_pair(user->GetCurrIP(), rsu));
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::DelRSU(UserPtr user)
+{
+STG_LOCKER lock(&mutex);
+std::map<uint32_t, RS::USER>::iterator it(authorizedUsers.begin());
+while (it != authorizedUsers.end())
+    {
+    if (it->second.user == user)
+        {
+        Send(it->second, true);
+        authorizedUsers.erase(it);
+        return;
+        }
+    ++it;
+    }
+/*const std::map<uint32_t, RS::USER>::iterator it(
+        authorizedUsers.find(user->GetCurrIP())
+        );
+if (it != authorizedUsers.end())
+    {
+    Send(it->second, true);
+    authorizedUsers.erase(it);
+    }*/
+}
+//-----------------------------------------------------------------------------
+void RS::IP_NOTIFIER::Notify(const uint32_t & /*oldValue*/, const uint32_t & newValue)
+{
+if (newValue)
+    rs.AddRSU(user);
+else
+    rs.DelRSU(user);
+}
+//-----------------------------------------------------------------------------
+void RS::CONNECTED_NOTIFIER::Notify(const bool & /*oldValue*/, const bool & newValue)
+{
+if (newValue)
+    rs.AddRSU(user);
+else
+    rs.DelRSU(user);
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::InitEncrypt(BLOWFISH_CTX * ctx, const std::string & password) const
+{
+unsigned char keyL[PASSWD_LEN];  // Пароль для шифровки
+memset(keyL, 0, PASSWD_LEN);
+strncpy((char *)keyL, password.c_str(), PASSWD_LEN);
+Blowfish_Init(ctx, keyL, PASSWD_LEN);
+}
+//-----------------------------------------------------------------------------
+void REMOTE_SCRIPT::Encrypt(BLOWFISH_CTX * ctx, void * dst, const void * src, size_t len8) const
+{
+if (dst != src)
+    memcpy(dst, src, len8 * 8);
+for (size_t i = 0; i < len8; ++i)
+    Blowfish_Encrypt(ctx, static_cast<uint32_t *>(dst) + i * 2, static_cast<uint32_t *>(dst) + i * 2 + 1);
+}
+//-----------------------------------------------------------------------------