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
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.
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.
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.
24 ///////////////////////////////////////////////////////////////////////////
26 ///////////////////////////////////////////////////////////////////////////////
28 // Module: ttelhndl.cpp
30 // Contents: Telnet Handler
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 // June 15, 1998 pbranna@clemson.edu (Paul Brannan)
37 // This is code originally from tnnet.cpp and ansiprsr.cpp
39 ///////////////////////////////////////////////////////////////////////////////
45 int naws_string(char *buf
, int width
, int height
);
47 // This helps make the code more readable (Paul Brannan 1/1/99)
49 #define TELOPT_PRINTD(x) printit(x);
50 #define TELOPT_PRINTD2(x,n) { \
51 static char buf[20]; \
59 #define TELOPT_PRINTD(x) ;
60 #define TELOPT_PRINTD2(x,n) ;
63 // A new print function for debugging (Paul Brannan 5/15/98)
65 void TTelnetHandler::print_telopt(const char *s
, int d
) {
75 TTelnetHandler::TTelnetHandler(TNetwork
&RefNetwork
, TConsole
&RefConsole
,
77 Network(RefNetwork
), Console(RefConsole
), Parser(RefParser
) {
80 // Paul Brannan 9/13/98
81 dwBuffer
= ini
.get_buffer_size();
82 szBuffer
= new char [dwBuffer
];
83 Network
.SetNawsFunc(NULL
);
86 void TTelnetHandler::init() {
92 Network
.set_local_echo(1);
95 TTelnetHandler::~TTelnetHandler() {
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.
103 temp
= new char [length
* 2];
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
];
110 memcpy( buf
, temp
, current
);
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
};
119 Network
.WriteString(buf
, 2);
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);
126 void TTelnetHandler::SendIACParams(char c
) {
129 static int length
= escapeIAC(buf
, 1);
130 Network
.WriteString(buf
, length
);
132 void TTelnetHandler::SendIACParams(char c1
, char c2
) {
134 buf
[0] = c1
; buf
[1] = c2
;
135 static int length
= escapeIAC(buf
, 2);
136 Network
.WriteString(buf
, length
);
139 int naws_string(char *b
, int width
, int height
) {
141 unsigned char *buf
= (unsigned char *)b
;
150 buf
[l
++] = TELOPT_NAWS
;
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
;
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
;
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.
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
)
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 ?
195 // Added support for user-defined term name (Paul Brannan 5/13/98)
197 const char *pszTerms
[] = {ini
.get_term(), "ANSI","DEC-VT100","DEC-VT52","UNKNOWN"};
198 if(!iTermSet
&& (pszTerms
[0] == 0 || *pszTerms
[0] == 0)) iTermSet
++;
200 if (pszBuffer
+ 2 < pszBufferEnd
) {
201 switch ((unsigned char)pszBuffer
[1]) {
203 ///////////////// DO ////////////////////
206 switch (pszBuffer
[2]){
208 TELOPT_PRINTD("RCVD DO TELOPT_BINARY\n");
210 SendIAC(WILL
, TELOPT_BINARY
);
212 TELOPT_PRINTD("SENT WILL TELOPT_BINARY\n");
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");
222 TELOPT_PRINTD("RCVD DO TELOPT_TTYPE\n");
223 SendIAC(WILL
, TELOPT_TTYPE
);
224 TELOPT_PRINTD("SENT WILL TELOPT_TTYPE\n");
227 TELOPT_PRINTD("RCVD DO TELOPT_NAWS\n");
228 SendIAC(WILL
, TELOPT_NAWS
);
229 SendIAC(SB
, TELOPT_NAWS
);
231 Network
.SetNawsFunc(naws_string
);
233 n
= Console
.GetWidth();
234 SendIACParams(szResponse
[1],szResponse
[0]);
236 n
= Console
.GetHeight();
237 SendIACParams(szResponse
[1],szResponse
[0]);
240 TELOPT_PRINTD("SENT WILL TELOPT_NAWS\n");
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...");
249 TELOPT_PRINTD2("RCVD DO", pszBuffer
[2]);
250 SendIAC(WONT
, pszBuffer
[2]);
251 TELOPT_PRINTD2("SENT WONT", pszBuffer
[2]);
254 if (pszBuffer
+ 2 < pszBufferEnd
)
259 ///////////////// WILL ////////////////////
262 switch ((unsigned char)pszBuffer
[2]){
264 TELOPT_PRINTD("RCVD WILL TELOPT_BINARY\n");
266 SendIAC(DO
, TELOPT_BINARY
);
268 TELOPT_PRINTD("SENT DO TELOPT_BINARY\n");
272 TELOPT_PRINTD2("RCVD WILL TELOPT_ECHO", pszBuffer
[2]);
274 SendIAC(DO
, TELOPT_ECHO
);
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);
283 // Suppress Go Ahead (Paul Brannan 12/31/98)
285 TELOPT_PRINTD("RCVD WILL TELOPT_SGA\n");
287 SendIAC(DO
, TELOPT_SGA
);
288 if(bInEchoRx
) Network
.set_line_mode(0);
290 TELOPT_PRINTD("SENT DO TELOPT_SGA\n");
296 TELOPT_PRINTD2("RCVD WILL", pszBuffer
[2]);
297 SendIAC(DONT
, pszBuffer
[2]);
298 TELOPT_PRINTD2("SENT DONT", pszBuffer
[2]);
302 if (pszBuffer
+ 2 < pszBufferEnd
)
307 ///////////////// WONT ////////////////////
310 switch ((unsigned char)pszBuffer
[2]){
312 TELOPT_PRINTD("RCVD WONT TELOPT_ECHO\n");
314 SendIAC(DONT
, TELOPT_ECHO
);
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");
323 // Suppress Go Ahead (Paul Brannan 12/31/98)
325 TELOPT_PRINTD("RCVD WONT TELOPT_SGA\n");
327 SendIAC(DONT
, TELOPT_SGA
);
328 Network
.set_line_mode(0);
330 TELOPT_PRINTD("SENT DONT TELOPT_SGA\n");
335 TELOPT_PRINTD2("RCVD WONT", pszBuffer
[2]);
338 if (pszBuffer
+ 2 < pszBufferEnd
)
343 ///////////////// DONT ////////////////////
346 switch ((unsigned char)pszBuffer
[2]){
348 TELOPT_PRINTD("RCVD DONT TELOPT_ECHO\n");
350 SendIAC(WONT
, TELOPT_ECHO
);
352 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n");
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");
362 TELOPT_PRINTD2("RCVD DONT", pszBuffer
[2]);
365 if (pszBuffer
+ 2 < pszBufferEnd
)
370 ///////////////// SB ////////////////////
373 switch ((unsigned char)pszBuffer
[2]){
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
]);
381 SendIAC(SB
, TELOPT_TTYPE
);
383 Network
.WriteString(pszTerms
[iTermSet
], strlen(pszTerms
[iTermSet
]));
386 if (iTermSet
< LASTTERM
)
389 if (pszBuffer
+ 5 < pszBufferEnd
)
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");
399 if(Network
.GetLocalAddress()) Network
.WriteString(Network
.GetLocalAddress(),
400 strlen(Network
.GetLocalAddress()));
401 TELOPT_PRINTD(Network
.GetLocalAddress());
404 if (pszBuffer
+ 5 < pszBufferEnd
)
421 // bring bug optimazations
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
){
432 char *pszHead
= pszBuffer
;
434 if(Network
.get_net_type() == TN_NETSOCKET
) {
435 while (pszBuffer
< pszBufferEnd
) {
436 // if IAC then parse IAC
437 if((unsigned char) *pszBuffer
== IAC
) {
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
);
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
;
461 // else copy char over to ANSI buffer
467 // Not a socket connection, so don't parse out IACs.
468 // (Paul Brannan 3/19/99)
470 pszBuffer
= pszBufferEnd
;
473 return(Parser
.ParseBuffer(pszHead
, pszBuffer
));
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
);
484 // This function is what used to be telProcessNetwork (Paul Brannan 6/15/98)
485 DWORD
TTelnetHandler::Go(LPVOID pvParams
)
487 NetParams
*pParams
= (NetParams
*)pvParams
;
489 // No longer a need to copy pParams-> socket and create an instance
490 // of TANSIParser (Paul Brannan 6/15/98)
492 Console
.sync(); // Sync with the parser so the cursor is positioned
494 Parser
.Init(); // Reset the parser (Paul Brannan 9/19/98)
495 init(); // Turn on local echo (Paul Brannan 9/19/98)
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
);
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
);
510 *pParams
->bNetPaused
= 0; //UnPause
512 if (Result
<= 0 || Result
> dwBuffer
){
517 // Process the buffer
518 char* pszNewHead
= pszHead
;
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;
527 pszHead
= pszNewHead
;
528 pszNewHead
= ParseBuffer(pszHead
, pszTail
); // Parse buffer
529 } while ((pszNewHead
!= pszHead
) && (pszNewHead
< pszTail
) && !*pParams
->bNetFinish
);
530 pszHead
= pszNewHead
;
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
);
540 SetEvent(pParams
->hExit
);
542 printm(0, FALSE
, MSG_TERMBYREM
);
543 *pParams
->bNetPaused
= 1; //Pause
544 *pParams
->bNetFinished
= 1;