summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hash.cpp20
-rw-r--r--src/hash.h14
-rw-r--r--src/protocolhttp.h9
-rw-r--r--src/protocoltelnet.cpp532
-rw-r--r--src/protocoltelnet.h151
-rw-r--r--src/tests/telnetsrv.cpp85
6 files changed, 733 insertions, 78 deletions
diff --git a/src/hash.cpp b/src/hash.cpp
index a207c29..9b8a1c1 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -2,26 +2,6 @@
2 2
3namespace Bu { subExceptionDef( HashException ) } 3namespace Bu { subExceptionDef( HashException ) }
4 4
5template<> uint32_t Bu::__calcHashCode<int>( const int &k )
6{
7 return k;
8}
9
10template<> bool Bu::__cmpHashKeys<int>( const int &a, const int &b )
11{
12 return a == b;
13}
14
15template<> uint32_t Bu::__calcHashCode<unsigned int>( const unsigned int &k )
16{
17 return k;
18}
19
20template<> bool Bu::__cmpHashKeys<unsigned int>( const unsigned int &a, const unsigned int &b )
21{
22 return a == b;
23}
24
25template<> 5template<>
26uint32_t Bu::__calcHashCode<const char *>( const char * const &k ) 6uint32_t Bu::__calcHashCode<const char *>( const char * const &k )
27{ 7{
diff --git a/src/hash.h b/src/hash.h
index 62b19c9..be57786 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -1008,12 +1008,16 @@ namespace Bu
1008 challoc ca; 1008 challoc ca;
1009 sizecalc szCalc; 1009 sizecalc szCalc;
1010 }; 1010 };
1011
1012 template<typename T> uint32_t __calcHashCode( const T &k )
1013 {
1014 return static_cast<uint32_t>( k );
1015 }
1011 1016
1012 template<> uint32_t __calcHashCode<int>( const int &k ); 1017 template<typename T> bool __cmpHashKeys( const T &a, const T &b )
1013 template<> bool __cmpHashKeys<int>( const int &a, const int &b ); 1018 {
1014 1019 return (a == b);
1015 template<> uint32_t __calcHashCode<unsigned int>( const unsigned int &k ); 1020 }
1016 template<> bool __cmpHashKeys<unsigned int>( const unsigned int &a, const unsigned int &b );
1017 1021
1018 template<> uint32_t __calcHashCode<const char *>( const char * const &k ); 1022 template<> uint32_t __calcHashCode<const char *>( const char * const &k );
1019 template<> bool __cmpHashKeys<const char *>( const char * const &a, const char * const &b ); 1023 template<> bool __cmpHashKeys<const char *>( const char * const &a, const char * const &b );
diff --git a/src/protocolhttp.h b/src/protocolhttp.h
index e2612f5..85510e3 100644
--- a/src/protocolhttp.h
+++ b/src/protocolhttp.h
@@ -11,7 +11,14 @@
11namespace Bu 11namespace Bu
12{ 12{
13 /** 13 /**
14 * 14 * An HTTP Protocol handler. Yes, I know that HTTP stands for Hyper Text
15 * Transfer Protocol, and that the Protocol part is redundant, but in this
16 * case the word Protocol is refering to the Libbu++ construct Bu::Protocol,
17 * and not a means of encoding conversations. Anyway, this class represents
18 * a general HTTP server processor. Every time a request comes in it calls
19 * the onRequest function in a subclass with the method and URI that were
20 * requested. The sub-class can then do whatever it needs to to send back
21 * a response.
15 */ 22 */
16 class ProtocolHttp : public Protocol 23 class ProtocolHttp : public Protocol
17 { 24 {
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
diff --git a/src/protocoltelnet.h b/src/protocoltelnet.h
index 3a606b5..f773f1e 100644
--- a/src/protocoltelnet.h
+++ b/src/protocoltelnet.h
@@ -3,35 +3,131 @@
3 3
4#include "bu/protocol.h" 4#include "bu/protocol.h"
5#include "bu/hash.h" 5#include "bu/hash.h"
6#include "bu/fstring.h"
7
8// #define __TELNET_DEBUG
6 9
7namespace Bu 10namespace Bu
8{ 11{
12 /**
13 * Telnet Protocol handler. This attempts to provide useful and general
14 * support for most of the most commonly used Telnet extensions in a simple
15 * and easy to use way. The Option variables control the settings that can
16 * be used on the line, and control which virtual "callbacks" will be called
17 * when different events happen.
18 *
19 * To setup initial values and to disable any options you wish override the
20 * onNewConnection function in your own class, like this:
21 *@code
22 class MyTelnet : public Bu::ProtocolTelnet
23 {
24 public:
25 ...
26
27 virtual void onNewConnection( class Bu::Client *pClient )
28 {
29 // Call the parent class' onNewConnection to get everything all
30 // set up.
31 Bu::ProtocolTelnet::onNewConnection( pClient );
32
33 // These functions disable the option to send files via telnet,
34 // disabling the remote option means that we won't accept this
35 // option (binary data being sent to us) from the client.
36 //
37 // Disabling the local option means that the client cannot ask us
38 // to send them binary data.
39 oBinary.enableRemote( false );
40 oBinary.enableLocal( false );
41
42 // This requests that the client send us window size updates
43 // whenever the size of their window changes, and an initial set to
44 // boot.
45 //
46 // To see if this option is set later, try oNAWS.isRemoteSet(), but
47 // wait a little while, asking immediatly will always return false,
48 // since the remote side has yet to receive our request.
49 oNAWS.remoteSet();
50 }
51 }
52 @endcode
53 *
54 */
9 class ProtocolTelnet : public Protocol 55 class ProtocolTelnet : public Protocol
10 { 56 {
11 public: 57 public:
12 ProtocolTelnet(); 58 ProtocolTelnet();
13 virtual ~ProtocolTelnet(); 59 virtual ~ProtocolTelnet();
14 60
61 /**
62 * If you override this function in a child class, make sure to call
63 * this version of it as the very first thing that you do, before you
64 * set any options. See the example in the class docs.
65 */
15 virtual void onNewConnection( class Bu::Client *pClient ); 66 virtual void onNewConnection( class Bu::Client *pClient );
67
68 /**
69 * You should never override this function unless you really, really
70 * know what you're doing. If you want to get data after each line
71 * entered (in canonical mode) or after any data arrives (non canonical
72 * mode) then override the gotLine and gotData functions, respectively.
73 */
16 virtual void onNewData( class Bu::Client *pClient ); 74 virtual void onNewData( class Bu::Client *pClient );
17 75
18 enum OptMode 76 /**
19 { 77 * Override this function to be notified of lines being submitted by
20 optOff, 78 * the client. This function is only called in canonical mode, after
21 optOn, 79 * all edits are performed on the data. In this mode weather you use
22 optDesire, 80 * the line or not, the data will be cleared from the buffer when this
23 optRefuse 81 * function returns, any changes made to the buffer will be destroyed.
24 }; 82 */
83 virtual void gotLine( Bu::FString &sLine ){};
25 84
26 OptMode getLocalOptBinary(); 85 /**
27 void setLocalOptBinary( OptMode eMode ); 86 * Override this function to be notified of any new data that comes in
28 OptMode getRemoteOptBinary(); 87 * from the client. This function is only called in non-canonical mode,
29 void setRemoteOptBinary( OptMode eMode ); 88 * and includes all raw data minus telnet control codes and ansi
89 * escape sequences. In this mode control of the buffer is up to the
90 * child class in this function, the buffer will never be cleared unless
91 * it happens in this function's override.
92 */
93 virtual void gotData( Bu::FString &sData ){};
30 94
31 OptMode getLocalOptEcho(); 95 /**
32 void setLocalOptEcho( OptMode eMode ); 96 * Using this function to enable or disable canonical mode only affects
33 OptMode getRemoteOptEcho(); 97 * the way the data is processed and which virtual functions are called
34 void setRemoteOptEcho( OptMode eMode ); 98 * during processing. It does not affect options set locally or
99 * remotely. Setting this to false will enable char-at-a-time mode,
100 * effectively disabling internal line-editing code. Characters
101 * such as backspace that are detected will not be handled and will be
102 * sent to the user override. The subclass will also be notified every
103 * time new data is available, not just whole lines.
104 *
105 * When set to true (the default), line editing control codes will be
106 * interpreted and used, and the subclass will only be notified when
107 * complete lines are available in the buffer.
108 */
109 void setCanonical( bool bCon=true );
110 bool isCanonical();
111
112 void write( const Bu::FString &sData );
113 void write( char *pData, int iSize );
114 void write( char cData );
115
116 public:
117 /**
118 * If you wish to know the current dimensions of the client window,
119 * override this function, it will be called whenever the size changes.
120 */
121 virtual void onSubNAWS( uint16_t iWidth, uint16_t iHeight ){};
122
123 /**
124 * This function is called whenever an unknown sub negotiation option is
125 * sent over the line. This doesn't mean that it's malformatted, it
126 * just means that this class doesn't support that option yet, but you
127 * can handle it yourself if you'd like. Feel free to change the
128 * sSubBuf, it will be cleared as soon as this function returns anyway.
129 */
130 virtual void onSubUnknown( char cSubOpt, Bu::FString &sSubBuf ){};
35 131
36 private: 132 private:
37 /** 133 /**
@@ -75,15 +171,38 @@ namespace Bu
75 char fOpts; 171 char fOpts;
76 char cCode; 172 char cCode;
77 }; 173 };
174 friend class Bu::ProtocolTelnet::Option;
78 175
79 Hash<char, Option *> hOpts; 176 Hash<char, Option *> hOpts;
80 177
178 public:
81 Option oBinary; 179 Option oBinary;
82 Option oEcho; 180 Option oEcho;
181 Option oNAWS;
182 Option oSuppressGA;
83 183
184 private:
185 void onWill( char cCode );
186 void onWont( char cCode );
187 void onDo( char cCode );
188 void onDont( char cCode );
189 void onSubOpt();
190 void onCtlChar( char cChr );
191
192#ifdef __TELNET_DEBUG
193 void printCode( char cCode );
194 void printOpt( char cOpt );
195#endif
196
197 private:
84 Client *pClient; 198 Client *pClient;
85 199
86 friend class Bu::ProtocolTelnet::Option; 200 Bu::FString sDataBuf; /**< Buffer for regular line data. */
201 Bu::FString sSubBuf; /**< Buffer for subnegotiation data. */
202 char cSubOpt; /**< Which suboption are we processing. */
203
204 bool bCanonical; /**< Are we canonicalizing incoming data? */
205 bool bSubOpt; /**< Are we processing a suboption right now? */
87 }; 206 };
88} 207}
89 208
diff --git a/src/tests/telnetsrv.cpp b/src/tests/telnetsrv.cpp
new file mode 100644
index 0000000..39e3217
--- /dev/null
+++ b/src/tests/telnetsrv.cpp
@@ -0,0 +1,85 @@
1#include "bu/server.h"
2#include "bu/protocoltelnet.h"
3#include "bu/client.h"
4
5class MyTelnet : public Bu::ProtocolTelnet
6{
7public:
8 MyTelnet()
9 {
10 }
11
12 virtual ~MyTelnet()
13 {
14 }
15
16 virtual void onNewConnection( Bu::Client *pClient )
17 {
18 Bu::ProtocolTelnet::onNewConnection( pClient );
19
20 //oNAWS.remoteSet();
21 oEcho.localSet();
22 oSuppressGA.remoteSet( true );
23 oSuppressGA.localSet( true );
24 setCanonical();
25 }
26
27 virtual void onSubNAWS( uint16_t iWidth, uint16_t iHeight )
28 {
29 printf("New dim = (%dx%d)\n", iWidth, iHeight );
30 }
31
32 virtual void gotLine( Bu::FString &sLine )
33 {
34 printf("Line: \"%s\"\n", sLine.getStr() );
35 write("\n\r", 2 );
36 }
37
38private:
39
40};
41
42class TelServer : public Bu::Server
43{
44public:
45 TelServer()
46 {
47 }
48
49 virtual ~TelServer()
50 {
51 }
52
53 virtual void onNewConnection( Bu::Client *pClient, int iPort )
54 {
55 printf("New connection.\n");
56
57 pClient->setProtocol( new MyTelnet() );
58 }
59
60 virtual void onClosedConnection( Bu::Client *pClient )
61 {
62 printf("Lost connection.\n");
63
64 delete pClient->getProtocol();
65 }
66
67private:
68
69};
70
71int main( int argc, char *argv[] )
72{
73 TelServer ts;
74
75 ts.addPort( 4000 );
76 ts.setTimeout( 0, 5000 );
77
78 printf("Initializing server on port: 4000\n");
79
80 for(;;)
81 {
82 ts.scan();
83 }
84}
85