telnetConsole.cpp
Engine/source/console/telnetConsole.cpp
Public Variables
Public Functions
DefineEngineFunction(telnetSetParameters , void , (int port, const char *consolePass, const char *listenPass, bool remoteEcho) , (false) , "@brief Initializes and open the telnet <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param port Port <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> listen on <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> console connections (0 will shut down listening).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param consolePass Password <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> read/write access <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param listenPass Password <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> read access <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param remoteEcho Enable echoing back <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client, off by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">default.\n\n</a>" " @ingroup Debugging" )
telnetCallback(U32 level, const char * consoleLine)
Detailed Description
Public Variables
MODULE_END
MODULE_INIT
MODULE_SHUTDOWN
TelnetConsole * TelConsole
Public Functions
DefineEngineFunction(telnetSetParameters , void , (int port, const char *consolePass, const char *listenPass, bool remoteEcho) , (false) , "@brief Initializes and open the telnet <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param port Port <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> listen on <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> console connections (0 will shut down listening).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param consolePass Password <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> read/write access <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param listenPass Password <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> read access <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param remoteEcho Enable echoing back <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client, off by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">default.\n\n</a>" " @ingroup Debugging" )
telnetCallback(U32 level, const char * consoleLine)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25 26#include "console/simBase.h" 27#include "console/engineAPI.h" 28#include "console/telnetConsole.h" 29 30#include "core/strings/stringFunctions.h" 31#include "core/util/journal/process.h" 32#include "core/module.h" 33 34 35MODULE_BEGIN( TelnetConsole ) 36 37 MODULE_INIT 38 { 39 TelnetConsole::create(); 40 } 41 42 MODULE_SHUTDOWN 43 { 44 TelnetConsole::destroy(); 45 } 46 47MODULE_END; 48 49 50TelnetConsole *TelConsole = NULL; 51 52void TelnetConsole::create() 53{ 54 TelConsole = new TelnetConsole; 55 Process::notify(TelConsole, &TelnetConsole::process, PROCESS_FIRST_ORDER); 56} 57 58void TelnetConsole::destroy() 59{ 60 Process::remove(TelConsole, &TelnetConsole::process); 61 delete TelConsole; 62 TelConsole = NULL; 63} 64 65DefineEngineFunction( telnetSetParameters, void, ( int port, const char* consolePass, const char* listenPass, bool remoteEcho ), ( false ), 66 "@brief Initializes and open the telnet console.\n\n" 67 "@param port Port to listen on for console connections (0 will shut down listening).\n" 68 "@param consolePass Password for read/write access to console.\n" 69 "@param listenPass Password for read access to console.\n" 70 "@param remoteEcho [optional] Enable echoing back to the client, off by default.\n\n" 71 "@ingroup Debugging") 72{ 73 if (TelConsole) 74 TelConsole->setTelnetParameters(port, consolePass, listenPass, remoteEcho); 75} 76 77static void telnetCallback(U32 level, const char *consoleLine) 78{ 79 TORQUE_UNUSED(level); 80 if (TelConsole) 81 TelConsole->processConsoleLine(consoleLine); 82} 83 84TelnetConsole::TelnetConsole() 85{ 86 Con::addConsumer(telnetCallback); 87 88 mAcceptSocket = NetSocket::INVALID; 89 mAcceptPort = -1; 90 mClientList = NULL; 91 mRemoteEchoEnabled = false; 92 93 dStrncpy(mTelnetPassword, "", PasswordMaxLength); 94 dStrncpy(mListenPassword, "", PasswordMaxLength); 95} 96 97TelnetConsole::~TelnetConsole() 98{ 99 Con::removeConsumer(telnetCallback); 100 if(mAcceptSocket != NetSocket::INVALID) 101 Net::closeSocket(mAcceptSocket); 102 TelnetClient *walk = mClientList, *temp; 103 while(walk) 104 { 105 temp = walk->nextClient; 106 if(walk->socket != NetSocket::INVALID) 107 Net::closeSocket(walk->socket); 108 delete walk; 109 walk = temp; 110 } 111} 112 113void TelnetConsole::setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho) 114{ 115 if(port == mAcceptPort) 116 return; 117 118 mRemoteEchoEnabled = remoteEcho; 119 120 if(mAcceptSocket != NetSocket::INVALID) 121 { 122 Net::closeSocket(mAcceptSocket); 123 mAcceptSocket = NetSocket::INVALID; 124 } 125 mAcceptPort = port; 126 if(mAcceptPort != -1 && mAcceptPort != 0) 127 { 128 NetAddress address; 129 Net::getIdealListenAddress(&address); 130 address.port = mAcceptPort; 131 132 mAcceptSocket = Net::openSocket(); 133 Net::bindAddress(address, mAcceptSocket); 134 Net::listen(mAcceptSocket, 4); 135 136 Net::setBlocking(mAcceptSocket, false); 137 } 138 dStrncpy(mTelnetPassword, telnetPassword, PasswordMaxLength); 139 dStrncpy(mListenPassword, listenPassword, PasswordMaxLength); 140} 141 142void TelnetConsole::processConsoleLine(const char *consoleLine) 143{ 144 if (mClientList==<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>) return; // just escape early. don't even do another step... 145 146 // ok, spew this line out to all our subscribers... 147 S32 len = dStrlen(consoleLine)+1; 148 for(TelnetClient *walk = mClientList; walk; walk = walk->nextClient) 149 { 150 if(walk->state == FullAccessConnected || walk->state == ReadOnlyConnected) 151 { 152 Net::send(walk->socket, (const unsigned char*)consoleLine, len); 153 Net::send(walk->socket, (const unsigned char*)"\r\n", 2); 154 } 155 } 156} 157 158void TelnetConsole::process() 159{ 160 NetAddress address; 161 162 if(mAcceptSocket != NetSocket::INVALID) 163 { 164 // ok, see if we have any new connections: 165 NetSocket newConnection; 166 newConnection = Net::accept(mAcceptSocket, &address); 167 168 if(newConnection != NetSocket::INVALID) 169 { 170 char buffer[256]; 171 Net::addressToString(&address, buffer); 172 Con::printf("Telnet connection from %s", buffer); 173 174 TelnetClient *cl = new TelnetClient; 175 cl->socket = newConnection; 176 cl->curPos = 0; 177#if defined(TORQUE_SHIPPING) && defined(TORQUE_DISABLE_TELNET_CONSOLE_PASSWORD) 178 // disable the password in a ship build? WTF. lets make an error: 179 PleaseMakeSureYouKnowWhatYouAreDoingAndCommentOutThisLineIfSo. 180#elif !defined(TORQUE_SHIPPING) && defined(TORQUE_DISABLE_TELNET_CONSOLE_PASSWORD) 181 cl->state = FullAccessConnected; 182#else 183 cl->state = PasswordTryOne; 184#endif 185 186 Net::setBlocking(newConnection, false); 187 188 const char *prompt = Con::getVariable("Con::Prompt"); 189 char connectMessage[1024]; 190 dSprintf(connectMessage, sizeof(connectMessage), 191 "Torque Telnet Remote Console\r\n\r\n%s", 192 cl->state == FullAccessConnected ? prompt : "Enter Password:"); 193 194 Net::send(cl->socket, (const unsigned char*)connectMessage, dStrlen(connectMessage)+1); 195 cl->nextClient = mClientList; 196 mClientList = cl; 197 } 198 } 199 200 char recvBuf[256]; 201 char reply[1024]; 202 203 // see if we have any input to process... 204 205 for(TelnetClient *client = mClientList; client; client = client->nextClient) 206 { 207 S32 numBytes; 208 Net::Error err = Net::recv(client->socket, (unsigned char*)recvBuf, sizeof(recvBuf), &numBytes); 209 210 if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0) 211 { 212 Net::closeSocket(client->socket); 213 client->socket = NetSocket::INVALID; 214 continue; 215 } 216 217 S32 replyPos = 0; 218 for(S32 i = 0; i < numBytes;i++) 219 { 220 if(recvBuf[i] == '\r') 221 continue; 222 // execute the current command 223 224 if(recvBuf[i] == '\n') 225 { 226 reply[replyPos++] = '\r'; 227 reply[replyPos++] = '\n'; 228 229 client->curLine[client->curPos] = 0; 230 client->curPos = 0; 231 232 if(client->state == FullAccessConnected) 233 { 234 Net::send(client->socket, (const unsigned char*)reply, replyPos); 235 replyPos = 0; 236 237 // Notify console of line to execute. 238 RawData rd; 239 rd.size = dStrlen(client->curLine) + 1; 240 rd.data = ( S8* ) client->curLine; 241 Con::smConsoleInput.trigger(rd); 242 243 // note - send prompt next 244 const char *prompt = Con::getVariable("Con::Prompt"); 245 Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt)); 246 } 247 else if(client->state == ReadOnlyConnected) 248 { 249 Net::send(client->socket, (const unsigned char*)reply, replyPos); 250 replyPos = 0; 251 } 252 else 253 { 254 client->state++; 255 if(!dStrncmp(client->curLine, mTelnetPassword, PasswordMaxLength)) 256 { 257 Net::send(client->socket, (const unsigned char*)reply, replyPos); 258 replyPos = 0; 259 260 // send prompt 261 const char *prompt = Con::getVariable("Con::Prompt"); 262 Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt)); 263 client->state = FullAccessConnected; 264 } 265 else if(!dStrncmp(client->curLine, mListenPassword, PasswordMaxLength)) 266 { 267 Net::send(client->socket, (const unsigned char*)reply, replyPos); 268 replyPos = 0; 269 270 // send prompt 271 const char *listenConnected = "Connected.\r\n"; 272 Net::send(client->socket, (const unsigned char*)listenConnected, dStrlen(listenConnected)); 273 client->state = ReadOnlyConnected; 274 } 275 else 276 { 277 const char *sendStr; 278 if(client->state == DisconnectThisDude) 279 sendStr = "Too many tries... cya."; 280 else 281 sendStr = "Nope... try agian.\r\nEnter Password:"; 282 Net::send(client->socket, (const unsigned char*)sendStr, dStrlen(sendStr)); 283 if(client->state == DisconnectThisDude) 284 { 285 Net::closeSocket(client->socket); 286 client->socket = NetSocket::INVALID; 287 } 288 } 289 } 290 } 291 else if(recvBuf[i] == '\b') 292 { 293 // pull the old backspace manuever... 294 if(client->curPos > 0) 295 { 296 client->curPos--; 297 if(client->state == FullAccessConnected) 298 { 299 reply[replyPos++] = '\b'; 300 reply[replyPos++] = ' '; 301 reply[replyPos++] = '\b'; 302 } 303 } 304 } 305 else if(client->curPos < Con::MaxLineLength-1) 306 { 307 client->curLine[client->curPos++] = recvBuf[i]; 308 // don't echo password chars... 309 if(client->state == FullAccessConnected) 310 reply[replyPos++] = recvBuf[i]; 311 } 312 } 313 314 // Echo the character back to the user, unless the remote echo 315 // is disabled (by default) 316 if(replyPos && mRemoteEchoEnabled) 317 Net::send(client->socket, (const unsigned char*)reply, replyPos); 318 } 319 320 TelnetClient ** walk = &mClientList; 321 TelnetClient *cl; 322 while((cl = *walk) != NULL) 323 { 324 if(cl->socket == NetSocket::INVALID) 325 { 326 *walk = cl->nextClient; 327 delete cl; 328 } 329 else 330 walk = &cl->nextClient; 331 } 332} 333