aboutsummaryrefslogtreecommitdiff
path: root/networking/telnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/telnet.c')
-rw-r--r--networking/telnet.c205
1 files changed, 98 insertions, 107 deletions
diff --git a/networking/telnet.c b/networking/telnet.c
index 1e6be85bd..fa1628723 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -20,7 +20,7 @@
20 * by Fernando Silveira <swrh@gmx.net> 20 * by Fernando Silveira <swrh@gmx.net>
21 */ 21 */
22//config:config TELNET 22//config:config TELNET
23//config: bool "telnet (8.7 kb)" 23//config: bool "telnet (8.8 kb)"
24//config: default y 24//config: default y
25//config: help 25//config: help
26//config: Telnet is an interface to the TELNET protocol, but is also commonly 26//config: Telnet is an interface to the TELNET protocol, but is also commonly
@@ -94,19 +94,19 @@ enum {
94 IACBUFSIZE = 128, 94 IACBUFSIZE = 128,
95 95
96 CHM_TRY = 0, 96 CHM_TRY = 0,
97 CHM_ON = 1, 97 CHM_ON = 1,
98 CHM_OFF = 2, 98 CHM_OFF = 2,
99 99
100 UF_ECHO = 0x01, 100 UF_ECHO = 0x01,
101 UF_SGA = 0x02, 101 UF_SGA = 0x02,
102 102
103 TS_NORMAL = 0, 103 TS_NORMAL = 0,
104 TS_COPY = 1, 104 TS_COPY = 1,
105 TS_IAC = 2, 105 TS_IAC = 2,
106 TS_OPT = 3, 106 TS_OPT = 3,
107 TS_SUB1 = 4, 107 TS_SUB1 = 4,
108 TS_SUB2 = 5, 108 TS_SUB2 = 5,
109 TS_CR = 6, 109 TS_CR = 6,
110}; 110};
111 111
112typedef unsigned char byte; 112typedef unsigned char byte;
@@ -152,8 +152,10 @@ static void subneg(byte c);
152 152
153static void iac_flush(void) 153static void iac_flush(void)
154{ 154{
155 full_write(netfd, G.iacbuf, G.iaclen); 155 if (G.iaclen != 0) {
156 G.iaclen = 0; 156 full_write(netfd, G.iacbuf, G.iaclen);
157 G.iaclen = 0;
158 }
157} 159}
158 160
159static void doexit(int ev) NORETURN; 161static void doexit(int ev) NORETURN;
@@ -244,25 +246,34 @@ static void handle_net_output(int len)
244 246
245static void handle_net_input(int len) 247static void handle_net_input(int len)
246{ 248{
249 byte c;
247 int i; 250 int i;
248 int cstart = 0; 251 int cstart = cstart; /* for compiler */
249 252
250 for (i = 0; i < len; i++) { 253 i = 0;
251 byte c = G.buf[i]; 254 //bb_error_msg("[%u,'%.*s']", G.telstate, len, G.buf);
252 255 if (G.telstate == TS_NORMAL) { /* most typical state */
253 if (G.telstate == TS_NORMAL) { /* most typical state */ 256 while (i < len) {
254 if (c == IAC) { 257 c = G.buf[i];
255 cstart = i; 258 i++;
256 G.telstate = TS_IAC; 259 if (c == IAC) /* unlikely */
257 } 260 goto got_IAC;
258 else if (c == '\r') { 261 if (c != '\r') /* likely */
259 cstart = i + 1; 262 continue;
260 G.telstate = TS_CR; 263 G.telstate = TS_CR;
261 } 264 cstart = i;
262 /* No IACs were seen so far, no need to copy 265 goto got_special;
263 * bytes within G.buf: */
264 continue;
265 } 266 }
267 full_write(STDOUT_FILENO, G.buf, len);
268 return;
269 got_IAC:
270 G.telstate = TS_IAC;
271 cstart = i - 1;
272 got_special: ;
273 }
274
275 for (; i < len; i++) {
276 c = G.buf[i];
266 277
267 switch (G.telstate) { 278 switch (G.telstate) {
268 case TS_CR: 279 case TS_CR:
@@ -278,20 +289,19 @@ static void handle_net_input(int len)
278 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */ 289 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */
279 if (c == IAC) 290 if (c == IAC)
280 G.telstate = TS_IAC; 291 G.telstate = TS_IAC;
281 else 292 else {
282 G.buf[cstart++] = c; 293 G.buf[cstart++] = c;
283 if (c == '\r') 294 if (c == '\r')
284 G.telstate = TS_CR; 295 G.telstate = TS_CR;
296 }
285 break; 297 break;
286 298
287 case TS_IAC: /* Prev char was IAC */ 299 case TS_IAC: /* Prev char was IAC */
288 if (c == IAC) { /* IAC IAC -> one IAC */ 300 switch (c) {
301 case IAC: /* IAC IAC -> one IAC */
289 G.buf[cstart++] = c; 302 G.buf[cstart++] = c;
290 G.telstate = TS_COPY; 303 G.telstate = TS_COPY;
291 break; 304 break;
292 }
293 /* else */
294 switch (c) {
295 case SB: 305 case SB:
296 G.telstate = TS_SUB1; 306 G.telstate = TS_SUB1;
297 break; 307 break;
@@ -320,103 +330,83 @@ static void handle_net_input(int len)
320 } 330 }
321 } 331 }
322 332
323 if (G.telstate != TS_NORMAL) { 333 /* We had some IACs, or CR */
324 /* We had some IACs, or CR */ 334 iac_flush();
325 if (G.iaclen) 335 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
326 iac_flush(); 336 G.telstate = TS_NORMAL;
327 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */ 337 if (cstart != 0)
328 G.telstate = TS_NORMAL; 338 full_write(STDOUT_FILENO, G.buf, cstart);
329 len = cstart;
330 }
331
332 if (len)
333 full_write(STDOUT_FILENO, G.buf, len);
334} 339}
335 340
336static void put_iac(int c) 341static void put_iac(int c)
337{ 342{
338 G.iacbuf[G.iaclen++] = c; 343 int iaclen = G.iaclen;
344 if (iaclen >= IACBUFSIZE) {
345 iac_flush();
346 iaclen = 0;
347 }
348 G.iacbuf[iaclen] = c; /* "... & 0xff" is implicit */
349 G.iaclen = iaclen + 1;
339} 350}
340 351
341static void put_iac2_merged(unsigned wwdd_and_c) 352static void put_iac2_msb_lsb(unsigned x_y)
342{ 353{
343 if (G.iaclen + 3 > IACBUFSIZE) 354 put_iac(x_y >> 8); /* "... & 0xff" is implicit */
344 iac_flush(); 355 put_iac(x_y); /* "... & 0xff" is implicit */
356}
357#define put_iac2_x_y(x,y) put_iac2_msb_lsb(((x)<<8) + (y))
358
359static void put_iac4_msb_lsb(unsigned x_y_z_t)
360{
361 put_iac2_msb_lsb(x_y_z_t >> 16);
362 put_iac2_msb_lsb(x_y_z_t); /* "... & 0xffff" is implicit */
363}
364#define put_iac4_x_y_z_t(x,y,z,t) put_iac4_msb_lsb(((x)<<24) + ((y)<<16) + ((z)<<8) + (t))
345 365
366static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c)
367{
346 put_iac(IAC); 368 put_iac(IAC);
347 put_iac(wwdd_and_c >> 8); 369 put_iac2_msb_lsb(wwdd_and_c);
348 put_iac(wwdd_and_c & 0xff);
349} 370}
350#define put_iac2(wwdd,c) put_iac2_merged(((wwdd)<<8) + (c)) 371#define put_iac3_IAC_x_y(wwdd,c) put_iac3_IAC_x_y_merged(((wwdd)<<8) + (c))
351 372
352#if ENABLE_FEATURE_TELNET_TTYPE 373#if ENABLE_FEATURE_TELNET_TTYPE
353static void put_iac_subopt(byte c, char *str) 374static void put_iac_subopt(byte c, char *str)
354{ 375{
355 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) 376 put_iac4_x_y_z_t(IAC, SB, c, 0);
356
357 if (G.iaclen + len > IACBUFSIZE)
358 iac_flush();
359
360 put_iac(IAC);
361 put_iac(SB);
362 put_iac(c);
363 put_iac(0);
364 377
365 while (*str) 378 while (*str)
366 put_iac(*str++); 379 put_iac(*str++);
367 380
368 put_iac(IAC); 381 put_iac2_x_y(IAC, SE);
369 put_iac(SE);
370} 382}
371#endif 383#endif
372 384
373#if ENABLE_FEATURE_TELNET_AUTOLOGIN 385#if ENABLE_FEATURE_TELNET_AUTOLOGIN
374static void put_iac_subopt_autologin(void) 386static void put_iac_subopt_autologin(void)
375{ 387{
376 int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2) 388 const char *p;
377 const char *p = "USER";
378
379 if (G.iaclen + len > IACBUFSIZE)
380 iac_flush();
381
382 put_iac(IAC);
383 put_iac(SB);
384 put_iac(TELOPT_NEW_ENVIRON);
385 put_iac(TELQUAL_IS);
386 put_iac(NEW_ENV_VAR);
387
388 while (*p)
389 put_iac(*p++);
390 389
391 put_iac(NEW_ENV_VALUE); 390 put_iac4_x_y_z_t(IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS);
391 put_iac4_x_y_z_t(NEW_ENV_VAR, 'U', 'S', 'E'); /* "USER" */
392 put_iac2_x_y('R', NEW_ENV_VALUE);
392 393
393 p = G.autologin; 394 p = G.autologin;
394 while (*p) 395 while (*p)
395 put_iac(*p++); 396 put_iac(*p++);
396 397
397 put_iac(IAC); 398 put_iac2_x_y(IAC, SE);
398 put_iac(SE);
399} 399}
400#endif 400#endif
401 401
402#if ENABLE_FEATURE_TELNET_WIDTH 402#if ENABLE_FEATURE_TELNET_WIDTH
403static void put_iac_naws(byte c, int x, int y) 403static void put_iac_naws(byte c, int x, int y)
404{ 404{
405 if (G.iaclen + 9 > IACBUFSIZE) 405 put_iac3_IAC_x_y(SB, c);
406 iac_flush();
407 406
408 put_iac(IAC); 407 put_iac4_msb_lsb((x << 16) + y);
409 put_iac(SB);
410 put_iac(c);
411 408
412 /* "... & 0xff" implicitly done below */ 409 put_iac2_x_y(IAC, SE);
413 put_iac(x >> 8);
414 put_iac(x);
415 put_iac(y >> 8);
416 put_iac(y);
417
418 put_iac(IAC);
419 put_iac(SE);
420} 410}
421#endif 411#endif
422 412
@@ -445,8 +435,8 @@ static void will_charmode(void)
445 G.telflags |= (UF_ECHO | UF_SGA); 435 G.telflags |= (UF_ECHO | UF_SGA);
446 setConMode(); 436 setConMode();
447 437
448 put_iac2(DO, TELOPT_ECHO); 438 put_iac3_IAC_x_y(DO, TELOPT_ECHO);
449 put_iac2(DO, TELOPT_SGA); 439 put_iac3_IAC_x_y(DO, TELOPT_SGA);
450 iac_flush(); 440 iac_flush();
451} 441}
452 442
@@ -456,24 +446,24 @@ static void do_linemode(void)
456 G.telflags &= ~(UF_ECHO | UF_SGA); 446 G.telflags &= ~(UF_ECHO | UF_SGA);
457 setConMode(); 447 setConMode();
458 448
459 put_iac2(DONT, TELOPT_ECHO); 449 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
460 put_iac2(DONT, TELOPT_SGA); 450 put_iac3_IAC_x_y(DONT, TELOPT_SGA);
461 iac_flush(); 451 iac_flush();
462} 452}
463 453
464static void to_notsup(char c) 454static void to_notsup(char c)
465{ 455{
466 if (G.telwish == WILL) 456 if (G.telwish == WILL)
467 put_iac2(DONT, c); 457 put_iac3_IAC_x_y(DONT, c);
468 else if (G.telwish == DO) 458 else if (G.telwish == DO)
469 put_iac2(WONT, c); 459 put_iac3_IAC_x_y(WONT, c);
470} 460}
471 461
472static void to_echo(void) 462static void to_echo(void)
473{ 463{
474 /* if server requests ECHO, don't agree */ 464 /* if server requests ECHO, don't agree */
475 if (G.telwish == DO) { 465 if (G.telwish == DO) {
476 put_iac2(WONT, TELOPT_ECHO); 466 put_iac3_IAC_x_y(WONT, TELOPT_ECHO);
477 return; 467 return;
478 } 468 }
479 if (G.telwish == DONT) 469 if (G.telwish == DONT)
@@ -489,9 +479,9 @@ static void to_echo(void)
489 G.telflags ^= UF_ECHO; 479 G.telflags ^= UF_ECHO;
490 480
491 if (G.telflags & UF_ECHO) 481 if (G.telflags & UF_ECHO)
492 put_iac2(DO, TELOPT_ECHO); 482 put_iac3_IAC_x_y(DO, TELOPT_ECHO);
493 else 483 else
494 put_iac2(DONT, TELOPT_ECHO); 484 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
495 485
496 setConMode(); 486 setConMode();
497 full_write1_str("\r\n"); /* sudden modec */ 487 full_write1_str("\r\n"); /* sudden modec */
@@ -509,9 +499,9 @@ static void to_sga(void)
509 499
510 G.telflags ^= UF_SGA; /* toggle */ 500 G.telflags ^= UF_SGA; /* toggle */
511 if (G.telflags & UF_SGA) 501 if (G.telflags & UF_SGA)
512 put_iac2(DO, TELOPT_SGA); 502 put_iac3_IAC_x_y(DO, TELOPT_SGA);
513 else 503 else
514 put_iac2(DONT, TELOPT_SGA); 504 put_iac3_IAC_x_y(DONT, TELOPT_SGA);
515} 505}
516 506
517#if ENABLE_FEATURE_TELNET_TTYPE 507#if ENABLE_FEATURE_TELNET_TTYPE
@@ -519,9 +509,9 @@ static void to_ttype(void)
519{ 509{
520 /* Tell server we will (or won't) do TTYPE */ 510 /* Tell server we will (or won't) do TTYPE */
521 if (G.ttype) 511 if (G.ttype)
522 put_iac2(WILL, TELOPT_TTYPE); 512 put_iac3_IAC_x_y(WILL, TELOPT_TTYPE);
523 else 513 else
524 put_iac2(WONT, TELOPT_TTYPE); 514 put_iac3_IAC_x_y(WONT, TELOPT_TTYPE);
525} 515}
526#endif 516#endif
527 517
@@ -530,9 +520,9 @@ static void to_new_environ(void)
530{ 520{
531 /* Tell server we will (or will not) do AUTOLOGIN */ 521 /* Tell server we will (or will not) do AUTOLOGIN */
532 if (G.autologin) 522 if (G.autologin)
533 put_iac2(WILL, TELOPT_NEW_ENVIRON); 523 put_iac3_IAC_x_y(WILL, TELOPT_NEW_ENVIRON);
534 else 524 else
535 put_iac2(WONT, TELOPT_NEW_ENVIRON); 525 put_iac3_IAC_x_y(WONT, TELOPT_NEW_ENVIRON);
536} 526}
537#endif 527#endif
538 528
@@ -540,7 +530,7 @@ static void to_new_environ(void)
540static void to_naws(void) 530static void to_naws(void)
541{ 531{
542 /* Tell server we will do NAWS */ 532 /* Tell server we will do NAWS */
543 put_iac2(WILL, TELOPT_NAWS); 533 put_iac3_IAC_x_y(WILL, TELOPT_NAWS);
544} 534}
545#endif 535#endif
546 536
@@ -649,6 +639,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
649 bb_show_usage(); 639 bb_show_usage();
650 640
651 xmove_fd(create_and_connect_stream_or_die(host, port), netfd); 641 xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
642 printf("Connected to %s\n", host);
652 643
653 setsockopt_keepalive(netfd); 644 setsockopt_keepalive(netfd);
654 645