telnetConsole.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "platform/event.h"
  24. #include "network/telnetConsole.h"
  25. #include "game/gameInterface.h"
  26. TelnetConsole *TelConsole = NULL;
  27. void TelnetConsole::create()
  28. {
  29. TelConsole = new TelnetConsole;
  30. }
  31. void TelnetConsole::destroy()
  32. {
  33. delete TelConsole;
  34. TelConsole = NULL;
  35. }
  36. ConsoleFunction( telnetSetParameters, void, 4, 5, "(int port, string consolePass, string listenPass)"
  37. "Initialize and open the telnet console.\n\n"
  38. "@param port Port to listen on for console connections (0 will shut down listening).\n"
  39. "@param consolePass Password for read/write access to console.\n"
  40. "@param listenPass Password for read access to console.\n"
  41. "@param remoteEcho [optional] Enable echoing back to the client, off by default.\n"
  42. "@return No return value")
  43. {
  44. if (TelConsole)
  45. {
  46. bool remoteEcho = false;
  47. if( argc == 5 )
  48. remoteEcho = dAtob( argv[4] );
  49. TelConsole->setTelnetParameters(dAtoi(argv[1]), argv[2], argv[3], remoteEcho);
  50. }
  51. }
  52. static void telnetCallback(ConsoleLogEntry::Level level, const char *consoleLine)
  53. {
  54. if (TelConsole)
  55. TelConsole->processConsoleLine(consoleLine);
  56. }
  57. TelnetConsole::TelnetConsole()
  58. {
  59. Con::addConsumer(telnetCallback);
  60. mAcceptSocket = InvalidSocket;
  61. mAcceptPort = -1;
  62. mClientList = NULL;
  63. mRemoteEchoEnabled = false;
  64. }
  65. TelnetConsole::~TelnetConsole()
  66. {
  67. Con::removeConsumer(telnetCallback);
  68. if(mAcceptSocket != InvalidSocket)
  69. Net::closeSocket(mAcceptSocket);
  70. TelnetClient *walk = mClientList, *temp;
  71. while(walk)
  72. {
  73. temp = walk->nextClient;
  74. if(walk->socket != InvalidSocket)
  75. Net::closeSocket(walk->socket);
  76. delete walk;
  77. walk = temp;
  78. }
  79. }
  80. void TelnetConsole::setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho)
  81. {
  82. if(port == mAcceptPort)
  83. return;
  84. mRemoteEchoEnabled = remoteEcho;
  85. if(mAcceptSocket != InvalidSocket)
  86. {
  87. Net::closeSocket(mAcceptSocket);
  88. mAcceptSocket = InvalidSocket;
  89. }
  90. mAcceptPort = port;
  91. if(mAcceptPort != -1 && mAcceptPort != 0)
  92. {
  93. mAcceptSocket = Net::openSocket();
  94. Net::bind(mAcceptSocket, mAcceptPort);
  95. Net::listen(mAcceptSocket, 4);
  96. Net::setBlocking(mAcceptSocket, false);
  97. }
  98. dStrncpy(mTelnetPassword, telnetPassword, PasswordMaxLength);
  99. dStrncpy(mListenPassword, listenPassword, PasswordMaxLength);
  100. }
  101. void TelnetConsole::processConsoleLine(const char *consoleLine)
  102. {
  103. if (mClientList==NULL) return; // just escape early. don't even do another step...
  104. // ok, spew this line out to all our subscribers...
  105. S32 len = dStrlen(consoleLine)+1;
  106. for(TelnetClient *walk = mClientList; walk; walk = walk->nextClient)
  107. {
  108. if(walk->state == FullAccessConnected || walk->state == ReadOnlyConnected)
  109. {
  110. if ( walk->socket != InvalidSocket )
  111. {
  112. Net::send(walk->socket, (const unsigned char*)consoleLine, len);
  113. Net::send(walk->socket, (const unsigned char*)"\r\n", 2);
  114. }
  115. }
  116. }
  117. }
  118. void TelnetConsole::process()
  119. {
  120. NetAddress address;
  121. if(mAcceptSocket != InvalidSocket)
  122. {
  123. // ok, see if we have any new connections:
  124. NetSocket newConnection;
  125. newConnection = Net::accept(mAcceptSocket, &address);
  126. if(newConnection != InvalidSocket)
  127. {
  128. Con::printf ("Telnet connection from %i.%i.%i.%i",
  129. address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]);
  130. TelnetClient *cl = new TelnetClient;
  131. cl->socket = newConnection;
  132. cl->curPos = 0;
  133. cl->state = PasswordTryOne;
  134. Net::setBlocking(newConnection, false);
  135. const char *connectMessage = "Torque Telnet Remote Console\r\n\r\nEnter Password:";
  136. if ( cl->socket != InvalidSocket )
  137. Net::send(cl->socket, (const unsigned char*)connectMessage, dStrlen(connectMessage)+1);
  138. cl->nextClient = mClientList;
  139. mClientList = cl;
  140. }
  141. }
  142. char recvBuf[256];
  143. char reply[1024];
  144. // see if we have any input to process...
  145. for(TelnetClient *client = mClientList; client; client = client->nextClient)
  146. {
  147. S32 numBytes;
  148. Net::Error err = Net::NotASocket;
  149. if ( client->socket != InvalidSocket )
  150. Net::recv(client->socket, (unsigned char*)recvBuf, sizeof(recvBuf), &numBytes);
  151. if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0)
  152. {
  153. if ( client->socket != InvalidSocket )
  154. {
  155. Net::closeSocket(client->socket);
  156. client->socket = InvalidSocket;
  157. }
  158. continue;
  159. }
  160. S32 replyPos = 0;
  161. for(S32 i = 0; i < numBytes;i++)
  162. {
  163. if(recvBuf[i] == '\r')
  164. continue;
  165. // execute the current command
  166. if(recvBuf[i] == '\n')
  167. {
  168. reply[replyPos++] = '\r';
  169. reply[replyPos++] = '\n';
  170. client->curLine[client->curPos] = 0;
  171. client->curPos = 0;
  172. if(client->state == FullAccessConnected)
  173. {
  174. if ( client->socket != InvalidSocket )
  175. Net::send(client->socket, (const unsigned char*)reply, replyPos);
  176. replyPos = 0;
  177. dStrcpy(mPostEvent.data, client->curLine);
  178. mPostEvent.size = ConsoleEventHeaderSize + dStrlen(client->curLine) + 1;
  179. Game->postEvent(mPostEvent);
  180. // note - send prompt next
  181. const char *prompt = Con::getVariable("Con::Prompt");
  182. if ( client->socket != InvalidSocket )
  183. Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt));
  184. }
  185. else if(client->state == ReadOnlyConnected)
  186. {
  187. if ( client->socket != InvalidSocket )
  188. Net::send(client->socket, (const unsigned char*)reply, replyPos);
  189. replyPos = 0;
  190. }
  191. else
  192. {
  193. client->state++;
  194. if(!dStrncmp(client->curLine, mTelnetPassword, PasswordMaxLength))
  195. {
  196. if ( client->socket != InvalidSocket )
  197. Net::send(client->socket, (const unsigned char*)reply, replyPos);
  198. replyPos = 0;
  199. // send prompt
  200. const char *prompt = Con::getVariable("Con::Prompt");
  201. if ( client->socket != InvalidSocket )
  202. Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt));
  203. client->state = FullAccessConnected;
  204. }
  205. else if(!dStrncmp(client->curLine, mListenPassword, PasswordMaxLength))
  206. {
  207. if ( client->socket != InvalidSocket )
  208. Net::send(client->socket, (const unsigned char*)reply, replyPos);
  209. replyPos = 0;
  210. // send prompt
  211. const char *listenConnected = "Connected.\r\n";
  212. if ( client->socket != InvalidSocket )
  213. Net::send(client->socket, (const unsigned char*)listenConnected, dStrlen(listenConnected));
  214. client->state = ReadOnlyConnected;
  215. }
  216. else
  217. {
  218. const char *sendStr;
  219. if(client->state == DisconnectThisDude)
  220. sendStr = "Too many tries... cya.";
  221. else
  222. sendStr = "Nope... try agian.\r\nEnter Password:";
  223. if ( client->socket != InvalidSocket )
  224. Net::send(client->socket, (const unsigned char*)sendStr, dStrlen(sendStr));
  225. if(client->state == DisconnectThisDude)
  226. {
  227. if ( client->socket != InvalidSocket )
  228. {
  229. Net::closeSocket(client->socket);
  230. client->socket = InvalidSocket;
  231. }
  232. }
  233. }
  234. }
  235. }
  236. else if(recvBuf[i] == '\b')
  237. {
  238. // pull the old backspace manuever...
  239. if(client->curPos > 0)
  240. {
  241. client->curPos--;
  242. if(client->state == FullAccessConnected)
  243. {
  244. reply[replyPos++] = '\b';
  245. reply[replyPos++] = ' ';
  246. reply[replyPos++] = '\b';
  247. }
  248. }
  249. }
  250. else if(client->curPos < Con::MaxLineLength-1)
  251. {
  252. client->curLine[client->curPos++] = recvBuf[i];
  253. // don't echo password chars...
  254. if(client->state == FullAccessConnected)
  255. reply[replyPos++] = recvBuf[i];
  256. }
  257. }
  258. // Echo the character back to the user, unless the remote echo
  259. // is disabled (by default)
  260. if(replyPos && mRemoteEchoEnabled)
  261. {
  262. if ( client->socket != InvalidSocket )
  263. Net::send(client->socket, (const unsigned char*)reply, replyPos);
  264. }
  265. }
  266. TelnetClient ** walk = &mClientList;
  267. TelnetClient *cl;
  268. while((cl = *walk) != NULL)
  269. {
  270. if(cl->socket == InvalidSocket)
  271. {
  272. *walk = cl->nextClient;
  273. delete cl;
  274. }
  275. else
  276. walk = &cl->nextClient;
  277. }
  278. }