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
print_telopt(const char * s,int d)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 
TTelnetHandler(TNetwork & RefNetwork,TConsole & RefConsole,TParser & RefParser)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 
init()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 
~TTelnetHandler()95 TTelnetHandler::~TTelnetHandler() {
96 	delete[] szBuffer;
97 }
98 
escapeIAC(char * buf,int length)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)
SendIAC(char c)116 void TTelnetHandler::SendIAC(char c) {
117 	static char buf[2] = {IAC};
118 	buf[1] = c;
119 	Network.WriteString(buf, 2);
120 }
SendIAC(char c1,char c2)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 }
SendIACParams(char c)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 }
SendIACParams(char c1,char c2)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 
naws_string(char * b,int width,int height)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)
ParseIAC(char * pszBuffer,char * pszBufferEnd)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)
ParseBuffer(char * pszBuffer,char * pszBufferEnd)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)
telProcessNetwork(LPVOID lpParameter)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)
Go(LPVOID pvParams)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