* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/*
- * Date: 27.10.2002
- */
-
/*
* Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
- */
-
- /*
- $Revision: 1.22 $
- $Date: 2010/10/04 20:24:14 $
- $Author: faust $
+ * Author : Maxim Mamontov <faust@stargazer.dp.ua>
*/
#include "configproto.h"
-#include "parser.h"
-#include "parser_auth_by.h"
-#include "parser_user_info.h"
+#include "conn.h"
-#include "stg/users.h"
-#include "stg/admins.h"
-#include "stg/tariffs.h"
-#include "stg/logger.h"
#include "stg/common.h"
+#include "stg/logger.h"
+
+#include <algorithm>
+#include <functional>
+#include <csignal>
+#include <cstring>
+#include <cerrno>
+#include <cassert>
-#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
-//-----------------------------------------------------------------------------
-void ParseXMLStart(void *data, const char *el, const char **attr)
+namespace
{
-CONFIGPROTO * cp = static_cast<CONFIGPROTO *>(data);
-if (cp->currParser)
+struct IsFinished : public std::unary_function<STG::Conn *, bool>
+{
+ result_type operator()(const argument_type & arg)
{
- //cp->currParser->SetCurrAdmin(*cp->currAdmin);
- cp->currParser->Start(data, el, attr);
+ return (arg->IsDone() && !arg->IsKeepAlive()) || !arg->IsOk();
}
-else
+};
+
+struct RemoveConn : public std::unary_function<STG::Conn *, void>
+{
+ result_type operator()(const argument_type & arg)
{
- for (size_t i = 0; i < cp->dataParser.size(); i++)
- {
- //cp->dataParser[i]->SetCurrAdmin(*cp->currAdmin);
- //cp->dataParser[i]->Reset();
- if (cp->dataParser[i]->Start(data, el, attr) == 0)
- {
- cp->currParser = cp->dataParser[i];
- break;
- }
- else
- {
- //cp->dataParser[i]->Reset();
- }
- }
+ delete arg;
}
+};
+
}
-//-----------------------------------------------------------------------------
-void ParseXMLEnd(void *data, const char *el)
+
+CONFIGPROTO::CONFIGPROTO(PLUGIN_LOGGER & l)
+ : m_settings(NULL),
+ m_admins(NULL),
+ m_tariffs(NULL),
+ m_users(NULL),
+ m_port(0),
+ m_running(false),
+ m_stopped(true),
+ m_logger(l),
+ m_listenSocket(-1)
+{
+ std::for_each(m_conns.begin(), m_conns.end(), RemoveConn());
+}
+
+int CONFIGPROTO::Prepare()
{
-CONFIGPROTO * cp = static_cast<CONFIGPROTO *>(data);
-if (cp->currParser)
+ sigset_t sigmask, oldmask;
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGUSR1);
+ sigaddset(&sigmask, SIGHUP);
+ pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
+
+ m_listenSocket = socket(PF_INET, SOCK_STREAM, 0);
+
+ if (m_listenSocket < 0)
{
- if (cp->currParser->End(data, el) == 0)
- {
- cp->dataAnswer = cp->currParser->GetAnswer();
- cp->currParser = NULL;
- }
+ m_errorStr = std::string("Cannot create listen socket: '") + strerror(errno) + "'.";
+ m_logger(m_errorStr);
+ return -1;
}
-else
+
+ struct sockaddr_in listenAddr;
+ listenAddr.sin_family = PF_INET;
+ listenAddr.sin_port = htons(m_port);
+ listenAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // TODO: arbitrary address
+
+ int dummy = 1;
+
+ if (setsockopt(m_listenSocket, SOL_SOCKET, SO_REUSEADDR, &dummy, 4) != 0)
{
- for (size_t i = 0; i < cp->dataParser.size(); i++)
- {
- if (cp->dataParser[i]->End(data, el) == 0)
- {
- cp->dataAnswer = cp->currParser->GetAnswer();
- cp->currParser = NULL;
- break;
- }
- }
+ m_errorStr = std::string("Failed to set SO_REUSEADDR to the listen socket: '") + strerror(errno) + "'.";
+ m_logger(m_errorStr);
+ return -1;
}
-}
-//-----------------------------------------------------------------------------
-CONFIGPROTO::CONFIGPROTO(PLUGIN_LOGGER & l)
- : adminIP(0),
- port(0),
- nonstop(true),
- state(0),
- currAdmin(NULL),
- logger(l),
- listenSocket(-1),
- admins(NULL),
- currParser(NULL)
-{
-/*dataParser.push_back(new PARSER_GET_SERVER_INFO);
-
-dataParser.push_back(new PARSER_GET_USERS);
-dataParser.push_back(new PARSER_GET_USER);
-dataParser.push_back(new PARSER_CHG_USER);
-dataParser.push_back(new PARSER_ADD_USER);
-dataParser.push_back(new PARSER_DEL_USER);
-dataParser.push_back(new PARSER_CHECK_USER);
-dataParser.push_back(new PARSER_SEND_MESSAGE);
-dataParser.push_back(new PARSER_AUTH_BY);
-dataParser.push_back(new PARSER_USER_INFO);
-
-dataParser.push_back(new PARSER_GET_TARIFFS);
-dataParser.push_back(new PARSER_ADD_TARIFF);
-dataParser.push_back(new PARSER_DEL_TARIFF);
-dataParser.push_back(new PARSER_CHG_TARIFF);
-
-dataParser.push_back(new PARSER_GET_ADMINS);
-dataParser.push_back(new PARSER_CHG_ADMIN);
-dataParser.push_back(new PARSER_DEL_ADMIN);
-dataParser.push_back(new PARSER_ADD_ADMIN);*/
-
-xmlParser = XML_ParserCreate(NULL);
-
-if (!xmlParser)
+
+ if (bind(m_listenSocket, reinterpret_cast<sockaddr *>(&listenAddr), sizeof(listenAddr)) == -1)
{
- logger("Couldn't allocate memory for parser.");
- exit(1);
+ m_errorStr = std::string("Cannot bind listen socket: '") + strerror(errno) + "'.";
+ m_logger(m_errorStr);
+ return -1;
}
+ if (listen(m_listenSocket, 64) == -1) // TODO: backlog length
+ {
+ m_errorStr = std::string("Failed to start listening for connections: '") + strerror(errno) + "'.";
+ m_logger(m_errorStr);
+ return -1;
+ }
+
+ m_running = true;
+ m_stopped = false;
+ return 0;
}
-//-----------------------------------------------------------------------------
-CONFIGPROTO::~CONFIGPROTO()
+
+int CONFIGPROTO::Stop()
{
-for (size_t i = 0; i < dataParser.size(); ++i)
- delete dataParser[i];
-XML_ParserFree(xmlParser);
+ m_running = false;
+ for (int i = 0; i < 5 && !m_stopped; ++i)
+ {
+ struct timespec ts = {0, 200000000};
+ nanosleep(&ts, NULL);
+ }
+
+ if (!m_stopped)
+ {
+ m_errorStr = "Cannot stop listenign thread.";
+ m_logger(m_errorStr);
+ return -1;
+ }
+
+ shutdown(m_listenSocket, SHUT_RDWR);
+ close(m_listenSocket);
+ return 0;
}
-//-----------------------------------------------------------------------------
-int CONFIGPROTO::ParseCommand()
+
+void CONFIGPROTO::Run()
{
-std::list<std::string>::iterator n;
-int done = 0;
-char str[9];
+ while (m_running)
+ {
+ fd_set fds;
-if (requestList.empty())
- return 0;
+ BuildFDSet(fds);
-n = requestList.begin();
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
-strncpy(str, (*n).c_str(), 8);
-str[8] = 0;
+ int res = select(MaxFD() + 1, &fds, NULL, NULL, &tv);
+ if (res < 0)
+ {
+ m_errorStr = std::string("'select' is failed: '") + strerror(errno) + "'.";
+ m_logger(m_errorStr);
+ break;
+ }
+ if (!m_running)
+ break;
+ if (res > 0)
+ HandleEvents(fds);
-XML_ParserReset(xmlParser, NULL);
-XML_SetElementHandler(xmlParser, ParseXMLStart, ParseXMLEnd);
-XML_SetUserData(xmlParser, this);
+ CleanupConns();
+ }
+ m_stopped = true;
+}
-while(nonstop)
- {
- strncpy(str, (*n).c_str(), 8);
- str[8] = 0;
- size_t len = strlen(str);
+int CONFIGPROTO::MaxFD() const
+{
+ int maxFD = m_listenSocket;
+ for (size_t i = 0; i < m_conns.size(); ++i)
+ if (maxFD < m_conns[i]->Sock())
+ maxFD = m_conns[i]->Sock();
+ return maxFD;
+}
- ++n;
- if (n == requestList.end())
- done = 1;
- --n;
+void CONFIGPROTO::BuildFDSet(fd_set & fds) const
+{
+ for (size_t i = 0; i < m_conns.size(); ++i)
+ FD_SET(m_conns[i]->Sock(), &fds);
+}
- if (XML_Parse(xmlParser, (*n).c_str(), static_cast<int>(len), done) == XML_STATUS_ERROR)
- {
- logger("Invalid configuration request");
- printfd(__FILE__, "Parse error at line %d:\n%s\n",
- XML_GetCurrentLineNumber(xmlParser),
- XML_ErrorString(XML_GetErrorCode(xmlParser)));
- if (currParser)
- {
- printfd(__FILE__, "Parser reset\n");
- //currParser->Reset();
- currParser = NULL;
- }
+void CONFIGPROTO::CleanupConns()
+{
+ std::vector<STG::Conn *>::iterator pos;
+ pos = std::remove_if(m_conns.begin(), m_conns.end(), IsFinished());
+ if (pos == m_conns.end())
+ return;
+ std::for_each(pos, m_conns.end(), RemoveConn());
+ m_conns.erase(pos, m_conns.end());
+}
- return -1;
- }
+void CONFIGPROTO::HandleEvents(const fd_set & fds)
+{
+ if (FD_ISSET(m_listenSocket, &fds))
+ AcceptConnection();
+ else
+ {
+ for (size_t i = 0; i < m_conns.size(); ++i)
+ if (FD_ISSET(m_conns[i]->Sock(), &fds))
+ m_conns[i]->Read();
+ }
+}
- if (done)
- return 0;
+void CONFIGPROTO::AcceptConnection()
+{
+ struct sockaddr_in outerAddr;
+ socklen_t outerAddrLen(sizeof(outerAddr));
+ int sock = accept(m_listenSocket, reinterpret_cast<sockaddr *>(&outerAddr), &outerAddrLen);
- ++n;
+ if (sock < 0)
+ {
+ m_errorStr = std::string("Failed to accept connection: '") + strerror(errno) + "'.";
+ printfd(__FILE__, "%s", m_errorStr.c_str());
+ m_logger(m_errorStr);
+ return;
}
-return 0;
+ assert(m_settings != NULL);
+ assert(m_admins != NULL);
+ assert(m_users != NULL);
+ assert(m_tariffs != NULL);
+
+ m_conns.push_back(new STG::Conn(*m_settings, *m_admins, *m_users, *m_tariffs, sock, outerAddr));
+
+ printfd(__FILE__, "New connection from %s:%d\n", inet_ntostring(m_conns.back()->IP()).c_str(), m_conns.back()->Port());
}
-//-----------------------------------------------------------------------------
-void CONFIGPROTO::SetAdmins(ADMINS * a)
+/*
+void CONFIGPROTO::WriteLogAccessFailed(uint32_t ip)
{
-admins = a;
+ m_logger("Admin's connection failed. IP %s", inet_ntostring(ip).c_str());
}
-//-----------------------------------------------------------------------------
+*/
/*
* Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
+ * Author : Maxim Mamontov <faust@stargazer.dp.ua>
*/
- /*
- $Revision: 1.14 $
- $Date: 2010/10/04 20:24:14 $
- $Author: faust $
- */
-
-
#ifndef CONFIGPROTO_H
#define CONFIGPROTO_H
-#include <string>
-#include <list>
-#include <vector>
-
#include "stg/module_settings.h"
#include "stg/os_int.h"
-#include <expat.h>
-#include <pthread.h>
+#include <string>
+#include <vector>
-#define STG_HEADER "SG04"
-#define OK_HEADER "OKHD"
-#define ERR_HEADER "ERHD"
-#define OK_LOGIN "OKLG"
-#define ERR_LOGIN "ERLG"
-#define OK_LOGINS "OKLS"
-#define ERR_LOGINS "ERLS"
+#include <sys/select.h>
+#include <sys/types.h>
+#include <unistd.h>
-class BASE_PARSER;
-class USERS;
+class SETTINGS;
class ADMINS;
-class ADMIN;
class TARIFFS;
+class USERS;
class PLUGIN_LOGGER;
-class STORE;
-class SETTINGS;
-//-----------------------------------------------------------------------------
+namespace STG
+{
+
+class Conn;
+
+}
+
class CONFIGPROTO {
public:
CONFIGPROTO(PLUGIN_LOGGER & l);
- ~CONFIGPROTO();
- void SetPort(uint16_t p) { port = p; }
- void SetAdmins(ADMINS * a);
- uint32_t GetAdminIP() const { return adminIP; }
+ void SetPort(uint16_t port) { m_port = port; }
+ void SetSettings(const SETTINGS * settings) { m_settings = settings; }
+ void SetAdmins(ADMINS * admins) { m_admins = admins; }
+ void SetTariffs(TARIFFS * tariffs) { m_tariffs = tariffs; }
+ void SetUsers(USERS * users) { m_users = users; }
+
int Prepare();
int Stop();
- const std::string & GetStrError() const { return errorStr; }
+ const std::string & GetStrError() const { return m_errorStr; }
void Run();
private:
CONFIGPROTO(const CONFIGPROTO & rvalue);
CONFIGPROTO & operator=(const CONFIGPROTO & rvalue);
- int RecvHdr(int sock);
- int RecvLogin(int sock);
- int SendLoginAnswer(int sock);
- int SendHdrAnswer(int sock, int err);
- int RecvLoginS(int sock);
- int SendLoginSAnswer(int sock, int err);
- int RecvData(int sock);
- int SendDataAnswer(int sock, const std::string & answer);
- int SendError(int sock, const std::string & text);
- void WriteLogAccessFailed(uint32_t ip);
- const std::string & GetDataAnswer() const { return dataAnswer; }
-
- int ParseCommand();
-
- std::list<std::string> requestList;
- uint32_t adminIP;
- std::string adminLogin;
- std::string adminPassword;
- uint16_t port;
- pthread_t thrReciveSendConf;
- bool nonstop;
- int state;
- ADMIN * currAdmin;
- PLUGIN_LOGGER & logger;
- std::string dataAnswer;
-
- int listenSocket;
-
- ADMINS * admins;
-
- BASE_PARSER * currParser;
- std::vector<BASE_PARSER *> dataParser;
-
- XML_Parser xmlParser;
-
- std::string errorStr;
-
- friend void ParseXMLStart(void *data, const char *el, const char **attr);
- friend void ParseXMLEnd(void *data, const char *el);
+ const SETTINGS * m_settings;
+ ADMINS * m_admins;
+ TARIFFS * m_tariffs;
+ USERS * m_users;
+
+ uint16_t m_port;
+ bool m_running;
+ bool m_stopped;
+ PLUGIN_LOGGER & m_logger;
+ int m_listenSocket;
+
+ std::string m_errorStr;
+
+ std::vector<STG::Conn *> m_conns;
+
+ int MaxFD() const;
+ void BuildFDSet(fd_set & fds) const;
+ void CleanupConns();
+ void HandleEvents(const fd_set & fds);
+ void AcceptConnection();
+
+ //void WriteLogAccessFailed(uint32_t ip);
};
-//-----------------------------------------------------------------------------
+
#endif //CONFIGPROTO_H
return HandleBuffer(res);
}
+bool Conn::WriteAnswer(const void* buffer, size_t size)
+{
+ ssize_t res = write(m_sock, buffer, size);
+ if (res < 0)
+ {
+ // TODO: log it
+ return false;
+ }
+ return true;
+}
+
BASE_PARSER * Conn::GetParser(const std::string & tag)
{
if (strcasecmp(tag.c_str(), "getserverinfo") == 0)
{
if (strncmp(m_header, STG_HEADER, sizeof(m_header)) != 0)
{
+ WriteAnswer(ERR_HEADER, sizeof(ERR_HEADER));
// TODO: log it
m_state = ERROR;
return false;
m_state = LOGIN;
m_buffer = m_login;
m_bufferSize = sizeof(m_login);
- return true;
+ return WriteAnswer(OK_HEADER, sizeof(OK_HEADER));
}
bool Conn::HandleLogin()
{
if (m_admins.Find(m_login, &m_admin)) // ADMINS::Find returns true on error.
{
+ WriteAnswer(ERR_LOGIN, sizeof(ERR_LOGIN));
// TODO: log it
m_state = ERROR;
return false;
m_state = CRYPTO_LOGIN;
m_buffer = m_cryptoLogin;
m_bufferSize = sizeof(m_cryptoLogin);
- return true;
+ return WriteAnswer(OK_LOGIN, sizeof(OK_LOGIN));
}
bool Conn::HandleCryptoLogin()
if (strncmp(m_login, login, sizeof(login)) != 0)
{
+ WriteAnswer(ERR_LOGINS, sizeof(ERR_LOGINS));
// TODO: log it
m_state = ERROR;
return false;
m_buffer = m_data;
m_bufferSize = sizeof(m_data);
m_stream = new STG::DECRYPT_STREAM(m_admin->GetPassword(), DataCallback, &m_dataState);
- return true;
+ return WriteAnswer(OK_LOGINS, sizeof(OK_LOGINS));
}
bool Conn::HandleData(size_t size)
return false;
}
+ if (state.final)
+ {
+ if (!state.conn.WriteResponse())
+ {
+ // TODO: log it
+ state.conn.m_state = ERROR;
+ return false;
+ }
+ state.conn.m_state = DONE;
+ }
+
return true;
}
conn.m_parser->End(data, el);
}
+
+bool Conn::WriteResponse()
+{
+ STG::ENCRYPT_STREAM stream(m_admin->GetPassword(), WriteCallback, this);
+ const std::string & answer = m_parser->GetAnswer();
+ stream.Put(answer.c_str(), answer.length() + 1 /* including \0 */, true /* final */);
+ return stream.IsOk();
+}
+
+bool Conn::WriteCallback(const void * block, size_t size, void * data)
+{
+ assert(data != NULL);
+ Conn & conn = *static_cast<Conn *>(data);
+ return WriteAll(conn.m_sock, block, size);;
+}