#include "SocketServer.h" #include "SocketThreadMonitord.h" #include "SocketThreadFMS32.h" #include "SocketThreadCrusader.h" #ifndef WIN32 #include <sys/param.h> #include <arpa/inet.h> // fuer inet_ntoa #else #define usleep Sleep #define socklen_t int #endif #include "memlock.h" #include "MonitorModulesResults.h" #include "base64.h" #include "convert.h" #include "Monitor.h" #include "MonitorLogging.h" #include <stdio.h> #include <fcntl.h> #include <errno.h> #ifdef HAVE_CONFIG_H #include <config.h> #endif using namespace std; // Vielleicht sollte man das hier spaeter mal auslagern ... // TODO Auslagern in externe Dateien (.h, .cpp)-Paar // const int SERVER_PORT = 9333 ; typedef struct {long tv_sec; long tv_usec;} timval; SocketServer::SocketServer(MonitorConfiguration *config, int iLockStartwert) { m_MonitorConfiguration=config ; m_ServerModus=SocketThread::monitord ; m_iLockStartwert=iLockStartwert ; m_bWantStop=false ; } SocketServer::SocketServer(MonitorConfiguration *config, std::string FilterFileName, int iLockStartwert) { m_MonitorConfiguration=config ; m_ServerModus=SocketThread::monitord ; m_iLockStartwert=iLockStartwert ; m_bWantStop=false ; #ifdef LUA m_bUseLUAScript=false ; #endif if (! FilterFileName.empty()) { #ifdef LUA // LUA TEST try { L = lua_open() ; luaL_openlibs(L) ; if(luaL_loadfile(L, FilterFileName.c_str())) { throw std::string(std::string(lua_tostring(L, -1))); } if (lua_pcall(L, 0, 0, 0)) { FILE_LOG(logERROR) << "LUA test fehlgeschlagen" << endl ; } m_bUseLUAScript=true ; FILE_LOG(logINFO) << "Successfully loaded LUA filter: " << FilterFileName ; } catch (const std::string &e) { FILE_LOG(logERROR) << "Error loading lua script: " << e; } #endif } } SocketServer::~SocketServer() { // Beim SocketManager abmelden GetSocketsManager()->removeModule(this); #ifdef LUA if (L!=NULL) { lua_close(L) ; } #endif } bool SocketServer::createListeningSocket() { bool result=false; #ifdef _WIN32 /* Initialisiere TCP fuer Windows ("winsock") */ short wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD (1, 1); if (WSAStartup (wVersionRequested, &wsaData) != 0) { ThrowMonitorException("Failed to init windows sockets (WSAStartup)") ; //fprintf( stderr, "Failed to init windows sockets\n"); //exit(1); } #endif /* Erzeuge das Socket */ m_sock = socket( PF_INET, SOCK_STREAM, 0); if (m_sock < 0) { ThrowMonitorException("Failed to create listening socket") ; //perror( "failed to create socket"); //exit(1); } switch (m_ServerModus) { case SocketThread::fms32pro: m_iPort=m_MonitorConfiguration->m_PortFMS32Pro ; break ; case SocketThread::crusader: m_iPort=m_MonitorConfiguration->m_PortCrusader ; break ; case SocketThread::monitord: default: m_iPort=m_MonitorConfiguration->m_Port ; break ; } if ( (m_iPort<=0) && (m_iPort >65535)) { if (m_ServerModus==SocketThread::monitord) { m_iPort=SERVER_PORT ; } else { m_iPort=0 ; } } /* Erzeuge die Socketadresse des Servers * Sie besteht aus Typ und Portnummer */ memset( &m_server, 0, sizeof (m_server)); m_server.sin_family = AF_INET; if (m_MonitorConfiguration->m_BindIP=="") { m_server.sin_addr.s_addr = htonl( INADDR_ANY); } else { m_server.sin_addr.s_addr = (inet_addr(m_MonitorConfiguration->m_BindIP.c_str())); } m_server.sin_port = htons( m_iPort); int on = 1 ; #ifndef WIN32 setsockopt(m_sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) ; #else setsockopt(m_sock,SOL_SOCKET,SO_REUSEADDR,(char*) &on,sizeof(on)) ; #endif /* Erzeuge die Bindung an die Serveradresse * (d.h. an einen bestimmten Port) */ if (bind( m_sock, (struct sockaddr*)&m_server, sizeof( m_server)) < 0) { // Verbindungsfehler ignorieren, damit die Nachrichten auf jeden Fall // aus dem ModuleResultSet "gepumpt" werden ! // //perror( "can't bind socket"); //exit(1); } else { /* Teile dem Socket mit, dass Verbindungswuensche * von Clients entgegengenommen werden */ listen( m_sock, 10); result=true ; /** * In globaler ManagerListe eintragen/anmelden */ GetSocketsManager()->addModule(this) ; } return result ; } void *SocketServer::Thread() { this->ThreadStarted() ; // Erstmal Bescheid geben, dass wir laufen if (createListeningSocket()) { initSocketThreads() ; } if (memLockOpen( 12345, & m_queueLock) < 0) { ThrowMonitorException("memLockOpen failed") ; } while (!m_bWantStop) { /* Bearbeite die Verbindungswünsche von Clients * in einer Endlosschleife * Der Aufruf von accept() blockiert solange, * bis ein Client Verbindung aufnimmt, oder der timeout abgelaufen ist */ fd_set fdset ; timeval tv ; int result=0 ; int maxHandle ; m_bWantStop=false ; while ((result==0) && (!m_bWantStop)) { tv.tv_sec=0; // Wartezeit auf Socketereignis, danach Kontrolle, ob ein "Sendeauftrag" vorliegt tv.tv_usec=100000 ; FD_ZERO(&fdset) ; // Den Socket m_sock ueberwachen ... FD_SET(m_sock,&fdset); // Diese Zeile sollte man NICHT mit "1" uebersetzen ! // Das geht voll daneben. Es muss m_sock+1 sein ! Auch bei einem (!) Socket ! maxHandle = m_sock + 1; result= select(maxHandle, &fdset, &fdset, &fdset, &tv); // wartet Zeitraum wie in tv definiert if (FD_ISSET(m_sock,&fdset)>0) // Socket ereignis ? { result=1 ; } // Neue Verbindungsanfrage ? // Dann annehmen und als eigenen Thread laufen lassen // if (result>0) { int useSocket=-1 ; // Jetzt freien SocketThread suchen for (int i=0;i<MAX_CLIENTS;++i) { if (!socketThread[i]->IsRunning()) { useSocket=i ; i=MAX_CLIENTS ; } } if ((useSocket>=MAX_CLIENTS) || (useSocket<0)) { // TODO zu viele Clients ! ThrowMonitorException("Too many client connects") ; perror("Zu viele Clients !\r\n") ; exit (1) ; } socklen_t sin_size = sizeof (sockaddr_in); int fd = accept(m_sock,(sockaddr*) &socketThread[useSocket]->m_client,&sin_size); FILE_LOG(logINFO) << "new connection from " << inet_ntoa(socketThread[useSocket]->m_client.sin_addr) ; // Thread mit dem oben angenommenen Socket starten socketThread[useSocket]->setFD(fd) ; socketThread[useSocket]->Start() ; } } } // Listening Socket beenden closesocket( m_sock); // Alle Clients beenden FILE_LOG(logINFO) << "Beende alle Clients" ; for (int i=0;i<MAX_CLIENTS;++i) { FILE_LOG(logDEBUG) << "beende client " << i ; if (socketThread[i]->IsRunning()) { socketThread[i]->closeSocket() ; FILE_LOG(logINFO) << i << ": closesocket done" ; socketThread[i]->Kill() ; FILE_LOG(logINFO) << i << ": kill done" ; } } // uns selbst beenden usleep(1000) ; //JThread::Kill() ; return NULL ; } void SocketServer::initSocketThreads() { int i ; for (i=0;i<MAX_CLIENTS;i++) { switch (m_ServerModus) { case SocketThread::monitord: socketThread[i]=new SocketThreadMonitord(m_MonitorConfiguration, m_iLockStartwert+i,i+1) ; break ; case SocketThread::fms32pro: socketThread[i]=new SocketThreadFMS32(m_MonitorConfiguration,m_iLockStartwert+i,i+1) ; break ; case SocketThread::crusader: socketThread[i]=new SocketThreadCrusader(m_MonitorConfiguration,m_iLockStartwert+i,i+1) ; break; default: break ; } } } void SocketServer::addResult(ModuleResultBase* pRes) { // Nun f?r die nachgeordneten Threads die Ausgabe nach den Uebergabewerte zusammenbauen // #ifdef LUA char eins[255],zwei[255] ; #endif // Wenn ja, Ereignis an alle Threads weitergeben for (int i=0;i<MAX_CLIENTS;++i) { if (socketThread[i]->IsRunning()) { m_bSkipDispatching=false ; // erstmal ggf. LUA Filter aufrufen #ifdef LUA if (m_bUseLUAScript==true) { int z ; /* push functions and arguments */ lua_getglobal(L, "filter"); /* function to be called */ // start array structure lua_newtable( L ); int numCount=1 ; for (ResultItemsMap::iterator iter=pRes->m_Items.begin(); iter!=pRes->m_Items.end(); ++iter) { { memset(eins,0,200) ; memset(zwei,0,200) ; strncpy(eins,iter->first.c_str(),199) ; strncpy(zwei,iter->second.c_str(),199) ; lua_pushstring( L, eins ); lua_pushstring( L, zwei ); lua_rawset( L, -3 ); numCount++ ; } } // noch clientIP, authenticated und loginname dazupacken // Authenticated memset(eins,0,200) ; memset(zwei,0,200) ; strncpy(eins,"client_authenticated",199) ; if (socketThread[i]->isClientAuthenticated()) { strncpy(zwei,"1",199) ; } else { strncpy(zwei,"0",199) ; } lua_pushstring( L, eins ); lua_pushstring( L, zwei ); lua_rawset( L, -3 ); numCount++ ; // ClientIP memset(eins,0,200) ; memset(zwei,0,200) ; strncpy(eins,"client_ip",199) ; strncpy(zwei,socketThread[i]->getClientIP().c_str(),199) ; lua_pushstring( L, eins ); lua_pushstring( L, zwei ); lua_rawset( L, -3 ); numCount++ ; // Loginname memset(eins,0,200) ; memset(zwei,0,200) ; strncpy(eins,"client_loginname",199) ; strncpy(zwei,socketThread[i]->getClientLogin().c_str(),199) ; lua_pushstring( L, eins ); lua_pushstring( L, zwei ); lua_rawset( L, -3 ); numCount++ ; // Clienttype memset(eins,0,200) ; memset(zwei,0,200) ; strncpy(eins,"client_type",199) ; switch (m_ServerModus) { case SocketThread::monitord: strncpy(zwei,"monitord",199) ; break ; case SocketThread::fms32pro: strncpy(zwei,"fms32",199) ; break ; case SocketThread::crusader: strncpy(zwei,"crusader",199) ; break ; default: strncpy(zwei,"unknown",199) ; break ; } lua_pushstring( L, eins ); lua_pushstring( L, zwei ); lua_rawset( L, -3 ); numCount++ ; // set the number of elements (index to the last array element) lua_pushliteral( L, "n" ); lua_pushnumber( L, numCount-1 ); lua_rawset( L, -3 ); // set the name of the array that the script will access lua_setglobal( L, "arg" ); /* do the call (2 arguments, 1 result) */ if (lua_pcall(L, 0, LUA_MULTRET, 0) != 0) { FILE_LOG(logERROR) << "Fehler beim Aufruf lua dispatcher script:" << lua_tostring(L, -1); //error(L, "error running function `f': %s", // lua_tostring(L, -1)); } /* retrieve result */ if (!lua_isnumber(L, -1)) { FILE_LOG(logERROR) << "nicht-numerische Antwort vom lua dispatcher script" ; //error(L, "function `f' must return a number"); } z = lua_tonumber(L, -1); lua_pop(L, 1); /* pop returned value */ FILE_LOG(logDEBUG1) << "lua Result (global dispatcher)" << z ; if (z==1) m_bSkipDispatching=true ; } #endif if (m_bSkipDispatching==false) { socketThread[i]->addResult(pRes) ; } } } } /* * ----------------------------------------------------------- * ThreadBase * ----------------------------------------------------------- */ ThreadBase::~ThreadBase() { } ThreadBase::ThreadBase(int locknum) { m_iLockNum= locknum ; m_exitThread=false ; } bool ThreadBase::createLock() { if ( memLockCreate( m_iLockNum, &m_Lock) < 0) { ThrowMonitorException("memLockCreate failed for " + convertIntToString(m_iLockNum)) ; } return true; } void ThreadBase::releaseLock() { memLockDestroy(m_Lock) ; } /* * ----------------------------------------------------------- * SocketThread * ----------------------------------------------------------- */ SocketThread::~SocketThread() { } SocketThread::SocketThread(MonitorConfiguration *config, int locknum, int PortNum, SocketMode ServerMode) : ThreadBase(locknum) { m_MonitorConfiguration = config; m_iPortNum = PortNum ; m_ServerMode = ServerMode ; ResetThreadVars() ; } void SocketThread::setFD(int fd) { m_fd=fd ; } void SocketThread::ResetThreadVars() { // Zuruecksetzen: // Kein Socket zugewiesen // Client ist nicht angemeldet // Thread soll sich nicht beenden // m_fd=0 ; m_authenticated=false ; m_exitThread=false ; // Commandbuffer loeschen memset(m_CommandBuffer,0,MAX_COMMANDLINE) ; } void *SocketThread::Thread() { if (m_fd==0) { ThrowMonitorException("SocketThread started wird fd=NULL") ; return NULL ; } this->ThreadStarted() ; // Erstmal Bescheid geben, dass wir laufen createLock() ; createSocket() ; FILE_LOG(logINFO) << "SocketThreads exits" ; releaseLock() ; return NULL; } std::string SocketThread::createZVEIOutputString(ModuleResultBase Result) { std::string socketText="" ; return socketText ; } std::string SocketThread::createPOCSAGOutputString(ModuleResultBase Result) { std::string socketText="" ; return socketText ; } std::string SocketThread::createFMSOutputString(ModuleResultBase Result) { std::string socketText="" ; return socketText ; } std::string SocketThread::createOutputString(ModuleResultBase Result) { std::string socketText="" ; if (Result["typ"]=="fms") { socketText=createFMSOutputString(Result) ; } else if (Result["typ"]== "pocsag") { socketText=createPOCSAGOutputString(Result) ; } else if (Result["typ"]=="zvei") { socketText=createZVEIOutputString(Result) ; } return socketText ; } void SocketThread::addResult(ModuleResultBase* pRes) { std::string outText ; outText=createOutputString(*pRes); addOutputText(outText) ; } void SocketThread::addOutputText(std::string outText) { memLock(m_Lock) ; if (!(outText=="")) { m_outputStrings.insert(m_outputStrings.begin(),outText) ; } memUnlock(m_Lock) ; } void SocketThread::say(const std::string& something) { unsigned int len=send( m_fd, something.c_str(), something.length(), 0); if (len!=something.length()) { FILE_LOG(logERROR) << "error sending date to client. thread exiting" ; doLogout() ; } } void SocketThread::say(const char *something) { unsigned int len = send( m_fd, something, strlen(something), 0); if (len!=strlen(something)) { FILE_LOG(logERROR) << "error sending date to client. thread exiting" ; doLogout() ; } } void SocketThread::processInput() { } void SocketThread::doLogout() { m_exitThread=true ; } void SocketThread::sayWelcome() { } void SocketThread::sayGoodbye() { } void SocketThread::createSocket() { timeval tv ; if (m_fd < 0) { ThrowMonitorException("accept failed") ; //perror( "accept failed"); //exit(1); } // Hier ist die Sitzung noch nicht "authenticated" fd_set fdset ; FD_ZERO(&fdset) ; FD_SET(m_fd,&fdset); m_sClientIP= inet_ntoa(m_client.sin_addr) ; m_authenticated=false ; // Gültige IP Adresse, die sich nicht anmelden muss ? if (m_MonitorConfiguration->IsValidLogin("","",this->m_sClientIP)) { FILE_LOG(logINFO) << "login authentication (ip allowed): " << m_sClientIP ; this->m_authenticated=true ; } // FMS32Pro ist dafuer zu doof ;-) // if ((m_ServerMode==SocketThread::fms32pro)) { // TODO: IP Liste auch bei FMS32 anwenden ? dann nachfolgendes auskommentiert lassen // this->m_authenticated=true ; } m_CommandBuffer[0]='\0'; // Wir sagen natuerlich brav "Guten Tag" ... sayWelcome() ; fd_set fdset_write ; fd_set fdset_exceptions ; for (;;) { if (m_exitThread) { break ; } // Fuer eine Sekunde auf Meldung am Port warten tv.tv_sec=0 ; tv.tv_usec=10000 ; FD_ZERO(&fdset) ; FD_SET(m_fd,&fdset); // Neu: write + exceptions FD_ZERO(&fdset_write) ; FD_ZERO(&fdset_exceptions) ; //FD_SET(m_fd,&fdset_write); FD_SET(m_fd,&fdset_exceptions); int result = select(1, &fdset, NULL, &fdset_exceptions, &tv); if (FD_ISSET(m_fd,&fdset)>0) // Socket ereignis ? { FILE_LOG(logDEBUG) << "Socket reports read event" ; result=1 ; } if (FD_ISSET(m_fd,&fdset_write)>0) // Problem ? { FILE_LOG(logDEBUG) << "Socket reports write event" ; result=0 ; //m_exitThread=true ; } if (FD_ISSET(m_fd,&fdset_exceptions)>0) // Problem ? { FILE_LOG(logDEBUG) << "Socket reports exception event" ; result=0 ; m_exitThread=true ; } if (result>0) { char buffer[RECV_BUFFER] ; char * posPtr ; int gelesen ; gelesen=recv (m_fd, buffer, RECV_BUFFER-1, 0) ; if (gelesen<=0) // Nix am Port, aber doch Port Event ? { FILE_LOG(logINFO) << "recv()<=0 => socketthread exiting" ; m_exitThread=true ; } buffer[gelesen]='\0'; /* gelesenen Puffer an evtl. vorhanden Reste im Kommandopuffer anhängen */ strncat(m_CommandBuffer,buffer,MAX_COMMANDLINE-strlen(m_CommandBuffer)-1) ; /* Erst wenn mindestens eine Eingabezeile empfangen wurde, werden alle vollständigen Zeilen ausgewertet */ while ((posPtr=strstr(m_CommandBuffer,"\r\n"))>0) { /* Puffer nach dem CRLF zwischenspeichern */ char tempbuffer[MAX_COMMANDLINE] ; tempbuffer[0]='\0'; strncpy(tempbuffer,&posPtr[2],MAX_COMMANDLINE) ; /* Kommando mit dem Zeilenende beenden */ posPtr[0]='\0'; processInput() ; /* restlichen Puffer nach dem CRLF wiederherstellen */ strncpy(m_CommandBuffer,tempbuffer,MAX_COMMANDLINE) ; } } else { // Nix tun } // Gibt es etwas zu senden ? // memLock(m_Lock) ; while (!m_outputStrings.empty()) { std::string retString=m_outputStrings.back() ; if (m_authenticated) { retString.push_back('\r') ; retString.push_back('\n') ; say(retString) ; } m_outputStrings.pop_back() ; } memUnlock(m_Lock) ; } closesocket(m_fd); ResetThreadVars() ; } std::string SocketThread::getClientIP() { return (m_sClientIP) ; } std::string SocketThread::getClientLogin() { return (m_loginname) ; } bool SocketThread::isClientAuthenticated() { return (m_authenticated) ; } void SocketThread::closeSocket() { closesocket( m_fd); } bool SocketThread::paramIsHex(int param) { // TODO return true ; } bool SocketThread::paramIsBase64(int param) { // TODO return true ; } bool SocketThread::HexToString(int param, std::string &result) { return convertHexToString(m_cmdParam[param],result); } // ---------------------------------------- //MonitorSocketsManager MonitorSocketsManager *GlobalMonitorSocketsManager=NULL; MonitorSocketsManager* GetSocketsManager() { if (GlobalMonitorSocketsManager==NULL) { GlobalMonitorSocketsManager = new MonitorSocketsManager(); } return (GlobalMonitorSocketsManager); } MonitorSocketsManager::MonitorSocketsManager() { FILE_LOG(logDEBUG) << "SocketManager erstellt" ; if ( memLockCreate( 12347, & m_MemLock) < 0) { ThrowMonitorException("SocketsManager: memLockCreate failed") ; } m_bStop=false ; } MonitorSocketsManager::~MonitorSocketsManager() { } bool MonitorSocketsManager::addModule(SocketServer* pServer) { m_Modules.push_back(pServer) ; return true ; } bool MonitorSocketsManager::dispatchResult(ModuleResultBase *pRes) { tMonitorSocketServerVector::iterator i ; for (i= m_Modules.begin(); i< m_Modules.end(); i++) { (*i)->addResult(pRes) ; } return true ; } bool MonitorSocketsManager::removeModule(SocketServer* pServer) { return true ; }