diff options
Diffstat (limited to 'networking/telnet.c')
-rw-r--r-- | networking/telnet.c | 205 |
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 | ||
112 | typedef unsigned char byte; | 112 | typedef unsigned char byte; |
@@ -152,8 +152,10 @@ static void subneg(byte c); | |||
152 | 152 | ||
153 | static void iac_flush(void) | 153 | static 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 | ||
159 | static void doexit(int ev) NORETURN; | 161 | static void doexit(int ev) NORETURN; |
@@ -244,25 +246,34 @@ static void handle_net_output(int len) | |||
244 | 246 | ||
245 | static void handle_net_input(int len) | 247 | static 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 | ||
336 | static void put_iac(int c) | 341 | static 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 | ||
341 | static void put_iac2_merged(unsigned wwdd_and_c) | 352 | static 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 | |||
359 | static 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 | ||
366 | static 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 |
353 | static void put_iac_subopt(byte c, char *str) | 374 | static 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 |
374 | static void put_iac_subopt_autologin(void) | 386 | static 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 |
403 | static void put_iac_naws(byte c, int x, int y) | 403 | static 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 | ||
464 | static void to_notsup(char c) | 454 | static 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 | ||
472 | static void to_echo(void) | 462 | static 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) | |||
540 | static void to_naws(void) | 530 | static 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 | ||