summaryrefslogtreecommitdiff
path: root/src/protocoltelnet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocoltelnet.cpp')
-rw-r--r--src/protocoltelnet.cpp532
1 files changed, 496 insertions, 36 deletions
diff --git a/src/protocoltelnet.cpp b/src/protocoltelnet.cpp
index b0209db..e4fc926 100644
--- a/src/protocoltelnet.cpp
+++ b/src/protocoltelnet.cpp
@@ -1,30 +1,69 @@
1#include "bu/protocoltelnet.h" 1#include "bu/protocoltelnet.h"
2#include "bu/client.h" 2#include "bu/client.h"
3 3
4#define CODE_SE '\xf0' /**< End of subnegotiation params. */ 4/* We apparently at least want defs for the lower 13, not sure we care about
5#define CODE_NOP '\xf1' /**< No operation (keep-alive). */ 5 * the rest of the chars, maybe escape.
6#define CODE_DM '\xf2' /**< Datastream side of a Synch. */ 6 */
7#define CODE_BRK '\xf3' /**< Break character. */ 7#define CH_NUL '\x00' /* NUL */
8#define CODE_IP '\xf4' /**< Interrupt Process character. */ 8#define CH_SOH '\x01' /* Start Of Heading */
9#define CODE_AO '\xf5' /**< Abort Output character. */ 9#define CH_STX '\x02' /* Start of Text */
10#define CODE_AYT '\xf6' /**< Are You There? character. */ 10#define CH_ETX '\x03' /* End of Text */
11#define CODE_EC '\xf7' /**< Erase Character character. */ 11#define CH_EOT '\x04' /* End of transmission */
12#define CODE_EL '\xf8' /**< Erase Line character. */ 12#define CH_ENQ '\x05' /* Enquiery */
13#define CODE_GA '\xf9' /**< Go Ahead signal. */ 13#define CH_ACK '\x06' /* Acknowledge */
14#define CODE_SB '\xfa' /**< Begin subnegotiation options. */ 14#define CH_BEL '\x07' /* Bell */
15#define CODE_WILL '\xfb' /**< Desire to do something. */ 15#define CH_BS '\x08' /* Backspace */
16#define CODE_WONT '\xfc' /**< Refuse to perform. */ 16#define CH_TAB '\x09' /* Horizontal Tab */
17#define CODE_DO '\xfd' /**< Request option. */ 17#define CH_LF '\x0A' /* NL Line feed, new line */
18#define CODE_DONT '\xfe' /**< Demand a stop. */ 18#define CH_VT '\x0B' /* Vertical Tab */
19 19#define CH_FF '\x0C' /* Form feed, new page */
20#define CODE_IAC '\xff' /**< Interpret-As-Command. */ 20#define CH_CR '\x0D' /* Carriage return */
21 21#define CH_ESC '\x1B' /* Escape */
22#define OPT_BINARY '\x00' /**< Binary mode (file transfers?). */ 22#define CH_DEL '\x7F' /* Delete */
23#define OPT_ECHO '\x01' /**< (local) Echo mode. */ 23
24#define CODE_SE '\xf0' /* End of subnegotiation params. */
25#define CODE_NOP '\xf1' /* No operation (keep-alive). */
26#define CODE_DM '\xf2' /* Datastream side of a Synch. */
27#define CODE_BRK '\xf3' /* Break character. */
28#define CODE_IP '\xf4' /* Interrupt Process character. */
29#define CODE_AO '\xf5' /* Abort Output character. */
30#define CODE_AYT '\xf6' /* Are You There? character. */
31#define CODE_EC '\xf7' /* Erase Character character. */
32#define CODE_EL '\xf8' /* Erase Line character. */
33#define CODE_GA '\xf9' /* Go Ahead signal. */
34#define CODE_SB '\xfa' /* Begin subnegotiation options. */
35#define CODE_WILL '\xfb' /* Desire to do something. */
36#define CODE_WONT '\xfc' /* Refuse to perform. */
37#define CODE_DO '\xfd' /* Request option. */
38#define CODE_DONT '\xfe' /* Demand a stop. */
39
40#define CODE_IAC '\xff' /* Interpret-As-Command. */
41
42#define OPT_BINARY '\x00' /* Binary mode (file transfers?). */
43#define OPT_ECHO '\x01' /* (local) Echo mode. */
44#define OPT_SUPGA '\x03' /* Suppress Go Ahead signals. */
45#define OPT_STATUS '\x05' /* Allow status messages. */
46#define OPT_TIMING '\x06' /* Place a timing mark in the code. */
47#define OPT_EXASCII '\x11' /* Extended ASCII. */
48#define OPT_LOGOUT '\x12' /* Logout. */
49#define OPT_TTYPE '\x18' /* Terminal Type. */
50#define OPT_NAWS '\x1f' /* Negotiate about window size. */
51#define OPT_TSPEED '\x20' /* Terminal Speed. */
52#define OPT_NEWENV '\x27' /* New Environment Option. */
53#define OPT_EXOPL '\xff' /* Can we, will we, handle extended options. */
54
55#ifndef __TELNET_DEBUG
56# define printCode( a ) (void)0
57# define printOpt( a ) (void)0
58#endif
24 59
25Bu::ProtocolTelnet::ProtocolTelnet() : 60Bu::ProtocolTelnet::ProtocolTelnet() :
26 oBinary( *this, OPT_BINARY ), 61 oBinary( *this, OPT_BINARY ),
27 oEcho( *this, OPT_ECHO ) 62 oEcho( *this, OPT_ECHO ),
63 oNAWS( *this, OPT_NAWS ),
64 oSuppressGA(*this, OPT_SUPGA ),
65 bCanonical( true ),
66 bSubOpt( false )
28{ 67{
29} 68}
30 69
@@ -34,13 +73,410 @@ Bu::ProtocolTelnet::~ProtocolTelnet()
34 73
35void Bu::ProtocolTelnet::onNewConnection( Bu::Client *pClient ) 74void Bu::ProtocolTelnet::onNewConnection( Bu::Client *pClient )
36{ 75{
76 this->pClient = pClient;
37} 77}
38 78
39void Bu::ProtocolTelnet::onNewData( Bu::Client *pClient ) 79void Bu::ProtocolTelnet::onNewData( Bu::Client *pClient )
40{ 80{
81 char bc;
82 int iLeft;
83 while( (iLeft = pClient->getInputSize()) )
84 {
85 if( bSubOpt )
86 {
87 pClient->peek( &bc, 1 );
88 if( bc == CODE_IAC )
89 {
90 if( iLeft <= 1 ) return;
91 char bc2;
92 printCode( CODE_IAC );
93 pClient->peek( &bc2, 1, 1 );
94 printCode( bc2 );
95 if( bc2 == CODE_SE )
96 {
97 bSubOpt = false;
98 onSubOpt();
99 }
100 else if( bc2 == CODE_IAC )
101 {
102 sSubBuf += CODE_IAC;
103 }
104 else
105 {
106 // Error of some sort.
107 }
108 pClient->seek( 1 );
109 }
110 else
111 {
112 sSubBuf += bc;
113 }
114 pClient->seek( 1 );
115 }
116 else
117 {
118 pClient->peek( &bc, 1 );
119 if( bc == CODE_IAC )
120 {
121 if( iLeft <= 1 ) return;
122 char bc2;
123 pClient->peek( &bc2, 1, 1 );
124 printCode( bc );
125 printCode( bc2 );
126
127 switch( bc2 )
128 {
129 case CODE_WILL:
130 if( iLeft <= 2 ) return;
131 {
132 char bc3;
133 pClient->peek( &bc3, 1, 2 );
134 pClient->seek( 1 );
135 printOpt( bc3 );
136 onWill( bc3 );
137 }
138 break;
139
140 case CODE_WONT:
141 if( iLeft <= 2 ) return;
142 {
143 char bc3;
144 pClient->peek( &bc3, 1, 2 );
145 pClient->seek( 1 );
146 printOpt( bc3 );
147 onWont( bc3 );
148 }
149 break;
150
151 case CODE_DO:
152 if( iLeft <= 2 ) return;
153 {
154 char bc3;
155 pClient->peek( &bc3, 1, 2 );
156 pClient->seek( 1 );
157 printOpt( bc3 );
158 onDo( bc3 );
159 }
160 break;
161
162 case CODE_DONT:
163 if( iLeft <= 2 ) return;
164 {
165 char bc3;
166 pClient->peek( &bc3, 1, 2 );
167 pClient->seek( 1 );
168 printOpt( bc3 );
169 onDont( bc3 );
170 }
171 break;
172
173 case CODE_SB:
174 if( iLeft <= 2 ) return;
175 {
176 pClient->peek( &cSubOpt, 1, 2 );
177 pClient->seek( 1 );
178 printOpt( cSubOpt );
179 bSubOpt = true;
180 }
181 break;
182
183 case CODE_IAC:
184 sDataBuf += CODE_IAC;
185 printCode( CODE_IAC );
186 break;
187 }
188 pClient->seek( 1 );
189#ifdef __TELNET_DEBUG
190 printf("\n");
191#endif
192 }
193 else if( bc == CODE_SB )
194 {
195 }
196 else
197 {
198 // This is where control code handling goes
199 // Also, possibly, character code conversion, although I'm not
200 // sure that really matters anymore, go ASCII/UTF-8
201 if( bCanonical )
202 {
203 if( bc < 0x20 || bc >= CH_DEL )
204 {
205 if( bc == CH_CR )
206 {
207 if( iLeft <= 1 ) return;
208 char bc2;
209 pClient->peek( &bc2, 1, 1 );
210 if( bc2 == CH_NUL || bc2 == CH_LF )
211 {
212 onCtlChar( bc );
213 gotLine( sDataBuf );
214 sDataBuf.clear();
215 }
216 pClient->seek( 1 );
217 }
218 else
219 {
220 onCtlChar( bc );
221 }
222 }
223 else
224 {
225 sDataBuf += bc;
226 if( oEcho.isLocalSet() )
227 {
228 pClient->write( &bc, 1 );
229#ifdef __TELNET_DEBUG
230 printf("%c", bc );
231 fflush( stdout );
232#endif
233 }
234 }
235 }
236 else
237 {
238 sDataBuf += bc;
239 if( oEcho.isLocalSet() )
240 {
241 pClient->write( &bc, 1 );
242 }
243 }
244 }
245 pClient->seek( 1 );
246 }
247 }
248
249 // It's true, this code will not be executed if we only have half of an
250 // IAC code or multibyte escape sequence or something, but then again, it
251 // shouldn't be called then, and really, shouldn't be, it'll be called soon
252 // enough, when we get the rest of that code.
253 if( !bCanonical )
254 {
255 gotData( sDataBuf );
256 }
257}
258
259void Bu::ProtocolTelnet::setCanonical( bool bCon )
260{
261 bCanonical = bCon;
262}
263
264bool Bu::ProtocolTelnet::isCanonical()
265{
266 return bCanonical;
267}
268
269void Bu::ProtocolTelnet::write( const Bu::FString &sData )
270{
271 pClient->write( sData );
272}
273
274void Bu::ProtocolTelnet::write( char *pData, int iSize )
275{
276 pClient->write( pData, iSize );
277}
278
279void Bu::ProtocolTelnet::write( char cData )
280{
281 pClient->write( &cData, 1 );
282}
283
284void Bu::ProtocolTelnet::onWill( char cCode )
285{
286 try
287 {
288 Option *pOpt = hOpts[cCode];
289 if( pOpt->isRemoteEnabled() )
290 {
291 pOpt->fOpts |= Option::fRemoteIs;
292 char buf[3] = { CODE_IAC, CODE_DO, cCode };
293 pClient->write( buf, 3 );
294 }
295 else
296 {
297 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
298 pClient->write( buf, 3 );
299 }
300
301 }
302 catch( Bu::HashException &e )
303 {
304 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
305 pClient->write( buf, 3 );
306 }
307}
308
309void Bu::ProtocolTelnet::onWont( char cCode )
310{
311 try
312 {
313 Option *pOpt = hOpts[cCode];
314
315 pOpt->fOpts &= ~Option::fRemoteIs;
316 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
317 pClient->write( buf, 3 );
318 }
319 catch( Bu::HashException &e )
320 {
321 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
322 pClient->write( buf, 3 );
323 }
324}
325
326void Bu::ProtocolTelnet::onDo( char cCode )
327{
328 try
329 {
330 Option *pOpt = hOpts[cCode];
331 if( pOpt->isLocalEnabled() )
332 {
333 pOpt->fOpts |= Option::fLocalIs;
334 char buf[3] = { CODE_IAC, CODE_WILL, cCode };
335 pClient->write( buf, 3 );
336 }
337 else
338 {
339 char buf[3] = { CODE_IAC, CODE_WONT, cCode };
340 pClient->write( buf, 3 );
341 }
342
343 }
344 catch( Bu::HashException &e )
345 {
346 char buf[3] = { CODE_IAC, CODE_WONT, cCode };
347 pClient->write( buf, 3 );
348 }
349}
350
351void Bu::ProtocolTelnet::onDont( char cCode )
352{
353 try
354 {
355 Option *pOpt = hOpts[cCode];
356
357 pOpt->fOpts &= ~Option::fLocalIs;
358 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
359 pClient->write( buf, 3 );
360 }
361 catch( Bu::HashException &e )
362 {
363 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
364 pClient->write( buf, 3 );
365 }
41} 366}
42 367
368void Bu::ProtocolTelnet::onSubOpt()
369{
370 switch( cSubOpt )
371 {
372 case OPT_NAWS:
373 {
374 uint16_t iWidth, iHeight;
375 ((char *)&iWidth)[1] = sSubBuf[0];
376 ((char *)&iWidth)[0] = sSubBuf[1];
377 ((char *)&iHeight)[1] = sSubBuf[2];
378 ((char *)&iHeight)[0] = sSubBuf[3];
379 onSubNAWS( iWidth, iHeight );
380 }
381 break;
43 382
383 default:
384 onSubUnknown( cSubOpt, sSubBuf );
385 break;
386 }
387
388 sSubBuf.clear();
389}
390
391void Bu::ProtocolTelnet::onCtlChar( char cChr )
392{
393#ifdef __TELNET_DEBUG
394 switch( cChr )
395 {
396 case CH_NUL: printf("NUL "); break;
397 case CH_SOH: printf("SOH "); break;
398 case CH_STX: printf("STX "); break;
399 case CH_ETX: printf("ETX "); break;
400 case CH_EOT: printf("EOT "); break;
401 case CH_ENQ: printf("ENQ "); break;
402 case CH_ACK: printf("ACK "); break;
403 case CH_BEL: printf("BEL "); break;
404 case CH_BS: printf("BS "); break;
405 case CH_TAB: printf("TAB "); break;
406 case CH_LF: printf("LF "); break;
407 case CH_VT: printf("VT "); break;
408 case CH_FF: printf("FF "); break;
409 case CH_CR: printf("CR "); break;
410 case CH_ESC: printf("ESC "); break;
411 case CH_DEL: printf("DEL "); break;
412 default: printf("!![%02x] ", cChr ); break;
413 }
414 fflush( stdout );
415#endif
416
417 switch( cChr )
418 {
419 case CH_DEL:
420 {
421 if( sDataBuf.getSize() > 0 )
422 {
423 sDataBuf.resize( sDataBuf.getSize()-1 );
424 char buf[3] = { CH_BS, ' ', CH_BS };
425 pClient->write( buf, 3 );
426 }
427 }
428 break;
429
430 }
431}
432
433#ifdef __TELNET_DEBUG
434void Bu::ProtocolTelnet::printCode( char cCode )
435{
436 switch( cCode )
437 {
438 case CODE_SE: printf("SE "); break;
439 case CODE_NOP: printf("NOP "); break;
440 case CODE_DM: printf("DM "); break;
441 case CODE_BRK: printf("BRK "); break;
442 case CODE_IP: printf("IP "); break;
443 case CODE_AO: printf("AO "); break;
444 case CODE_AYT: printf("AYT "); break;
445 case CODE_EC: printf("EC "); break;
446 case CODE_EL: printf("EL "); break;
447 case CODE_GA: printf("GA "); break;
448 case CODE_SB: printf("SB "); break;
449 case CODE_WILL: printf("WILL "); break;
450 case CODE_WONT: printf("WONT "); break;
451 case CODE_DO: printf("DO "); break;
452 case CODE_DONT: printf("DONT "); break;
453 case CODE_IAC: printf("IAC "); break;
454 default: printf("??%02x ", cCode ); break;
455 }
456 fflush( stdout );
457}
458
459void Bu::ProtocolTelnet::printOpt( char cOpt )
460{
461 switch( cOpt )
462 {
463 case OPT_BINARY: printf("BINARY "); break;
464 case OPT_ECHO: printf("ECHO "); break;
465 case OPT_SUPGA: printf("SUPGA "); break;
466 case OPT_STATUS: printf("STATUS "); break;
467 case OPT_TIMING: printf("TIMING "); break;
468 case OPT_EXASCII: printf("EXASCII "); break;
469 case OPT_LOGOUT: printf("LOGOUT "); break;
470 case OPT_TTYPE: printf("TTYPE "); break;
471 case OPT_NAWS: printf("NAWS "); break;
472 case OPT_TSPEED: printf("TSPEED "); break;
473 case OPT_NEWENV: printf("NEWENV "); break;
474 case OPT_EXOPL: printf("EXOPL "); break;
475 default: printf("??%02x ", cOpt); break;
476 }
477 fflush( stdout );
478}
479#endif
44 480
45Bu::ProtocolTelnet::Option::Option( Bu::ProtocolTelnet &rPT, char cCode ) : 481Bu::ProtocolTelnet::Option::Option( Bu::ProtocolTelnet &rPT, char cCode ) :
46 rPT( rPT ), 482 rPT( rPT ),
@@ -68,19 +504,31 @@ void Bu::ProtocolTelnet::Option::localSet( bool bSet )
68{ 504{
69 if( bSet == (bool)(fOpts&fLocalIs) ) return; 505 if( bSet == (bool)(fOpts&fLocalIs) ) return;
70 506
71 char buf[2]; 507 char buf[3] = { CODE_IAC, 0, cCode };
72 508
73 if( bSet ) 509 if( bSet )
74 { 510 {
75 buf[0] = CODE_WILL; 511 buf[1] = CODE_WILL;
76 buf[1] = cCode; 512 rPT.pClient->write( buf, 3 );
77 rPT.pClient->write( buf, 2 ); 513#ifdef __TELNET_DEBUG
514 printf("<= ");
515 rPT.printCode( buf[0] );
516 rPT.printCode( buf[1] );
517 rPT.printOpt( buf[2] );
518 printf("\n");
519#endif
78 } 520 }
79 else 521 else
80 { 522 {
81 buf[0] = CODE_WONT; 523 buf[1] = CODE_WONT;
82 buf[1] = cCode; 524 rPT.pClient->write( buf, 3 );
83 rPT.pClient->write( buf, 2 ); 525#ifdef __TELNET_DEBUG
526 printf("<= ");
527 rPT.printCode( buf[0] );
528 rPT.printCode( buf[1] );
529 rPT.printOpt( buf[2] );
530 printf("\n");
531#endif
84 } 532 }
85} 533}
86 534
@@ -101,21 +549,33 @@ void Bu::ProtocolTelnet::Option::remoteEnable( bool bSet )
101 549
102void Bu::ProtocolTelnet::Option::remoteSet( bool bSet ) 550void Bu::ProtocolTelnet::Option::remoteSet( bool bSet )
103{ 551{
104 if( bSet == (bool)(fOpts&fRemoteIs) ) return; 552 //if( bSet == (bool)(fOpts&fRemoteIs) ) return;
105 553
106 char buf[2]; 554 char buf[3] = { CODE_IAC, 0, cCode };
107 555
108 if( bSet ) 556 if( bSet )
109 { 557 {
110 buf[0] = CODE_DO; 558 buf[1] = CODE_DO;
111 buf[1] = cCode; 559 rPT.pClient->write( buf, 3 );
112 rPT.pClient->write( buf, 2 ); 560#ifdef __TELNET_DEBUG
561 printf("<= ");
562 rPT.printCode( buf[0] );
563 rPT.printCode( buf[1] );
564 rPT.printOpt( buf[2] );
565 printf("\n");
566#endif
113 } 567 }
114 else 568 else
115 { 569 {
116 buf[0] = CODE_DONT; 570 buf[1] = CODE_DONT;
117 buf[1] = cCode; 571 rPT.pClient->write( buf, 3 );
118 rPT.pClient->write( buf, 2 ); 572#ifdef __TELNET_DEBUG
573 printf("<= ");
574 rPT.printCode( buf[0] );
575 rPT.printCode( buf[1] );
576 rPT.printOpt( buf[2] );
577 printf("\n");
578#endif
119 } 579 }
120} 580}
121 581