1 /////////////////////////////////////////////////////////////////////////////// 2 //Telnet Win32 : an ANSI telnet client. 3 //Copyright (C) 1998 Paul Brannan 4 //Copyright (C) 1998 I.Ioannou 5 //Copyright (C) 1997 Brad Johnson 6 // 7 //This program is free software; you can redistribute it and/or 8 //modify it under the terms of the GNU General Public License 9 //as published by the Free Software Foundation; either version 2 10 //of the License, or (at your option) any later version. 11 // 12 //This program is distributed in the hope that it will be useful, 13 //but WITHOUT ANY WARRANTY; without even the implied warranty of 14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 //GNU General Public License for more details. 16 // 17 //You should have received a copy of the GNU General Public License 18 //along with this program; if not, write to the Free Software 19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 // 21 //I.Ioannou 22 //roryt@hol.gr 23 // 24 /////////////////////////////////////////////////////////////////////////// 25 26 /////////////////////////////////////////////////////////////////////////////// 27 // 28 // Module: ttelhndl.cpp 29 // 30 // Contents: Telnet Handler 31 // 32 // Product: telnet 33 // 34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu> 35 // June 15, 1998 pbranna@clemson.edu (Paul Brannan) 36 // 37 // This is code originally from tnnet.cpp and ansiprsr.cpp 38 // 39 /////////////////////////////////////////////////////////////////////////////// 40 41 #include "precomp.h" 42 43 #include "telnet.h" 44 45 int naws_string(char *buf, int width, int height); 46 47 // This helps make the code more readable (Paul Brannan 1/1/99) 48 #ifdef DEBUG_TELOPT 49 #define TELOPT_PRINTD(x) printit(x); 50 #define TELOPT_PRINTD2(x,n) { \ 51 static char buf[20]; \ 52 printit(s); \ 53 printit(" "); \ 54 itoa(d, buf, 10); \ 55 printit(buf); \ 56 printit("\n"); \ 57 } 58 #else 59 #define TELOPT_PRINTD(x) ; 60 #define TELOPT_PRINTD2(x,n) ; 61 #endif 62 63 // A new print function for debugging (Paul Brannan 5/15/98) 64 #ifdef DEBUG_TELOPT 65 void TTelnetHandler::print_telopt(const char *s, int d) { 66 static char buf[20]; 67 printit(s); 68 printit(" "); 69 itoa(d, buf, 10); 70 printit(buf); 71 printit("\n"); 72 } 73 #endif 74 75 TTelnetHandler::TTelnetHandler(TNetwork &RefNetwork, TConsole &RefConsole, 76 TParser &RefParser): 77 Network(RefNetwork), Console(RefConsole), Parser(RefParser) { 78 init(); 79 80 // Paul Brannan 9/13/98 81 dwBuffer = ini.get_buffer_size(); 82 szBuffer = new char [dwBuffer]; 83 Network.SetNawsFunc(NULL); 84 } 85 86 void TTelnetHandler::init() { 87 iTermSet = 0; 88 bInBinaryRx = 0; 89 bInBinaryTx = 0; 90 bInEchoTx = 0; 91 bInEchoRx = 0; 92 Network.set_local_echo(1); 93 } 94 95 TTelnetHandler::~TTelnetHandler() { 96 delete[] szBuffer; 97 } 98 99 int TTelnetHandler::escapeIAC(char *buf, int length){ 100 // The size of buffer must be greater than 2 * length to ensure no memory 101 // out of bounds errors. The 0xff is escaped into 0xff 0xff. 102 char * temp; 103 temp = new char [length * 2]; 104 int current=0; 105 for (int x=0; x < length; x++){ 106 if (buf[x] == (signed char)IAC) 107 temp[current++]=(char)IAC; 108 temp[current++]=buf[x]; 109 } 110 memcpy( buf, temp, current); 111 delete [] temp; 112 return current; 113 } 114 115 // This lets us get rid of all the printf's (Paul Brannan 5/15/98) 116 void TTelnetHandler::SendIAC(char c) { 117 static char buf[2] = {IAC}; 118 buf[1] = c; 119 Network.WriteString(buf, 2); 120 } 121 void TTelnetHandler::SendIAC(char c1, char c2) { 122 static char buf[3] = {IAC}; 123 buf[1] = c1; buf[2] = c2; 124 Network.WriteString(buf, 3); 125 } 126 void TTelnetHandler::SendIACParams(char c) { 127 static char buf[2]; 128 buf[0] = c; 129 static int length = escapeIAC(buf, 1); 130 Network.WriteString(buf, length); 131 } 132 void TTelnetHandler::SendIACParams(char c1, char c2) { 133 static char buf[4]; 134 buf[0] = c1; buf[1] = c2; 135 static int length = escapeIAC(buf, 2); 136 Network.WriteString(buf, length); 137 } 138 139 int naws_string(char *b, int width, int height) { 140 int l = 0; 141 unsigned char *buf = (unsigned char *)b; 142 143 union { 144 char szResponse[2]; 145 int n; 146 }; 147 148 buf[l++] = IAC; 149 buf[l++] = SB; 150 buf[l++] = TELOPT_NAWS; 151 152 n = width; 153 buf[l] = szResponse[1]; 154 if(buf[l-1] == IAC) buf[l++] = IAC; 155 buf[l++] = szResponse[0]; 156 if(buf[l-1] == IAC) buf[l++] = IAC; 157 158 n = height; 159 buf[l++] = szResponse[1]; 160 if(buf[l-1] == IAC) buf[l++] = IAC; 161 buf[l++] = szResponse[0]; 162 if(buf[l-1] == IAC) buf[l++] = IAC; 163 164 buf[l++] = IAC; 165 buf[l++] = SE; 166 167 return l; 168 } 169 170 // Ioannou 29 May 1998 : Something strange happens with 171 // Borland compiler at this point when it passes the arguments 172 // to SendIACParams. It always sends 80 lines to the server !!! 173 // There seems to be a bug with optimization (the disassemble shows 174 // that it uses an address plus 0xa than the right one). 175 // This turns them off for this point. 176 #ifdef __BORLANDC__ 177 #pragma -O- 178 #endif 179 180 // Removed old printf code that was commented out to clean this function 181 // up a bit (Paul brannan 6/15/98) 182 char* TTelnetHandler::ParseIAC(char* pszBuffer, char* pszBufferEnd) 183 { 184 // int n,l; 185 // char szResponse[40]; 186 // Ioannou 29 May 1998 : I prefer the union redefinitions 187 // than the typecasting (used with them from Pascal and Cobol :-) ) 188 // FIX ME !!!! Shall we use the winsock routines instead ? 189 190 union { 191 char szResponse[2]; 192 int n; 193 }; 194 195 // Added support for user-defined term name (Paul Brannan 5/13/98) 196 #define LASTTERM 4 197 const char *pszTerms[] = {ini.get_term(), "ANSI","DEC-VT100","DEC-VT52","UNKNOWN"}; 198 if(!iTermSet && (pszTerms[0] == 0 || *pszTerms[0] == 0)) iTermSet++; 199 200 if (pszBuffer + 2 < pszBufferEnd) { 201 switch ((unsigned char)pszBuffer[1]) { 202 203 ///////////////// DO //////////////////// 204 case DO: 205 { 206 switch (pszBuffer[2]){ 207 case TELOPT_BINARY: 208 TELOPT_PRINTD("RCVD DO TELOPT_BINARY\n"); 209 if (!bInBinaryRx){ 210 SendIAC(WILL, TELOPT_BINARY); 211 bInBinaryRx = 1; 212 TELOPT_PRINTD("SENT WILL TELOPT_BINARY\n"); 213 } 214 break; 215 case TELOPT_ECHO: 216 // we shouldn't echo for the server! (Paul Brannan 5/30/98) 217 TELOPT_PRINTD2("RCVD DO TELOPT_ECHO", pszBuffer[2]); 218 SendIAC(WONT, TELOPT_ECHO); 219 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n"); 220 break; 221 case TELOPT_TTYPE: 222 TELOPT_PRINTD("RCVD DO TELOPT_TTYPE\n"); 223 SendIAC(WILL, TELOPT_TTYPE); 224 TELOPT_PRINTD("SENT WILL TELOPT_TTYPE\n"); 225 break; 226 case TELOPT_NAWS: 227 TELOPT_PRINTD("RCVD DO TELOPT_NAWS\n"); 228 SendIAC(WILL, TELOPT_NAWS); 229 SendIAC(SB, TELOPT_NAWS); 230 231 Network.SetNawsFunc(naws_string); 232 233 n = Console.GetWidth(); 234 SendIACParams(szResponse[1],szResponse [0]); 235 236 n = Console.GetHeight(); 237 SendIACParams(szResponse[1],szResponse[0]); 238 239 SendIAC(SE); 240 TELOPT_PRINTD("SENT WILL TELOPT_NAWS\n"); 241 break; 242 case TELOPT_XDISPLOC: 243 TELOPT_PRINTD("RCVD DO TELOPT_XDISPLOC\n"); 244 SendIAC(WILL, TELOPT_XDISPLOC); 245 TELOPT_PRINTD("SENT WILL TELOPT_XDISPLOC\n"); 246 printit("Retrieving IP..."); 247 break; 248 default: 249 TELOPT_PRINTD2("RCVD DO", pszBuffer[2]); 250 SendIAC(WONT, pszBuffer[2]); 251 TELOPT_PRINTD2("SENT WONT", pszBuffer[2]); 252 break; 253 } 254 if (pszBuffer + 2 < pszBufferEnd) 255 pszBuffer += 3; 256 break; 257 } 258 259 ///////////////// WILL //////////////////// 260 case WILL: 261 { 262 switch ((unsigned char)pszBuffer[2]){ 263 case TELOPT_BINARY: 264 TELOPT_PRINTD("RCVD WILL TELOPT_BINARY\n"); 265 if (!bInBinaryTx){ 266 SendIAC(DO, TELOPT_BINARY); 267 bInBinaryTx = 1; 268 TELOPT_PRINTD("SENT DO TELOPT_BINARY\n"); 269 } 270 break; 271 case TELOPT_ECHO: 272 TELOPT_PRINTD2("RCVD WILL TELOPT_ECHO", pszBuffer[2]); 273 if(!bInEchoRx) { 274 SendIAC(DO, TELOPT_ECHO); 275 bInEchoRx = 1; 276 Network.set_local_echo(0); // Paul Brannan 8/25/98 277 if(iWillSGA) Network.set_line_mode(0); 278 TELOPT_PRINTD2("SENT DO TELOPT_ECHO", pszBuffer[2]); 279 if(Network.get_local_echo()) Network.set_line_mode(0); 280 } 281 break; 282 283 // Suppress Go Ahead (Paul Brannan 12/31/98) 284 case TELOPT_SGA: 285 TELOPT_PRINTD("RCVD WILL TELOPT_SGA\n"); 286 if(!iWillSGA) { 287 SendIAC(DO, TELOPT_SGA); 288 if(bInEchoRx) Network.set_line_mode(0); 289 iWillSGA = 1; 290 TELOPT_PRINTD("SENT DO TELOPT_SGA\n"); 291 } 292 break; 293 294 ////added 1/28/97 295 default: 296 TELOPT_PRINTD2("RCVD WILL", pszBuffer[2]); 297 SendIAC(DONT, pszBuffer[2]); 298 TELOPT_PRINTD2("SENT DONT", pszBuffer[2]); 299 break; 300 //// 301 } 302 if (pszBuffer + 2 < pszBufferEnd) 303 pszBuffer += 3; 304 break; 305 } 306 307 ///////////////// WONT //////////////////// 308 case WONT: 309 { 310 switch ((unsigned char)pszBuffer[2]){ 311 case TELOPT_ECHO: 312 TELOPT_PRINTD("RCVD WONT TELOPT_ECHO\n"); 313 if (bInEchoRx){ 314 SendIAC(DONT, TELOPT_ECHO); 315 // bInBinaryRx = 0; 316 bInEchoRx = 0; // Paul Brannan 8/25/98 317 Network.set_local_echo(1); 318 Network.set_line_mode(0); 319 TELOPT_PRINTD("SENT DONT TELOPT_ECHO\n"); 320 } 321 break; 322 323 // Suppress Go Ahead (Paul Brannan 12/31/98) 324 case TELOPT_SGA: 325 TELOPT_PRINTD("RCVD WONT TELOPT_SGA\n"); 326 if(iWillSGA) { 327 SendIAC(DONT, TELOPT_SGA); 328 Network.set_line_mode(0); 329 iWillSGA = 0; 330 TELOPT_PRINTD("SENT DONT TELOPT_SGA\n"); 331 } 332 break; 333 334 default: 335 TELOPT_PRINTD2("RCVD WONT", pszBuffer[2]); 336 break; 337 } 338 if (pszBuffer + 2 < pszBufferEnd) 339 pszBuffer += 3; 340 break; 341 } 342 343 ///////////////// DONT //////////////////// 344 case DONT: 345 { 346 switch ((unsigned char)pszBuffer[2]){ 347 case TELOPT_ECHO: 348 TELOPT_PRINTD("RCVD DONT TELOPT_ECHO\n"); 349 if (bInEchoTx){ 350 SendIAC(WONT, TELOPT_ECHO); 351 bInEchoTx = 0; 352 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n"); 353 } 354 break; 355 case TELOPT_NAWS: 356 TELOPT_PRINTD("RCVD DONT TELOPT_NAWS\n"); 357 SendIAC(WONT, TELOPT_NAWS); 358 Network.SetNawsFunc(naws_string); 359 TELOPT_PRINTD("SENT WONT TELOPT_NAWS\n"); 360 break; 361 default: 362 TELOPT_PRINTD2("RCVD DONT", pszBuffer[2]); 363 break; 364 } 365 if (pszBuffer + 2 < pszBufferEnd) 366 pszBuffer += 3; 367 break; 368 } 369 370 ///////////////// SB //////////////////// 371 case SB: 372 { 373 switch ((unsigned char)pszBuffer[2]){ 374 case TELOPT_TTYPE: 375 if (pszBuffer + 5 < pszBufferEnd) { 376 TELOPT_PRINTD("RCVD SB TELOPT_TTYPE\n"); 377 if (pszBuffer[3] == 1){ 378 TELOPT_PRINTD("SENT SB TT"); 379 TELOPT_PRINTD(pszTerms[iTermSet]); 380 TELOPT_PRINTD("\n"); 381 SendIAC(SB, TELOPT_TTYPE); 382 SendIACParams(0); 383 Network.WriteString(pszTerms[iTermSet], strlen(pszTerms[iTermSet])); 384 SendIAC(SE); 385 386 if (iTermSet < LASTTERM ) 387 iTermSet+=1; 388 } 389 if (pszBuffer + 5 < pszBufferEnd) 390 pszBuffer += 6; 391 } 392 break; 393 case TELOPT_XDISPLOC: 394 if(pszBuffer + 5 < pszBufferEnd) { 395 TELOPT_PRINTD("RCVD SB XDISPLOC\n"); 396 SendIAC(SB, TELOPT_XDISPLOC); 397 TELOPT_PRINTD("SENT SB XDISPLOC"); 398 SendIACParams(0); 399 if(Network.GetLocalAddress()) Network.WriteString(Network.GetLocalAddress(), 400 strlen(Network.GetLocalAddress())); 401 TELOPT_PRINTD(Network.GetLocalAddress()); 402 TELOPT_PRINTD("\n"); 403 SendIAC(SE); 404 if (pszBuffer + 5 < pszBufferEnd) 405 pszBuffer += 6; 406 } 407 break; 408 default: break; 409 } 410 break; 411 } 412 default: 413 pszBuffer += 2; 414 break; 415 } 416 } 417 return pszBuffer; 418 } 419 420 #ifdef __BORLANDC__ 421 // bring bug optimazations 422 #pragma -O. 423 #endif 424 425 // This is the code from TANSIParser::ParseBuffer. It parses out IACs, and 426 // then calls TParser::ParseBuffer to do the terminal emulation. 427 // (Paul Brannan 6/15/98) 428 // Hopefully eliminating the unnecessary copying should speed things up a 429 // little. (Paul Brannan 6/28/98) 430 char* TTelnetHandler::ParseBuffer(char* pszBuffer, char* pszBufferEnd){ 431 char *pszResult; 432 char *pszHead = pszBuffer; 433 434 if(Network.get_net_type() == TN_NETSOCKET) { 435 while (pszBuffer < pszBufferEnd) { 436 // if IAC then parse IAC 437 if((unsigned char) *pszBuffer == IAC) { 438 439 // check for escaped IAC 440 if((pszBufferEnd >= pszBuffer + 1) && 441 (unsigned char)*(pszBuffer + 1) == IAC) { 442 // we move data at the front of the buffer to the end so 443 // that if we only have IACs we won't return pszBuffer 444 // even though we did parse something. Returning 445 // pszBuffer is an error condition. 446 memmove(pszHead + 1, pszHead, pszBuffer - pszHead); 447 pszBuffer+=2; 448 pszHead++; 449 } 450 // parse the IAC 451 else { 452 pszResult = ParseIAC(pszBuffer, pszBufferEnd); 453 if(pszBuffer == pszResult) return pszBuffer; 454 // see above regarding moving from front to end. 455 memmove(pszHead + (pszResult - pszBuffer), pszHead, 456 pszBuffer - pszHead); 457 pszHead += (pszResult - pszBuffer); 458 pszBuffer = pszResult; 459 } 460 } 461 // else copy char over to ANSI buffer 462 else { 463 pszBuffer++; 464 } 465 } 466 467 // Not a socket connection, so don't parse out IACs. 468 // (Paul Brannan 3/19/99) 469 } else { 470 pszBuffer = pszBufferEnd; 471 } 472 473 return(Parser.ParseBuffer(pszHead, pszBuffer)); 474 } 475 476 // telProcessNetwork calls the member function TTelnetHandler::Go, since 477 // TTelnetHandler::Go is not a static function, and cannot be called with 478 // CreateThread(). (Paul Brannan 6/15/98) 479 DWORD WINAPI telProcessNetwork(LPVOID lpParameter) { 480 TelThreadParams *pParams = (TelThreadParams *)lpParameter; 481 return pParams->TelHandler.Go(&pParams->p); 482 } 483 484 // This function is what used to be telProcessNetwork (Paul Brannan 6/15/98) 485 DWORD TTelnetHandler::Go(LPVOID pvParams) 486 { 487 NetParams *pParams = (NetParams *)pvParams; 488 489 // No longer a need to copy pParams-> socket and create an instance 490 // of TANSIParser (Paul Brannan 6/15/98) 491 492 Console.sync(); // Sync with the parser so the cursor is positioned 493 494 Parser.Init(); // Reset the parser (Paul Brannan 9/19/98) 495 init(); // Turn on local echo (Paul Brannan 9/19/98) 496 497 *pParams->bNetFinished = 0; 498 char* pszHead = szBuffer; 499 char* pszTail = szBuffer; 500 while (!*pParams->bNetFinish) { 501 // Get data from Socket 502 *pParams->bNetPaused = 1; //Pause 503 int Result = Network.ReadString(pszTail, (szBuffer + dwBuffer) - pszTail); 504 505 // Speed up mouse by not going into loop (Paul Brannan 8/10/98) 506 // while(*pParams->bNetPause && !*pParams->bNetFinish) *pParams->bNetPaused = 1; //Pause 507 if(WaitForSingleObject(pParams->hPause, 0) == WAIT_OBJECT_0) 508 WaitForSingleObject(pParams->hUnPause, INFINITE); 509 510 *pParams->bNetPaused = 0; //UnPause 511 512 if (Result <= 0 || Result > dwBuffer ){ 513 break; 514 } 515 pszTail += Result; 516 517 // Process the buffer 518 char* pszNewHead = pszHead; 519 do { 520 // Speed up mouse by not going into loop (Paul Brannan 8/10/98) 521 if(WaitForSingleObject(pParams->hPause, 0) == WAIT_OBJECT_0) { 522 *pParams->bNetPaused = 1; 523 WaitForSingleObject(pParams->hUnPause, INFINITE); 524 *pParams->bNetPaused = 0; 525 } 526 527 pszHead = pszNewHead; 528 pszNewHead = ParseBuffer(pszHead, pszTail); // Parse buffer 529 } while ((pszNewHead != pszHead) && (pszNewHead < pszTail) && !*pParams->bNetFinish); 530 pszHead = pszNewHead; 531 532 // When we reach the end of the buffer, move contents to the 533 // beginning of the buffer to get free space at the end. 534 if (pszTail == (szBuffer + dwBuffer)) { 535 memmove(szBuffer, pszHead, pszTail - pszHead); 536 pszTail = szBuffer + (pszTail - pszHead); 537 pszHead = szBuffer; 538 } 539 } 540 SetEvent(pParams->hExit); 541 542 printm(0, FALSE, MSG_TERMBYREM); 543 *pParams->bNetPaused = 1; //Pause 544 *pParams->bNetFinished = 1; 545 return 0; 546 } 547