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 ///////////////////////////////////////////////////////////////////////////////
43 int naws_string(char *buf
, int width
, int height
);
45 // This helps make the code more readable (Paul Brannan 1/1/99)
47 #define TELOPT_PRINTD(x) printit(x);
48 #define TELOPT_PRINTD2(x,n) { \
49 static char buf[20]; \
57 #define TELOPT_PRINTD(x) ;
58 #define TELOPT_PRINTD2(x,n) ;
61 // A new print function for debugging (Paul Brannan 5/15/98)
63 void TTelnetHandler::print_telopt(const char *s
, int d
) {
73 TTelnetHandler::TTelnetHandler(TNetwork
&RefNetwork
, TConsole
&RefConsole
,
75 Network(RefNetwork
), Console(RefConsole
), Parser(RefParser
) {
78 // Paul Brannan 9/13/98
79 dwBuffer
= ini
.get_buffer_size();
80 szBuffer
= new char [dwBuffer
];
81 Network
.SetNawsFunc(NULL
);
84 void TTelnetHandler::init() {
90 Network
.set_local_echo(1);
93 TTelnetHandler::~TTelnetHandler() {
97 int TTelnetHandler::escapeIAC(char *buf
, int length
){
98 // The size of buffer must be greater than 2 * length to ensure no memory
99 // out of bounds errors. The 0xff is escaped into 0xff 0xff.
101 temp
= new char [length
* 2];
103 for (int x
=0; x
< length
; x
++){
104 if (buf
[x
] == (signed char)IAC
)
105 temp
[current
++]=(char)IAC
;
106 temp
[current
++]=buf
[x
];
108 memcpy( buf
, temp
, current
);
113 // This lets us get rid of all the printf's (Paul Brannan 5/15/98)
114 void TTelnetHandler::SendIAC(char c
) {
115 static char buf
[2] = {IAC
};
117 Network
.WriteString(buf
, 2);
119 void TTelnetHandler::SendIAC(char c1
, char c2
) {
120 static char buf
[3] = {IAC
};
121 buf
[1] = c1
; buf
[2] = c2
;
122 Network
.WriteString(buf
, 3);
124 void TTelnetHandler::SendIACParams(char c
) {
127 static int length
= escapeIAC(buf
, 1);
128 Network
.WriteString(buf
, length
);
130 void TTelnetHandler::SendIACParams(char c1
, char c2
) {
132 buf
[0] = c1
; buf
[1] = c2
;
133 static int length
= escapeIAC(buf
, 2);
134 Network
.WriteString(buf
, length
);
137 int naws_string(char *b
, int width
, int height
) {
139 unsigned char *buf
= (unsigned char *)b
;
148 buf
[l
++] = TELOPT_NAWS
;
151 buf
[l
] = szResponse
[1];
152 if(buf
[l
-1] == IAC
) buf
[l
++] = IAC
;
153 buf
[l
++] = szResponse
[0];
154 if(buf
[l
-1] == IAC
) buf
[l
++] = IAC
;
157 buf
[l
++] = szResponse
[1];
158 if(buf
[l
-1] == IAC
) buf
[l
++] = IAC
;
159 buf
[l
++] = szResponse
[0];
160 if(buf
[l
-1] == IAC
) buf
[l
++] = IAC
;
168 // Ioannou 29 May 1998 : Something strange happens with
169 // Borland compiler at this point when it passes the arguments
170 // to SendIACParams. It always sends 80 lines to the server !!!
171 // There seems to be a bug with optimization (the disassemble shows
172 // that it uses an address plus 0xa than the right one).
173 // This turns them off for this point.
178 // Removed old printf code that was commented out to clean this function
179 // up a bit (Paul brannan 6/15/98)
180 char* TTelnetHandler::ParseIAC(char* pszBuffer
, char* pszBufferEnd
)
183 // char szResponse[40];
184 // Ioannou 29 May 1998 : I prefer the union redefinitions
185 // than the typecasting (used with them from Pascal and Cobol :-) )
186 // FIX ME !!!! Shall we use the winsock routines instead ?
193 // Added support for user-defined term name (Paul Brannan 5/13/98)
195 const char *pszTerms
[] = {ini
.get_term(), "ANSI","DEC-VT100","DEC-VT52","UNKNOWN"};
196 if(!iTermSet
&& (pszTerms
[0] == 0 || *pszTerms
[0] == 0)) iTermSet
++;
198 if (pszBuffer
+ 2 < pszBufferEnd
) {
199 switch ((unsigned char)pszBuffer
[1]) {
201 ///////////////// DO ////////////////////
204 switch (pszBuffer
[2]){
206 TELOPT_PRINTD("RCVD DO TELOPT_BINARY\n");
208 SendIAC(WILL
, TELOPT_BINARY
);
210 TELOPT_PRINTD("SENT WILL TELOPT_BINARY\n");
214 // we shouldn't echo for the server! (Paul Brannan 5/30/98)
215 TELOPT_PRINTD2("RCVD DO TELOPT_ECHO", pszBuffer
[2]);
216 SendIAC(WONT
, TELOPT_ECHO
);
217 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n");
220 TELOPT_PRINTD("RCVD DO TELOPT_TTYPE\n");
221 SendIAC(WILL
, TELOPT_TTYPE
);
222 TELOPT_PRINTD("SENT WILL TELOPT_TTYPE\n");
225 TELOPT_PRINTD("RCVD DO TELOPT_NAWS\n");
226 SendIAC(WILL
, TELOPT_NAWS
);
227 SendIAC(SB
, TELOPT_NAWS
);
229 Network
.SetNawsFunc(naws_string
);
231 n
= Console
.GetWidth();
232 SendIACParams(szResponse
[1],szResponse
[0]);
234 n
= Console
.GetHeight();
235 SendIACParams(szResponse
[1],szResponse
[0]);
238 TELOPT_PRINTD("SENT WILL TELOPT_NAWS\n");
240 case TELOPT_XDISPLOC
:
241 TELOPT_PRINTD("RCVD DO TELOPT_XDISPLOC\n");
242 SendIAC(WILL
, TELOPT_XDISPLOC
);
243 TELOPT_PRINTD("SENT WILL TELOPT_XDISPLOC\n");
244 printit("Retrieving IP...");
247 TELOPT_PRINTD2("RCVD DO", pszBuffer
[2]);
248 SendIAC(WONT
, pszBuffer
[2]);
249 TELOPT_PRINTD2("SENT WONT", pszBuffer
[2]);
252 if (pszBuffer
+ 2 < pszBufferEnd
)
257 ///////////////// WILL ////////////////////
260 switch ((unsigned char)pszBuffer
[2]){
262 TELOPT_PRINTD("RCVD WILL TELOPT_BINARY\n");
264 SendIAC(DO
, TELOPT_BINARY
);
266 TELOPT_PRINTD("SENT DO TELOPT_BINARY\n");
270 TELOPT_PRINTD2("RCVD WILL TELOPT_ECHO", pszBuffer
[2]);
272 SendIAC(DO
, TELOPT_ECHO
);
274 Network
.set_local_echo(0); // Paul Brannan 8/25/98
275 if(iWillSGA
) Network
.set_line_mode(0);
276 TELOPT_PRINTD2("SENT DO TELOPT_ECHO", pszBuffer
[2]);
277 if(Network
.get_local_echo()) Network
.set_line_mode(0);
281 // Suppress Go Ahead (Paul Brannan 12/31/98)
283 TELOPT_PRINTD("RCVD WILL TELOPT_SGA\n");
285 SendIAC(DO
, TELOPT_SGA
);
286 if(bInEchoRx
) Network
.set_line_mode(0);
288 TELOPT_PRINTD("SENT DO TELOPT_SGA\n");
294 TELOPT_PRINTD2("RCVD WILL", pszBuffer
[2]);
295 SendIAC(DONT
, pszBuffer
[2]);
296 TELOPT_PRINTD2("SENT DONT", pszBuffer
[2]);
300 if (pszBuffer
+ 2 < pszBufferEnd
)
305 ///////////////// WONT ////////////////////
308 switch ((unsigned char)pszBuffer
[2]){
310 TELOPT_PRINTD("RCVD WONT TELOPT_ECHO\n");
312 SendIAC(DONT
, TELOPT_ECHO
);
314 bInEchoRx
= 0; // Paul Brannan 8/25/98
315 Network
.set_local_echo(1);
316 Network
.set_line_mode(0);
317 TELOPT_PRINTD("SENT DONT TELOPT_ECHO\n");
321 // Suppress Go Ahead (Paul Brannan 12/31/98)
323 TELOPT_PRINTD("RCVD WONT TELOPT_SGA\n");
325 SendIAC(DONT
, TELOPT_SGA
);
326 Network
.set_line_mode(0);
328 TELOPT_PRINTD("SENT DONT TELOPT_SGA\n");
333 TELOPT_PRINTD2("RCVD WONT", pszBuffer
[2]);
336 if (pszBuffer
+ 2 < pszBufferEnd
)
341 ///////////////// DONT ////////////////////
344 switch ((unsigned char)pszBuffer
[2]){
346 TELOPT_PRINTD("RCVD DONT TELOPT_ECHO\n");
348 SendIAC(WONT
, TELOPT_ECHO
);
350 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n");
354 TELOPT_PRINTD("RCVD DONT TELOPT_NAWS\n");
355 SendIAC(WONT
, TELOPT_NAWS
);
356 Network
.SetNawsFunc(naws_string
);
357 TELOPT_PRINTD("SENT WONT TELOPT_NAWS\n");
360 TELOPT_PRINTD2("RCVD DONT", pszBuffer
[2]);
363 if (pszBuffer
+ 2 < pszBufferEnd
)
368 ///////////////// SB ////////////////////
371 switch ((unsigned char)pszBuffer
[2]){
373 if (pszBuffer
+ 5 < pszBufferEnd
) {
374 TELOPT_PRINTD("RCVD SB TELOPT_TTYPE\n");
375 if (pszBuffer
[3] == 1){
376 TELOPT_PRINTD("SENT SB TT");
377 TELOPT_PRINTD(pszTerms
[iTermSet
]);
379 SendIAC(SB
, TELOPT_TTYPE
);
381 Network
.WriteString(pszTerms
[iTermSet
], strlen(pszTerms
[iTermSet
]));
384 if (iTermSet
< LASTTERM
)
387 if (pszBuffer
+ 5 < pszBufferEnd
)
391 case TELOPT_XDISPLOC
:
392 if(pszBuffer
+ 5 < pszBufferEnd
) {
393 TELOPT_PRINTD("RCVD SB XDISPLOC\n");
394 SendIAC(SB
, TELOPT_XDISPLOC
);
395 TELOPT_PRINTD("SENT SB XDISPLOC");
397 if(Network
.GetLocalAddress()) Network
.WriteString(Network
.GetLocalAddress(),
398 strlen(Network
.GetLocalAddress()));
399 TELOPT_PRINTD(Network
.GetLocalAddress());
402 if (pszBuffer
+ 5 < pszBufferEnd
)
419 // bring bug optimazations
423 // This is the code from TANSIParser::ParseBuffer. It parses out IACs, and
424 // then calls TParser::ParseBuffer to do the terminal emulation.
425 // (Paul Brannan 6/15/98)
426 // Hopefully eliminating the unnecessary copying should speed things up a
427 // little. (Paul Brannan 6/28/98)
428 char* TTelnetHandler::ParseBuffer(char* pszBuffer
, char* pszBufferEnd
){
430 char *pszHead
= pszBuffer
;
432 if(Network
.get_net_type() == TN_NETSOCKET
) {
433 while (pszBuffer
< pszBufferEnd
) {
434 // if IAC then parse IAC
435 if((unsigned char) *pszBuffer
== IAC
) {
437 // check for escaped IAC
438 if((pszBufferEnd
>= pszBuffer
+ 1) &&
439 (unsigned char)*(pszBuffer
+ 1) == IAC
) {
440 // we move data at the front of the buffer to the end so
441 // that if we only have IACs we won't return pszBuffer
442 // even though we did parse something. Returning
443 // pszBuffer is an error condition.
444 memmove(pszHead
+ 1, pszHead
, pszBuffer
- pszHead
);
450 pszResult
= ParseIAC(pszBuffer
, pszBufferEnd
);
451 if(pszBuffer
== pszResult
) return pszBuffer
;
452 // see above regarding moving from front to end.
453 memmove(pszHead
+ (pszResult
- pszBuffer
), pszHead
,
454 pszBuffer
- pszHead
);
455 pszHead
+= (pszResult
- pszBuffer
);
456 pszBuffer
= pszResult
;
459 // else copy char over to ANSI buffer
465 // Not a socket connection, so don't parse out IACs.
466 // (Paul Brannan 3/19/99)
468 pszBuffer
= pszBufferEnd
;
471 return(Parser
.ParseBuffer(pszHead
, pszBuffer
));
474 // telProcessNetwork calls the member function TTelnetHandler::Go, since
475 // TTelnetHandler::Go is not a static function, and cannot be called with
476 // CreateThread(). (Paul Brannan 6/15/98)
477 DWORD WINAPI
telProcessNetwork(LPVOID lpParameter
) {
478 TelThreadParams
*pParams
= (TelThreadParams
*)lpParameter
;
479 return pParams
->TelHandler
.Go(&pParams
->p
);
482 // This function is what used to be telProcessNetwork (Paul Brannan 6/15/98)
483 DWORD
TTelnetHandler::Go(LPVOID pvParams
)
485 NetParams
*pParams
= (NetParams
*)pvParams
;
487 // No longer a need to copy pParams-> socket and create an instance
488 // of TANSIParser (Paul Brannan 6/15/98)
490 Console
.sync(); // Sync with the parser so the cursor is positioned
492 Parser
.Init(); // Reset the parser (Paul Brannan 9/19/98)
493 init(); // Turn on local echo (Paul Brannan 9/19/98)
495 *pParams
->bNetFinished
= 0;
496 char* pszHead
= szBuffer
;
497 char* pszTail
= szBuffer
;
498 while (!*pParams
->bNetFinish
) {
499 // Get data from Socket
500 *pParams
->bNetPaused
= 1; //Pause
501 int Result
= Network
.ReadString(pszTail
, (szBuffer
+ dwBuffer
) - pszTail
);
503 // Speed up mouse by not going into loop (Paul Brannan 8/10/98)
504 // while(*pParams->bNetPause && !*pParams->bNetFinish) *pParams->bNetPaused = 1; //Pause
505 if(WaitForSingleObject(pParams
->hPause
, 0) == WAIT_OBJECT_0
)
506 WaitForSingleObject(pParams
->hUnPause
, INFINITE
);
508 *pParams
->bNetPaused
= 0; //UnPause
510 if (Result
<= 0 || Result
> dwBuffer
){
515 // Process the buffer
516 char* pszNewHead
= pszHead
;
518 // Speed up mouse by not going into loop (Paul Brannan 8/10/98)
519 if(WaitForSingleObject(pParams
->hPause
, 0) == WAIT_OBJECT_0
) {
520 *pParams
->bNetPaused
= 1;
521 WaitForSingleObject(pParams
->hUnPause
, INFINITE
);
522 *pParams
->bNetPaused
= 0;
525 pszHead
= pszNewHead
;
526 pszNewHead
= ParseBuffer(pszHead
, pszTail
); // Parse buffer
527 } while ((pszNewHead
!= pszHead
) && (pszNewHead
< pszTail
) && !*pParams
->bNetFinish
);
528 pszHead
= pszNewHead
;
530 // When we reach the end of the buffer, move contents to the
531 // beginning of the buffer to get free space at the end.
532 if (pszTail
== (szBuffer
+ dwBuffer
)) {
533 memmove(szBuffer
, pszHead
, pszTail
- pszHead
);
534 pszTail
= szBuffer
+ (pszTail
- pszHead
);
538 SetEvent(pParams
->hExit
);
540 printm(0, FALSE
, MSG_TERMBYREM
);
541 *pParams
->bNetPaused
= 1; //Pause
542 *pParams
->bNetFinished
= 1;