#include #include #include #include #include #include #include /*! ** ** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com ** ** Portions of this source has been released with the PhysXViewer application, as well as ** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. ** ** If you find this code useful or you are feeling particularily generous I would ** ask that you please go to http://www.amillionpixels.us and make a donation ** to Troy DeMolay. ** ** DeMolay is a youth group for young men between the ages of 12 and 21. ** It teaches strong moral principles, as well as leadership skills and ** public speaking. The donations page uses the 'pay for pixels' paradigm ** where, in this case, a pixel is only a single penny. Donations can be ** made for as small as $4 or as high as a $100 block. Each person who donates ** will get a link to their own site as well as acknowledgement on the ** donations blog located here http://www.amillionpixels.blogspot.com/ ** ** If you wish to contact me you can use the following methods: ** ** Skype ID: jratcliff63367 ** Yahoo: jratcliff63367 ** AOL: jratcliff1961 ** email: jratcliffscarab@gmail.com ** Personal website: http://jratcliffscarab.blogspot.com ** Coding Website: http://codesuppository.blogspot.com ** FundRaising Blog: http://amillionpixels.blogspot.com ** Fundraising site: http://www.amillionpixels.us ** New Temple Site: http://newtemple.blogspot.com ** ** ** The MIT license: ** ** Permission is hereby granted, freeof charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is furnished ** to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in all ** copies or substantial portions of the Software. ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //*********************************************************************************** //*** This code snippet allows you to embed a telnet client or server easily into any //*** of your applications. Currently this code only builds for Windows and the XBOX //*** It is very desirable to me to have this code build for APPLE, Linux, and Iphone //*** If you wish to contribute to this cause, please let me know and I will add you //*** as a developer to the google code page. http://code.google.com/p/telnet //*** //*** To test the program simply run 'telnet.exe'. //*** //*** The first time you run it, it will be set up as a telnet server. You can //*** connect to it by runing a telnet client and typing 'open localhost 23' //*** If you run the program a second time, it will detect that a server is already //*** using Port 23 and will instead start up as a client. You can now send messages //*** between the two instances of the application. //*** You can keep launching as many of them as you wish. //*********************************************************************************** #if defined(_XBOX) #include "NxXBOX.h" #include #endif #if defined(WIN32) #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x400 #endif #include #endif #if defined(_XBOX) #include "NxXBOX.h" #endif #if defined(__APPLE__) || defined(LINUX) #include #endif #include "telnet.h" // Common Telnet Functionality. #ifdef WIN32 #pragma warning(disable:4786 4996) #pragma comment(lib,"wsock32.lib") #endif TELNET::Telnet *gTelnet=0; // optional global variable representing the TELNET singleton for the application. namespace TELNET { //****************************************************** //*** Mutex layer //****************************************************** class OdfMutex { public: OdfMutex(void); ~OdfMutex(void); public: // Blocking Lock. void Lock(void); // Non-blocking Lock. Return's false if already locked. bool TryLock(void); // Unlock. void Unlock(void); private: #if defined(WIN32) || defined(_XBOX) CRITICAL_SECTION m_Mutex; #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_t m_Mutex; #endif }; OdfMutex::OdfMutex(void) { #if defined(WIN32) || defined(_XBOX) InitializeCriticalSection(&m_Mutex); #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_init(&m_Mutex, 0); #endif } OdfMutex::~OdfMutex(void) { #if defined(WIN32) || defined(_XBOX) DeleteCriticalSection(&m_Mutex); #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_destroy(&m_Mutex); #endif } // Blocking Lock. void OdfMutex::Lock(void) { #if defined(WIN32) || defined(_XBOX) EnterCriticalSection(&m_Mutex); #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_lock(&m_Mutex); #endif } // Non-blocking Lock. Return's false if already locked. bool OdfMutex::TryLock(void) { bool bRet = false; #if defined(WIN32) || defined(_XBOX) //assert(("TryEnterCriticalSection seems to not work on XP???", 0)); bRet = TryEnterCriticalSection(&m_Mutex) ? true : false; #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_trylock(&m_Mutex) #endif return bRet; } // Unlock. void OdfMutex::Unlock(void) { #if defined(WIN32) || defined(_XBOX) LeaveCriticalSection(&m_Mutex); #elif defined(__APPLE__) || defined(LINUX) pthread_mutex_unlock(&m_Mutex) #endif } //****************************************************** //*** End Of Mutex layer //****************************************************** //****************************************************** //*** Threading layer //****************************************************** class OdfThread { protected: // Called when the thread is started. This method runs on the Thread. virtual void OnThreadExecute(void)=0; public: OdfThread(void); ~OdfThread(void); public: // Start execution of the thread. void ThreadExecute(void); // Wait for the Thread to terminate. void ThreadWait(void); // Stop the thread's execution (not safe). void ThreadKill(void); protected: // Safely Quit the thread. void ThreadQuit(void); private: HANDLE m_hThread; friend static DWORD WINAPI _ODFThreadFunc(LPVOID arg); }; static DWORD WINAPI _ODFThreadFunc(LPVOID arg) { ((OdfThread *)arg)->OnThreadExecute(); return 0; } OdfThread::OdfThread(void) { m_hThread = 0; } OdfThread::~OdfThread(void) { assert(!m_hThread); } // Start execution of the thread. void OdfThread::ThreadExecute(void) { if(!m_hThread) { m_hThread = CreateThread(0, 0, _ODFThreadFunc, this, 0, 0); } } // Wait for the Thread to terminate. void OdfThread::ThreadWait(void) { if(m_hThread) { WaitForSingleObject(m_hThread, INFINITE); } } // Safely Quit the thread. void OdfThread::ThreadQuit(void) { if(m_hThread) { m_hThread = 0; ExitThread(0); } } // Stop the thread's execution (not safe). void OdfThread::ThreadKill(void) { if(m_hThread) { #if defined(WIN32) TerminateThread(m_hThread, 0); #endif #if defined(_XBOX) //-- TODO: Please figure out the equivalent of TerminateThread assert(false); #endif CloseHandle(m_hThread); m_hThread = 0; } } //****************************************************** //*** End of Threading layer //****************************************************** //****************************************************** //** The Telnet header file //****************************************************** class TelnetLineNode; typedef std::queue TelnetLineNodeQueue; #ifdef WIN32 #pragma warning(push) #pragma warning(disable:4996) #endif class TelnetBlock { public: TelnetBlock(void) { m_uiClient = 0; m_pcName = 0; m_pData = 0; m_uiDataSize = 0; } ~TelnetBlock(void) { Free(); } void setBlock(unsigned int client,const char *name,const void *mem,unsigned int len) { Free(); m_uiClient = client; if ( name ) { size_t len = strlen(name); m_pcName = new char[len+1]; strcpy(m_pcName,name); } if ( mem && len ) { m_pData = new char[len]; memcpy(m_pData,mem,len); m_uiDataSize = len; } } const void *GetData(void) { return m_pData; } unsigned int GetSize(void) { return m_uiDataSize; } void Free(void) { if(m_pcName) { delete [] m_pcName; m_pcName = 0; } if(m_pData) { delete [] m_pData; m_pData = 0; } } unsigned int m_uiClient; char *m_pcName; void *m_pData; unsigned int m_uiDataSize; }; typedef std::queue TelnetBlockQueue; // Common Telnet Functionality. class TelnetInterface { public: // Called when a connection has been established. virtual void OnConnect(unsigned int uiClient) {} public: // Sends text across the telnet connection. // returns false on failure. virtual bool SendText(unsigned int uiClient, const char *pcLine, ...)=0; // Sends a binary block of data across the telnet session. // returns false on failure. virtual bool SendBlock(const TelnetBlock &block)=0; public: // Waits until there is a block ready to be read. bool WaitForBlock(void); // Pops the last line off the local queue. // returns 0 if no lines available. const char *GetLine(unsigned int &uiClient); // Pops the last binary block off the local queue. // returns 0 if no lines available. bool GetBlock(TelnetBlock &block); void ReleaseBlock(TelnetBlock &block); protected: // Add a Line to the Local Queue. void PushLine(const char *pcLine, unsigned int uiClient); // Add a block to the local queue. void PushBlock(TelnetBlock *pBlock); protected: TelnetInterface(void); TelnetInterface(const TelnetInterface&){} ~TelnetInterface(void); private: OdfMutex m_LineMutex; TelnetLineNodeQueue m_Lines; char *m_pcLineBuffer; OdfMutex m_BlockMutex; OdfMutex m_HaveBlock; TelnetBlockQueue m_Blocks; }; #ifdef WIN32 #pragma warning(pop) #endif //****************************************************** //** The Telnet header file //****************************************************** class TelnetLineNode { public: char *pcLine; unsigned int uiClient; }; // Waits until there is a block ready to be read. bool TelnetInterface::WaitForBlock(void) { bool ret = false; if(!m_Blocks.empty()) return true; m_HaveBlock.Lock(); if(!m_Blocks.empty()) ret = true; m_HaveBlock.Unlock(); return ret; } // Pops the last line off the local queue. // returns 0 if no lines available. const char *TelnetInterface::GetLine(unsigned int &uiClient) { const char *pRet = 0; m_LineMutex.Lock(); if(!m_Lines.empty()) { TelnetLineNode *node = m_Lines.front(); m_Lines.pop(); if(m_pcLineBuffer) { ::free(m_pcLineBuffer); m_pcLineBuffer = 0; } m_pcLineBuffer = node->pcLine; uiClient = node->uiClient; delete node; pRet = m_pcLineBuffer; } m_LineMutex.Unlock(); return pRet; } // Pops the last binary block off the local queue. // returns 0 if no lines available. bool TelnetInterface::GetBlock(TelnetBlock &block) { bool ret = false; m_BlockMutex.Lock(); if(!m_Blocks.empty()) { TelnetBlock *pBlock = m_Blocks.front(); m_Blocks.pop(); memcpy(&block, pBlock, sizeof(TelnetBlock)); delete pBlock; ret = true; } m_BlockMutex.Unlock(); if(m_Blocks.empty()) { m_HaveBlock.TryLock(); } return ret; } void TelnetInterface::ReleaseBlock(TelnetBlock &block) { block.Free(); } // Add a Line to the Local Queue. void TelnetInterface::PushLine(const char *pcLine, unsigned int uiClient) { m_LineMutex.Lock(); TelnetLineNode *node = new TelnetLineNode; node->pcLine = (char*)::malloc(sizeof(char)*(strlen(pcLine)+1)); strcpy(node->pcLine, pcLine); node->uiClient = uiClient; m_Lines.push(node); m_LineMutex.Unlock(); } // Add a block to the local queue. void TelnetInterface::PushBlock(TelnetBlock *pBlock) { assert(pBlock); m_BlockMutex.Lock(); m_Blocks.push(pBlock); m_BlockMutex.Unlock(); m_HaveBlock.TryLock(); m_HaveBlock.Unlock(); } TelnetInterface::TelnetInterface(void) { m_pcLineBuffer = 0; m_HaveBlock.Lock(); } TelnetInterface::~TelnetInterface(void) { if(m_pcLineBuffer) { ::free(m_pcLineBuffer); } while(!m_Lines.empty()) { TelnetLineNode *pLine = m_Lines.front(); ::free(pLine->pcLine); delete pLine; m_Lines.pop(); } m_HaveBlock.TryLock(); m_HaveBlock.Unlock(); } //************************************************************************************ //** The Telnet Parser header file. //************************************************************************************ typedef std::queue StringQueue; class _Block { public: void *m_pData; unsigned int m_uiDataSize; }; typedef std::queue<_Block> BlockQueue; class TelnetParser { public: TelnetParser(void) { m_uiBufferSize = 1024; m_uiBufferUsed = 0; m_pBuffer = (char *)::malloc(sizeof(char)*m_uiBufferSize); m_pcLastLine = 0; m_uiCurrBlockSize = 1024; m_uiCurrBlockUsed = 0; m_pCurrBlock = (char *)::malloc(sizeof(char)*m_uiCurrBlockSize); m_bBlockMode = false; } ~TelnetParser(void) { if(m_pBuffer) { ::free(m_pBuffer); } if(m_pcLastLine) { ::free(m_pcLastLine); } if(m_pCurrBlock) { ::free(m_pCurrBlock); } while(!m_Lines.empty()) { char *pTemp = m_Lines.front(); m_Lines.pop(); if(pTemp) { ::free(pTemp); } } while(!m_Blocks.empty()) { _Block &block = m_Blocks.front(); if(block.m_pData) { ::free(block.m_pData); } m_Blocks.pop(); } } public: void AddBuffer(const char *pcBuffer, unsigned int uiLen) { unsigned int uiNewSize = uiLen + m_uiBufferUsed; if(uiNewSize >= m_uiBufferSize) Resize(uiNewSize); for(unsigned int i=0; i= 32 && c < 128) { // simply add the character. m_pBuffer[m_uiBufferUsed++] = c; } else if(c == '\n' && m_uiBufferUsed) { // Add a line to the queue. char *pcLine = (char*)::malloc(sizeof(char)*(m_uiBufferUsed+1)); memcpy(pcLine, m_pBuffer, m_uiBufferUsed); pcLine[m_uiBufferUsed] = 0; m_Lines.push(pcLine); m_uiBufferUsed = 0; } else if(c == 8 && m_uiBufferUsed) { // if backspace then remove the last character. m_uiBufferUsed--; } else { // ??? } m_pBuffer[m_uiBufferUsed] = 0; char *pBlockStart = strstr(m_pBuffer, "'); if(end) { if(equals && equals < end) { char *name = equals+1; unsigned int len = (unsigned int)(end - name) + 1; } m_bBlockMode = true; } } } void BlockChar(char c) { if(m_uiCurrBlockUsed+1 >= m_uiCurrBlockSize) { unsigned int uiNewSize = m_uiCurrBlockSize * 2; char *pBlock = (char*)::malloc(sizeof(char)*uiNewSize); memcpy(pBlock, m_pCurrBlock, m_uiCurrBlockUsed); if(m_pCurrBlock) { ::free(m_pCurrBlock); } m_pCurrBlock = pBlock; m_uiCurrBlockSize = uiNewSize; } m_pCurrBlock[m_uiCurrBlockUsed++] = c; m_pCurrBlock[m_uiCurrBlockUsed] = 0; char *pcEnd = strstr(m_pCurrBlock, ""); if(pcEnd) { m_bBlockMode = false; unsigned int uiSize = (unsigned int)(pcEnd - m_pCurrBlock); _Block block; block.m_uiDataSize = uiSize; block.m_pData = (char*)::malloc(sizeof(char)*uiSize); memcpy(block.m_pData, m_pCurrBlock, uiSize); m_Blocks.push(block); for(unsigned int i=uiSize+9; iThreadFunc(); ExitThread(0); return 0; } void TelnetClient::ThreadFunc(void) { bool bDone = false; m_Mutex.Lock(); SOCKET clientSocket = m_Socket; bDone = clientSocket == INVALID_SOCKET ? true : false; m_Mutex.Unlock(); #define BUFFER_SIZE 8192 char vcBuffer[BUFFER_SIZE+1]; TelnetParser parser; while(!bDone) { int iBytesRead = 0; iBytesRead = recv(clientSocket, vcBuffer, BUFFER_SIZE, 0); if(iBytesRead <= 0) { bDone = true; m_Mutex.Lock(); closesocket(clientSocket); clientSocket = INVALID_SOCKET; m_Socket = clientSocket; m_Mutex.Unlock(); } else { parser.AddBuffer(vcBuffer, iBytesRead); } const char *pcLine = 0; while((pcLine = parser.GetLine())) { PushLine(pcLine, 0); } const void *pBlockData = 0; unsigned int uiBlockSize = 0; while((pBlockData = parser.GetBlock(uiBlockSize))) { TelnetBlock *pBlock = new TelnetBlock; pBlock->m_pData = (void *)pBlockData; pBlock->m_uiDataSize = uiBlockSize; pBlock->m_uiClient = 0; pBlock->m_pcName = 0; PushBlock(pBlock); } m_Mutex.Lock(); clientSocket = m_Socket; bDone = clientSocket == INVALID_SOCKET ? true : false; m_Mutex.Unlock(); } } /***************** ** TelnetClient ** *****************/ TelnetClient::TelnetClient(void) { m_Socket = INVALID_SOCKET; m_Thread = 0; WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } TelnetClient::~TelnetClient(void) { Close(); } // Connect to a remote Telnet server. // returns false on failure. bool TelnetClient::Connect(const char *pcAddress, unsigned short uiPort) { bool bRet = false; Close(); m_Thread = 0; #if defined(WIN32) m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(m_Socket != INVALID_SOCKET) { sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(uiPort); addr.sin_addr.s_addr = inet_addr(pcAddress); if(addr.sin_addr.s_addr == INADDR_NONE) { hostent *pHost = gethostbyname(pcAddress); memcpy(&addr.sin_addr, pHost->h_addr, pHost->h_length); addr.sin_family = pHost->h_addrtype; } if(connect(m_Socket, (sockaddr *)&addr, sizeof(addr)) == 0) { OnConnect(0); m_Thread = CreateThread(0, 0, _TelnetClientFunc, this, 0, 0); bRet = true; } else { assert(0); } if(!m_Thread) { int error = WSAGetLastError(); closesocket(m_Socket); m_Socket = INVALID_SOCKET; } } else { assert(0); } #endif return bRet; } // Closes the current connection. void TelnetClient::Close(void) { if(m_Socket != INVALID_SOCKET) { // Close the Socket. m_Mutex.Lock(); closesocket(m_Socket); m_Socket = INVALID_SOCKET; m_Mutex.Unlock(); // Wait for the Listen Thread to stop. WaitForSingleObject(m_Thread, INFINITE); m_Thread = 0; } } // Sends text across the telnet connection. // returns false on failure. bool TelnetClient::SendText(unsigned int uiClient, const char *pcLine, ...) { char vcBuffer[8192]; _vsnprintf(vcBuffer,8191, pcLine, (va_list)(&pcLine+1)); unsigned int uiLen = (unsigned int)strlen(vcBuffer); send(m_Socket, vcBuffer, uiLen, 0); return true; } // Sends a binary block of data across the telnet session. // returns false on failure. bool TelnetClient::SendBlock(const TelnetBlock &block) { bool bRet = false; char vcFooter[] = "\r\n"; char vcHeader[256]; sprintf(vcHeader, "\r\n", block.m_pcName); send(m_Socket, vcHeader, (int)strlen(vcHeader),0); // Send in small chunks... const char *pData = (const char*)block.m_pData; unsigned int uiDataSent = 0; while(uiDataSent < block.m_uiDataSize) { unsigned int uiSendSize = block.m_uiDataSize - uiDataSent; uiSendSize = uiSendSize > 1024 ? 1024 : uiSendSize; int iSent = send(m_Socket, &pData[uiDataSent], uiSendSize, 0); uiDataSent += (unsigned int)iSent; } send(m_Socket, vcFooter,(int)strlen(vcFooter), 0); bRet = true; return bRet; } //**************************************************************************** //** Telnet server header file //*************************************************************************** class TelnetServer_Client; typedef std::map TelnetServer_ClientMap; // Simple Telnet Server. class TelnetServer : public TelnetInterface { friend class TelnetServer_Client; public: TelnetServer(void); ~TelnetServer(void); public: // Starts the Telnet server listening on // the specified port. // returns false if failure. bool Listen(unsigned short uiPort); // Closes all connections. void Close(void); public: // Sends text across the telnet connection. // returns false on failure. virtual bool SendText(unsigned int uiClient, const char *pcLine, ...); // Sends a binary block of data across the telnet session. // returns false on failure. virtual bool SendBlock(const TelnetBlock &block); private: void ThreadFunc(void); friend DWORD WINAPI _TelnetServerFunc(LPVOID arg); private: TelnetServer(const TelnetServer &){} private: unsigned int m_uiLastClient; OdfMutex m_ListenMutex; SOCKET m_ListenSocket; HANDLE m_ListenThread; TelnetServer_ClientMap m_Clients; }; //**************************************************************************** //** Telnet server source //**************************************************************************** class TelnetServer_Client { public: TelnetServer *m_pParent; unsigned int m_uiClient; OdfMutex m_Mutex; SOCKET m_Socket; HANDLE m_Thread; public: ~TelnetServer_Client(void) { TelnetServer_ClientMap::iterator iter; iter = m_pParent->m_Clients.find(m_uiClient); if(iter != m_pParent->m_Clients.end()) { m_pParent->m_Clients[m_uiClient] = 0; //m_pParent->m_Clients.erase(iter); } } private: void ThreadFunc(void); friend DWORD WINAPI _TelnetServerClientFunc(LPVOID arg); }; /********************** ** Threading Support ** **********************/ static DWORD WINAPI _TelnetServerClientFunc(LPVOID arg) { TelnetServer_Client *pClient = (TelnetServer_Client *)arg; pClient->ThreadFunc(); delete pClient; ExitThread(0); return 0; } static DWORD WINAPI _TelnetServerFunc(LPVOID arg) { ((TelnetServer *)arg)->ThreadFunc(); ExitThread(0); return 0; } void TelnetServer_Client::ThreadFunc(void) { bool bDone = false; OdfMutex &mutex = m_Mutex; #define BUFFER_SIZE 8192 char vcBuffer[BUFFER_SIZE]; TelnetParser parser; mutex.Lock(); SOCKET clientSocket = m_Socket; bDone = clientSocket == INVALID_SOCKET ? true : false; mutex.Unlock(); while(!bDone) { int iBytesRead=0; #if !defined(_XBOX) iBytesRead = recv(clientSocket, vcBuffer, BUFFER_SIZE, 0); #endif if(iBytesRead > 0) { parser.AddBuffer(vcBuffer, iBytesRead); } const char *pcLine = 0; while((pcLine = parser.GetLine())) { m_pParent->PushLine(pcLine, m_uiClient); } mutex.Lock(); clientSocket = m_Socket; bDone = clientSocket == INVALID_SOCKET ? true : false; mutex.Unlock(); } } void TelnetServer::ThreadFunc(void) { bool bDone = false; m_ListenMutex.Lock(); SOCKET listenSocket = m_ListenSocket; bDone = listenSocket == INVALID_SOCKET ? true : false; m_ListenMutex.Unlock(); while(!bDone) { SOCKET clientSocket= INVALID_SOCKET; #if !defined(_XBOX) clientSocket = accept(listenSocket, 0, 0); #endif if(clientSocket != INVALID_SOCKET) { TelnetServer_Client *pClient = new TelnetServer_Client; pClient->m_pParent = this; pClient->m_uiClient = ++m_uiLastClient; pClient->m_Socket = clientSocket; m_Clients[pClient->m_uiClient] = pClient; pClient->m_Thread = CreateThread(0, 0, _TelnetServerClientFunc, pClient, 0, 0); OnConnect(pClient->m_uiClient); } m_ListenMutex.Lock(); listenSocket = m_ListenSocket; bDone = listenSocket == INVALID_SOCKET ? true : false; m_ListenMutex.Unlock(); } TelnetServer_ClientMap::iterator iter; for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++) { TelnetServer_Client *pClient = (*iter).second; if(pClient) { OdfMutex &mutex = pClient->m_Mutex; // Signal the socket. mutex.Lock(); #if !defined(_XBOX) closesocket(pClient->m_Socket); #endif pClient->m_Socket = INVALID_SOCKET; mutex.Unlock(); // Wait for the thread to stop. WaitForSingleObject(pClient->m_Thread, INFINITE); } } m_Clients.clear(); } /***************** ** TelnetServer ** *****************/ TelnetServer::TelnetServer(void) { m_uiLastClient = 1; m_ListenSocket = INVALID_SOCKET; m_ListenThread = 0; #if !defined(_XBOX) WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); #endif } TelnetServer::~TelnetServer(void) { Close(); } // Starts the Telnet server listening on // the specified port. // returns false if failure. bool TelnetServer::Listen(unsigned short uiPort) { bool bRet = false; Close(); m_ListenThread = 0; #if !defined(_XBOX) m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(m_ListenSocket != INVALID_SOCKET) { sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(uiPort); addr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(m_ListenSocket, (sockaddr*)&addr, sizeof(addr)) == 0) { if(listen(m_ListenSocket, SOMAXCONN) == 0) { m_ListenThread = CreateThread(0, 0, _TelnetServerFunc, this, 0, 0); bRet = true; } else { int error = WSAGetLastError(); assert(0); } } else // if(bind(m_ListenSocket, (sockaddr*)&addr, sizeof(addr)) == 0) { int error = WSAGetLastError(); // assert(0); } if(!m_ListenThread) { int error = WSAGetLastError(); closesocket(m_ListenSocket); m_ListenSocket = INVALID_SOCKET; } } else // if(m_ListenSocket != INVALID_SOCKET) { int error = WSAGetLastError(); assert(0); } // assert(m_ListenSocket != INVALID_SOCKET); #endif return bRet; } // Closes all connections. void TelnetServer::Close(void) { if(m_ListenSocket != INVALID_SOCKET) { // Close the Socket. m_ListenMutex.Lock(); #if !defined(_XBOX) closesocket(m_ListenSocket); #endif m_ListenSocket = INVALID_SOCKET; m_ListenMutex.Unlock(); // Wait for the Listen Thread to stop. WaitForSingleObject(m_ListenThread, INFINITE); m_ListenThread = 0; } } // Sends text across the telnet connection. // returns false on failure. bool TelnetServer::SendText(unsigned int uiClient, const char *pcLine, ...) { bool bRet = false; TelnetServer_ClientMap::iterator iter; char vcBuffer[8192]; _vsnprintf(vcBuffer, 8191, pcLine, (va_list)(&pcLine+1)); unsigned int uiLen = (unsigned int)strlen(vcBuffer); if(!uiClient) { for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++) { TelnetServer_Client *pClient = (*iter).second; pClient->m_Mutex.Lock(); #if !defined(_XBOX) send(pClient->m_Socket, vcBuffer, uiLen, 0); #endif pClient->m_Mutex.Unlock(); bRet = true; } } else { iter = m_Clients.find(uiClient); if(iter != m_Clients.end()) { TelnetServer_Client *pClient = (*iter).second; pClient->m_Mutex.Lock(); #if !defined(_XBOX) send(pClient->m_Socket, vcBuffer, uiLen, 0); #endif pClient->m_Mutex.Unlock(); bRet = true; } } return bRet; } // Sends a binary block of data across the telnet session. // returns false on failure. bool TelnetServer::SendBlock(const TelnetBlock &block) { unsigned int uiClient = block.m_uiClient; bool bRet = false; char vcFooter[] = "\r\n"; char vcHeader[256]; sprintf(vcHeader, "\r\n", block.m_pcName); TelnetServer_ClientMap::iterator iter; if(!uiClient) { for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++) { TelnetServer_Client *pClient = (*iter).second; pClient->m_Mutex.Lock(); #if !defined(_XBOX) send(pClient->m_Socket, vcHeader, (int)strlen(vcHeader), 0); send(pClient->m_Socket, (char*)block.m_pData, block.m_uiDataSize, 0); send(pClient->m_Socket, vcFooter, (int)strlen(vcFooter), 0); #endif pClient->m_Mutex.Unlock(); bRet = true; } } else { iter = m_Clients.find(uiClient); if(iter != m_Clients.end()) { TelnetServer_Client *pClient = (*iter).second; pClient->m_Mutex.Lock(); #if !defined(_XBOX) send(pClient->m_Socket, vcHeader, (int)strlen(vcHeader), 0); // Send in small chunks... const char *pData = (const char*)block.m_pData; unsigned int uiDataSent = 0; while(uiDataSent < block.m_uiDataSize) { unsigned int uiSendSize = block.m_uiDataSize - uiDataSent; uiSendSize = uiSendSize > 1024 ? 1024 : uiSendSize; int iSent = send(pClient->m_Socket, &pData[uiDataSent], uiSendSize, 0); uiDataSent += (unsigned int)iSent; } send(pClient->m_Socket, vcFooter, (int)strlen(vcFooter), 0); #endif pClient->m_Mutex.Unlock(); bRet = true; } } return bRet; } #if defined(__APPLE__) || defined(__CELLOS_LV2__) || defined(LINUX) #define stricmp(a, b) strcasecmp((a), (b)) #endif /*******************************************************************/ /******************** InParser.h ********************************/ /*******************************************************************/ class InPlaceParserInterface { public: virtual int ParseLine(int lineno,int argc,const char **argv) =0; // return TRUE to continue parsing, return FALSE to abort parsing process }; enum SeparatorType { ST_DATA, // is data ST_HARD, // is a hard separator ST_SOFT, // is a soft separator ST_EOS // is a comment symbol, and everything past this character should be ignored }; class InPlaceParser { public: InPlaceParser(void) { Init(); } InPlaceParser(char *data,int len) { Init(); SetSourceData(data,len); } InPlaceParser(const char *fname) { Init(); SetFile(fname); } ~InPlaceParser(void); void Init(void) { mQuoteChar = 34; mData = 0; mLen = 0; mMyAlloc = false; for (int i=0; i<256; i++) { mHard[i] = ST_DATA; mHardString[i*2] = (char)i; mHardString[i*2+1] = 0; } mHard[0] = ST_EOS; mHard[32] = ST_SOFT; mHard[9] = ST_SOFT; mHard[13] = ST_SOFT; mHard[10] = ST_SOFT; } void SetFile(const char *fname); // use this file as source data to parse. void SetSourceData(char *data,int len) { mData = data; mLen = len; mMyAlloc = false; }; int Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason int ProcessLine(int lineno,char *line,InPlaceParserInterface *callback); const char ** GetArglist(char *source,int &count); // convert source string into an arg list, this is a destructive parse. void SetHardSeparator(char c) // add a hard separator { mHard[c] = ST_HARD; } void SetHard(char c) // add a hard separator { mHard[c] = ST_HARD; } void SetCommentSymbol(char c) // comment character, treated as 'end of string' { mHard[c] = ST_EOS; } void ClearHardSeparator(char c) { mHard[c] = ST_DATA; } void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character. bool EOS(char c) { if ( mHard[c] == ST_EOS ) { return true; } return false; } void SetQuoteChar(char c) { mQuoteChar = c; } private: inline char * AddHard(int &argc,const char **argv,char *foo); inline bool IsHard(char c); inline char * SkipSpaces(char *foo); inline bool IsWhiteSpace(char c); inline bool IsNonSeparator(char c); // non seperator,neither hard nor soft bool mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it. char *mData; // ascii data to parse. int mLen; // length of data SeparatorType mHard[256]; char mHardString[256*2]; char mQuoteChar; }; /*******************************************************************/ /******************** InParser.cpp ********************************/ /*******************************************************************/ void InPlaceParser::SetFile(const char *fname) { if ( mMyAlloc ) { ::free(mData); } mData = 0; mLen = 0; mMyAlloc = false; FILE *fph = fopen(fname,"rb"); if ( fph ) { fseek(fph,0L,SEEK_END); mLen = ftell(fph); fseek(fph,0L,SEEK_SET); if ( mLen ) { mData = (char *) ::malloc(sizeof(char)*(mLen+1)); int ok = (int)fread(mData, mLen, 1, fph); if ( !ok ) { ::free(mData); mData = 0; } else { mData[mLen] = 0; // zero byte terminate end of file marker. mMyAlloc = true; } } fclose(fph); } } InPlaceParser::~InPlaceParser(void) { if ( mMyAlloc ) { ::free(mData); } } #define MAXARGS 512 bool InPlaceParser::IsHard(char c) { return mHard[c] == ST_HARD; } char * InPlaceParser::AddHard(int &argc,const char **argv,char *foo) { while ( IsHard(*foo) ) { const char *hard = &mHardString[*foo*2]; if ( argc < MAXARGS ) { argv[argc++] = hard; } foo++; } return foo; } bool InPlaceParser::IsWhiteSpace(char c) { return mHard[c] == ST_SOFT; } char * InPlaceParser::SkipSpaces(char *foo) { while ( !EOS(*foo) && IsWhiteSpace(*foo) ) foo++; return foo; } bool InPlaceParser::IsNonSeparator(char c) { if ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 ) return true; return false; } int InPlaceParser::ProcessLine(int lineno,char *line,InPlaceParserInterface *callback) { int ret = 0; const char *argv[MAXARGS]; int argc = 0; char *foo = line; while ( !EOS(*foo) && argc < MAXARGS ) { foo = SkipSpaces(foo); // skip any leading spaces if ( EOS(*foo) ) break; if ( *foo == mQuoteChar ) // if it is an open quote { foo++; if ( argc < MAXARGS ) { argv[argc++] = foo; } while ( !EOS(*foo) && *foo != mQuoteChar ) foo++; if ( !EOS(*foo) ) { *foo = 0; // replace close quote with zero byte EOS foo++; } } else { foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces if ( IsNonSeparator(*foo) ) // add non-hard argument. { bool quote = false; if ( *foo == mQuoteChar ) { foo++; quote = true; } if ( argc < MAXARGS ) { argv[argc++] = foo; } if ( quote ) { while (*foo && *foo != mQuoteChar ) foo++; if ( *foo ) *foo = 32; } // continue..until we hit an eos .. while ( !EOS(*foo) ) // until we hit EOS { if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit { *foo = 0; foo++; break; } else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument { const char *hard = &mHardString[*foo*2]; *foo = 0; if ( argc < MAXARGS ) { argv[argc++] = hard; } foo++; break; } foo++; } // end of while loop... } } } if ( argc ) { ret = callback->ParseLine(lineno, argc, argv ); } return ret; } int InPlaceParser::Parse(InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason { assert( callback ); if ( !mData ) return 0; int ret = 0; int lineno = 0; char *foo = mData; char *begin = foo; while ( *foo ) { if ( *foo == 10 || *foo == 13 ) { lineno++; *foo = 0; if ( *begin ) // if there is any data to parse at all... { int v = ProcessLine(lineno,begin,callback); if ( v ) ret = v; } foo++; if ( *foo == 10 ) foo++; // skip line feed, if it is in the carraige-return line-feed format... begin = foo; } else { foo++; } } lineno++; // lasst line. int v = ProcessLine(lineno,begin,callback); if ( v ) ret = v; return ret; } void InPlaceParser::DefaultSymbols(void) { SetHardSeparator(','); SetHardSeparator('('); SetHardSeparator(')'); SetHardSeparator('='); SetHardSeparator('['); SetHardSeparator(']'); SetHardSeparator('{'); SetHardSeparator('}'); SetCommentSymbol('#'); } const char ** InPlaceParser::GetArglist(char *line,int &count) // convert source string into an arg list, this is a destructive parse. { const char **ret = 0; static const char *argv[MAXARGS]; int argc = 0; char *foo = line; while ( !EOS(*foo) && argc < MAXARGS ) { foo = SkipSpaces(foo); // skip any leading spaces if ( EOS(*foo) ) break; if ( *foo == mQuoteChar ) // if it is an open quote { foo++; if ( argc < MAXARGS ) { argv[argc++] = foo; } while ( !EOS(*foo) && *foo != mQuoteChar ) foo++; if ( !EOS(*foo) ) { *foo = 0; // replace close quote with zero byte EOS foo++; } } else { foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces if ( IsNonSeparator(*foo) ) // add non-hard argument. { bool quote = false; if ( *foo == mQuoteChar ) { foo++; quote = true; } if ( argc < MAXARGS ) { argv[argc++] = foo; } if ( quote ) { while (*foo && *foo != mQuoteChar ) foo++; if ( *foo ) *foo = 32; } // continue..until we hit an eos .. while ( !EOS(*foo) ) // until we hit EOS { if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit { *foo = 0; foo++; break; } else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument { const char *hard = &mHardString[*foo*2]; *foo = 0; if ( argc < MAXARGS ) { argv[argc++] = hard; } foo++; break; } foo++; } // end of while loop... } } } count = argc; if ( argc ) { ret = argv; } return ret; } #define MAXPARSEBUFFER 2048 class MyTelnet : public Telnet { public: MyTelnet(const char *address,unsigned int port) { mParser.DefaultSymbols(); mClient = 0; mInterface = 0; mServer = new TelnetServer; mIsServer = mServer->Listen(port); mHaveConnection = true; if ( mIsServer ) { mInterface = static_cast< TelnetInterface *>(mServer); } else { delete mServer; mServer = 0; mClient = new TelnetClient; mHaveConnection = mClient->Connect(address,port); if ( !mHaveConnection ) { delete mClient; mClient = 0; } else { mInterface = static_cast< TelnetInterface *>(mClient); } } } virtual bool isServer(void) // returns true if we are a server or a client. First one created on a machine is a server, additional copies are clients. { return mIsServer; } virtual bool haveConnection(void) { return mHaveConnection; } virtual bool sendMessage(unsigned int client,const char *fmt,...) { bool ret = false; if ( mInterface ) { char wbuff[MAXPARSEBUFFER]; wbuff[MAXPARSEBUFFER-1] = 0; _vsnprintf(wbuff,MAXPARSEBUFFER-1, fmt, (char *)(&fmt+1)); ret = mInterface->SendText(client,"%s",wbuff); } return ret; } virtual const char * receiveMessage(unsigned int &client) { const char *ret = 0; client = 0; if ( mInterface ) { ret = mInterface->GetLine(client); } return ret; } virtual const char ** getArgs(const char *input,int &argc) // parse string into a series of arguments. { strncpy(mParseBuffer,input,MAXPARSEBUFFER); mParseBuffer[MAXPARSEBUFFER-1] = 0; return mParser.GetArglist(mParseBuffer,argc); } virtual ~MyTelnet(void) { delete mClient; delete mServer; } private: bool mIsServer; bool mHaveConnection; TelnetInterface *mInterface; TelnetClient *mClient; TelnetServer *mServer; char mParseBuffer[MAXPARSEBUFFER]; InPlaceParser mParser; }; Telnet * createTelnet(const char *address,unsigned int port) { MyTelnet *m = new MyTelnet(address,port); return static_cast< Telnet *>(m); } void releaseTelnet(Telnet *t) { MyTelnet *m = static_cast< MyTelnet *>(t); delete m; } }; // end of namespace