[TELNET]
[reactos.git] / reactos / base / applications / network / telnet / src / ttelhndl.cpp
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 }