Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/telnet.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/telnet.c
r1765 r2725 9 9 * Last modified: Fri Jun 9 14:34:24 2000 too 10 10 * 11 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.11 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 12 12 * 13 13 * HISTORY … … 22 22 */ 23 23 24 #include <termios.h>25 24 #include <arpa/telnet.h> 26 25 #include <netinet/in.h> … … 44 43 UF_SGA = 0x02, 45 44 46 TS_0 = 1, 45 TS_NORMAL = 0, 46 TS_COPY = 1, 47 47 TS_IAC = 2, 48 48 TS_OPT = 3, 49 49 TS_SUB1 = 4, 50 50 TS_SUB2 = 5, 51 TS_CR = 6, 51 52 }; 52 53 53 54 typedef unsigned char byte; 54 55 56 enum { netfd = 3 }; 57 55 58 struct globals { 56 int netfd; /* console fd:s are 0 and 1 (and 2) */ 57 short iaclen; /* could even use byte */ 59 int iaclen; /* could even use byte, but it's a loss on x86 */ 58 60 byte telstate; /* telnet negotiation state from network input */ 59 61 byte telwish; /* DO, DONT, WILL, WONT */ 60 62 byte charmode; 61 63 byte telflags; 62 byte gotsig;63 64 byte do_termios; 64 65 #if ENABLE_FEATURE_TELNET_TTYPE … … 69 70 #endif 70 71 #if ENABLE_FEATURE_AUTOWIDTH 71 intwin_width, win_height;72 unsigned win_width, win_height; 72 73 #endif 73 74 /* same buffer used both for network and console read/write */ … … 77 78 struct termios termios_def; 78 79 struct termios termios_raw; 79 } ;80 } FIX_ALIASING; 80 81 #define G (*(struct globals*)&bb_common_bufsiz1) 81 void BUG_telnet_globals_too_big(void);82 82 #define INIT_G() do { \ 83 if (sizeof(G) > COMMON_BUFSIZE)\84 BUG_telnet_globals_too_big(); \85 /* memset(&G, 0, sizeof G); - already is */\83 struct G_sizecheck { \ 84 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ 85 }; \ 86 86 } while (0) 87 87 88 /* Function prototypes */ 88 89 89 static void rawmode(void); 90 90 static void cookmode(void); … … 92 92 static void will_charmode(void); 93 93 static void telopt(byte c); 94 static intsubneg(byte c);95 96 static void iac flush(void)97 { 98 write( G.netfd, G.iacbuf, G.iaclen);94 static void subneg(byte c); 95 96 static void iac_flush(void) 97 { 98 write(netfd, G.iacbuf, G.iaclen); 99 99 G.iaclen = 0; 100 100 } … … 102 102 #define write_str(fd, str) write(fd, str, sizeof(str) - 1) 103 103 104 static void doexit(int ev) NORETURN; 104 105 static void doexit(int ev) 105 106 { … … 108 109 } 109 110 110 static void con escape(void)111 static void con_escape(void) 111 112 { 112 113 char b; 113 114 114 if ( G.gotsig) /* came from linemode... go raw */115 if (bb_got_signal) /* came from line mode... go raw */ 115 116 rawmode(); 116 117 … … 121 122 " e exit telnet\r\n"); 122 123 123 if (read( 0, &b, 1) <= 0)124 doexit( 1);124 if (read(STDIN_FILENO, &b, 1) <= 0) 125 doexit(EXIT_FAILURE); 125 126 126 127 switch (b) { 127 128 case 'l': 128 if (! G.gotsig) {129 if (!bb_got_signal) { 129 130 do_linemode(); 130 goto r rturn;131 goto ret; 131 132 } 132 133 break; 133 134 case 'c': 134 if ( G.gotsig) {135 if (bb_got_signal) { 135 136 will_charmode(); 136 goto r rturn;137 goto ret; 137 138 } 138 139 break; … … 143 144 break; 144 145 case 'e': 145 doexit( 0);146 doexit(EXIT_SUCCESS); 146 147 } 147 148 148 149 write_str(1, "continuing...\r\n"); 149 150 150 if ( G.gotsig)151 if (bb_got_signal) 151 152 cookmode(); 152 153 rrturn: 154 G.gotsig = 0; 155 156 } 157 158 static void handlenetoutput(int len) 159 { 160 /* here we could do smart tricks how to handle 0xFF:s in output 161 * stream like writing twice every sequence of FF:s (thus doing 162 * many write()s. But I think interactive telnet application does 163 * not need to be 100% 8-bit clean, so changing every 0xff:s to 164 * 0x7f:s 153 ret: 154 bb_got_signal = 0; 155 } 156 157 static void handle_net_output(int len) 158 { 159 /* here we could do smart tricks how to handle 0xFF:s in output 160 * stream like writing twice every sequence of FF:s (thus doing 161 * many write()s. But I think interactive telnet application does 162 * not need to be 100% 8-bit clean, so changing every 0xff:s to 163 * 0x7f:s 165 164 * 166 * 167 * 168 * 169 * 170 * 171 * third - whay doyyou have to make 'many write()s'?172 * 173 * So I implemented it. It's realy useful for me. I hope that174 * others people will find it interesting to.165 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com) 166 * I don't agree. 167 * first - I cannot use programs like sz/rz 168 * second - the 0x0D is sent as one character and if the next 169 * char is 0x0A then it's eaten by a server side. 170 * third - why do you have to make 'many write()s'? 171 * I don't understand. 172 * So I implemented it. It's really useful for me. I hope that 173 * other people will find it interesting too. 175 174 */ 176 177 int i, j; 178 byte * p = (byte*)G.buf; 179 byte outbuf[4*DATABUFSIZE]; 180 181 for (i = len, j = 0; i > 0; i--, p++) 182 { 183 if (*p == 0x1d) 184 { 185 conescape(); 175 byte outbuf[2 * DATABUFSIZE]; 176 byte *p = (byte*)G.buf; 177 int j = 0; 178 179 for (; len > 0; len--, p++) { 180 byte c = *p; 181 if (c == 0x1d) { 182 con_escape(); 186 183 return; 187 184 } 188 outbuf[j++] = *p;189 if ( *p == 0xff)190 outbuf[j++] = 0xff;191 else if ( *p == 0x0d)192 outbuf[j++] = 0x00;185 outbuf[j++] = c; 186 if (c == IAC) 187 outbuf[j++] = c; /* IAC -> IAC IAC */ 188 else if (c == '\r') 189 outbuf[j++] = '\0'; /* CR -> CR NUL */ 193 190 } 194 191 if (j > 0) 195 write(G.netfd, outbuf, j);196 } 197 198 static void handle netinput(int len)192 full_write(netfd, outbuf, j); 193 } 194 195 static void handle_net_input(int len) 199 196 { 200 197 int i; 201 198 int cstart = 0; 202 199 203 for (i = 0; i < len; i++) 204 { 200 for (i = 0; i < len; i++) { 205 201 byte c = G.buf[i]; 206 202 207 if (G.telstate == 0) /* most of the time state == 0 */ 208 { 209 if (c == IAC) 210 { 203 if (G.telstate == TS_NORMAL) { /* most typical state */ 204 if (c == IAC) { 211 205 cstart = i; 212 206 G.telstate = TS_IAC; 213 207 } 214 } 215 else 216 switch (G.telstate) 217 { 218 case TS_0: 219 if (c == IAC) 220 G.telstate = TS_IAC; 221 else 222 G.buf[cstart++] = c; 223 break; 224 225 case TS_IAC: 226 if (c == IAC) /* IAC IAC -> 0xFF */ 227 { 228 G.buf[cstart++] = c; 229 G.telstate = TS_0; 230 break; 231 } 232 /* else */ 233 switch (c) 234 { 235 case SB: 236 G.telstate = TS_SUB1; 237 break; 238 case DO: 239 case DONT: 240 case WILL: 241 case WONT: 242 G.telwish = c; 243 G.telstate = TS_OPT; 244 break; 245 default: 246 G.telstate = TS_0; /* DATA MARK must be added later */ 247 } 248 break; 249 case TS_OPT: /* WILL, WONT, DO, DONT */ 250 telopt(c); 251 G.telstate = TS_0; 252 break; 253 case TS_SUB1: /* Subnegotiation */ 254 case TS_SUB2: /* Subnegotiation */ 255 if (subneg(c)) 256 G.telstate = TS_0; 257 break; 258 } 259 } 260 if (G.telstate) 261 { 262 if (G.iaclen) iacflush(); 263 if (G.telstate == TS_0) G.telstate = 0; 264 208 else if (c == '\r') { 209 cstart = i + 1; 210 G.telstate = TS_CR; 211 } 212 /* No IACs were seen so far, no need to copy 213 * bytes within G.buf: */ 214 continue; 215 } 216 217 switch (G.telstate) { 218 case TS_CR: 219 /* Prev char was CR. If cur one is NUL, ignore it. 220 * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling. 221 */ 222 G.telstate = TS_COPY; 223 if (c == '\0') 224 break; 225 /* else: fall through - need to handle CR IAC ... properly */ 226 227 case TS_COPY: /* Prev char was ordinary */ 228 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */ 229 if (c == IAC) 230 G.telstate = TS_IAC; 231 else 232 G.buf[cstart++] = c; 233 if (c == '\r') 234 G.telstate = TS_CR; 235 break; 236 237 case TS_IAC: /* Prev char was IAC */ 238 if (c == IAC) { /* IAC IAC -> one IAC */ 239 G.buf[cstart++] = c; 240 G.telstate = TS_COPY; 241 break; 242 } 243 /* else */ 244 switch (c) { 245 case SB: 246 G.telstate = TS_SUB1; 247 break; 248 case DO: 249 case DONT: 250 case WILL: 251 case WONT: 252 G.telwish = c; 253 G.telstate = TS_OPT; 254 break; 255 /* DATA MARK must be added later */ 256 default: 257 G.telstate = TS_COPY; 258 } 259 break; 260 261 case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */ 262 telopt(c); 263 G.telstate = TS_COPY; 264 break; 265 266 case TS_SUB1: /* Subnegotiation */ 267 case TS_SUB2: /* Subnegotiation */ 268 subneg(c); /* can change G.telstate */ 269 break; 270 } 271 } 272 273 if (G.telstate != TS_NORMAL) { 274 /* We had some IACs, or CR */ 275 if (G.iaclen) 276 iac_flush(); 277 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */ 278 G.telstate = TS_NORMAL; 265 279 len = cstart; 266 280 } 267 281 268 282 if (len) 269 write(1, G.buf, len);270 } 271 272 static void put iac(int c)283 full_write(STDOUT_FILENO, G.buf, len); 284 } 285 286 static void put_iac(int c) 273 287 { 274 288 G.iacbuf[G.iaclen++] = c; 275 289 } 276 290 277 static void put iac2(byte wwdd, byte c)291 static void put_iac2(byte wwdd, byte c) 278 292 { 279 293 if (G.iaclen + 3 > IACBUFSIZE) 280 iac flush();281 282 put iac(IAC);283 put iac(wwdd);284 put iac(c);294 iac_flush(); 295 296 put_iac(IAC); 297 put_iac(wwdd); 298 put_iac(c); 285 299 } 286 300 287 301 #if ENABLE_FEATURE_TELNET_TTYPE 288 static void put iac_subopt(byte c, char *str)289 { 290 int 302 static void put_iac_subopt(byte c, char *str) 303 { 304 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) 291 305 292 306 if (G.iaclen + len > IACBUFSIZE) 293 iac flush();294 295 put iac(IAC);296 put iac(SB);297 put iac(c);298 put iac(0);307 iac_flush(); 308 309 put_iac(IAC); 310 put_iac(SB); 311 put_iac(c); 312 put_iac(0); 299 313 300 314 while (*str) 301 put iac(*str++);302 303 put iac(IAC);304 put iac(SE);315 put_iac(*str++); 316 317 put_iac(IAC); 318 put_iac(SE); 305 319 } 306 320 #endif 307 321 308 322 #if ENABLE_FEATURE_TELNET_AUTOLOGIN 309 static void put iac_subopt_autologin(void)323 static void put_iac_subopt_autologin(void) 310 324 { 311 325 int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2) 312 const char * user= "USER";326 const char *p = "USER"; 313 327 314 328 if (G.iaclen + len > IACBUFSIZE) 315 iacflush(); 316 317 putiac(IAC); 318 putiac(SB); 319 putiac(TELOPT_NEW_ENVIRON); 320 putiac(TELQUAL_IS); 321 putiac(NEW_ENV_VAR); 322 323 while (*user) 324 putiac(*user++); 325 326 putiac(NEW_ENV_VALUE); 327 328 while (*G.autologin) 329 putiac(*G.autologin++); 330 331 putiac(IAC); 332 putiac(SE); 329 iac_flush(); 330 331 put_iac(IAC); 332 put_iac(SB); 333 put_iac(TELOPT_NEW_ENVIRON); 334 put_iac(TELQUAL_IS); 335 put_iac(NEW_ENV_VAR); 336 337 while (*p) 338 put_iac(*p++); 339 340 put_iac(NEW_ENV_VALUE); 341 342 p = G.autologin; 343 while (*p) 344 put_iac(*p++); 345 346 put_iac(IAC); 347 put_iac(SE); 333 348 } 334 349 #endif 335 350 336 351 #if ENABLE_FEATURE_AUTOWIDTH 337 static void put iac_naws(byte c, int x, int y)352 static void put_iac_naws(byte c, int x, int y) 338 353 { 339 354 if (G.iaclen + 9 > IACBUFSIZE) 340 iac flush();341 342 put iac(IAC);343 put iac(SB);344 put iac(c);345 346 put iac((x >> 8) & 0xff);347 put iac(x & 0xff);348 put iac((y >> 8) & 0xff);349 put iac(y & 0xff);350 351 put iac(IAC);352 put iac(SE);355 iac_flush(); 356 357 put_iac(IAC); 358 put_iac(SB); 359 put_iac(c); 360 361 put_iac((x >> 8) & 0xff); 362 put_iac(x & 0xff); 363 put_iac((y >> 8) & 0xff); 364 put_iac(y & 0xff); 365 366 put_iac(IAC); 367 put_iac(SE); 353 368 } 354 369 #endif … … 379 394 setConMode(); 380 395 381 put iac2(DO, TELOPT_ECHO);382 put iac2(DO, TELOPT_SGA);383 iac flush();396 put_iac2(DO, TELOPT_ECHO); 397 put_iac2(DO, TELOPT_SGA); 398 iac_flush(); 384 399 } 385 400 … … 390 405 setConMode(); 391 406 392 put iac2(DONT, TELOPT_ECHO);393 put iac2(DONT, TELOPT_SGA);394 iac flush();407 put_iac2(DONT, TELOPT_ECHO); 408 put_iac2(DONT, TELOPT_SGA); 409 iac_flush(); 395 410 } 396 411 … … 398 413 { 399 414 if (G.telwish == WILL) 400 put iac2(DONT, c);415 put_iac2(DONT, c); 401 416 else if (G.telwish == DO) 402 put iac2(WONT, c);417 put_iac2(WONT, c); 403 418 } 404 419 … … 407 422 /* if server requests ECHO, don't agree */ 408 423 if (G.telwish == DO) { 409 put iac2(WONT, TELOPT_ECHO);424 put_iac2(WONT, TELOPT_ECHO); 410 425 return; 411 426 } … … 423 438 424 439 if (G.telflags & UF_ECHO) 425 put iac2(DO, TELOPT_ECHO);440 put_iac2(DO, TELOPT_ECHO); 426 441 else 427 put iac2(DONT, TELOPT_ECHO);442 put_iac2(DONT, TELOPT_ECHO); 428 443 429 444 setConMode(); 430 write_str(1,"\r\n"); /* sudden modec */445 full_write1_str("\r\n"); /* sudden modec */ 431 446 } 432 447 … … 441 456 return; 442 457 443 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */ 444 putiac2(DO, TELOPT_SGA); 458 G.telflags ^= UF_SGA; /* toggle */ 459 if (G.telflags & UF_SGA) 460 put_iac2(DO, TELOPT_SGA); 445 461 else 446 put iac2(DONT, TELOPT_SGA);462 put_iac2(DONT, TELOPT_SGA); 447 463 } 448 464 … … 451 467 { 452 468 /* Tell server we will (or won't) do TTYPE */ 453 454 469 if (G.ttype) 455 put iac2(WILL, TELOPT_TTYPE);470 put_iac2(WILL, TELOPT_TTYPE); 456 471 else 457 put iac2(WONT, TELOPT_TTYPE);472 put_iac2(WONT, TELOPT_TTYPE); 458 473 } 459 474 #endif … … 463 478 { 464 479 /* Tell server we will (or will not) do AUTOLOGIN */ 465 466 480 if (G.autologin) 467 put iac2(WILL, TELOPT_NEW_ENVIRON);481 put_iac2(WILL, TELOPT_NEW_ENVIRON); 468 482 else 469 put iac2(WONT, TELOPT_NEW_ENVIRON);483 put_iac2(WONT, TELOPT_NEW_ENVIRON); 470 484 } 471 485 #endif … … 475 489 { 476 490 /* Tell server we will do NAWS */ 477 put iac2(WILL, TELOPT_NAWS);491 put_iac2(WILL, TELOPT_NAWS); 478 492 } 479 493 #endif … … 497 511 case TELOPT_NAWS: 498 512 to_naws(); 499 put iac_naws(c, G.win_width, G.win_height);513 put_iac_naws(c, G.win_width, G.win_height); 500 514 break; 501 515 #endif … … 507 521 508 522 /* subnegotiation -- ignore all (except TTYPE,NAWS) */ 509 static intsubneg(byte c)523 static void subneg(byte c) 510 524 { 511 525 switch (G.telstate) { … … 515 529 #if ENABLE_FEATURE_TELNET_TTYPE 516 530 else 517 if (c == TELOPT_TTYPE )518 put iac_subopt(TELOPT_TTYPE, G.ttype);531 if (c == TELOPT_TTYPE && G.ttype) 532 put_iac_subopt(TELOPT_TTYPE, G.ttype); 519 533 #endif 520 534 #if ENABLE_FEATURE_TELNET_AUTOLOGIN 521 535 else 522 if (c == TELOPT_NEW_ENVIRON )523 put iac_subopt_autologin();536 if (c == TELOPT_NEW_ENVIRON && G.autologin) 537 put_iac_subopt_autologin(); 524 538 #endif 525 539 break; 526 540 case TS_SUB2: 527 if (c == SE) 528 return TRUE; 541 if (c == SE) { 542 G.telstate = TS_COPY; 543 return; 544 } 529 545 G.telstate = TS_SUB1; 530 /* break; */ 531 } 532 return FALSE; 533 } 534 535 static void fgotsig(int sig) 536 { 537 G.gotsig = sig; 538 } 539 546 break; 547 } 548 } 540 549 541 550 static void rawmode(void) … … 551 560 } 552 561 553 int telnet_main(int argc, char ** argv);554 int telnet_main(int argc , char**argv)562 int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 563 int telnet_main(int argc UNUSED_PARAM, char **argv) 555 564 { 556 565 char *host; 557 566 int port; 558 567 int len; 559 #ifdef USE_POLL560 568 struct pollfd ufds[2]; 561 #else562 fd_set readfds;563 int maxfd;564 #endif565 569 566 570 INIT_G(); … … 579 583 cfmakeraw(&G.termios_raw); 580 584 } 581 582 if (argc < 2)583 bb_show_usage();584 585 585 586 #if ENABLE_FEATURE_TELNET_AUTOLOGIN … … 597 598 bb_show_usage(); 598 599 599 G.netfd = create_and_connect_stream_or_die(host, port); 600 601 setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); 602 603 signal(SIGINT, fgotsig); 604 605 #ifdef USE_POLL 606 ufds[0].fd = 0; ufds[1].fd = G.netfd; 607 ufds[0].events = ufds[1].events = POLLIN; 608 #else 609 FD_ZERO(&readfds); 610 FD_SET(0, &readfds); 611 FD_SET(G.netfd, &readfds); 612 maxfd = G.netfd + 1; 613 #endif 600 xmove_fd(create_and_connect_stream_or_die(host, port), netfd); 601 602 setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); 603 604 signal(SIGINT, record_signo); 605 606 ufds[0].fd = STDIN_FILENO; 607 ufds[0].events = POLLIN; 608 ufds[1].fd = netfd; 609 ufds[1].events = POLLIN; 614 610 615 611 while (1) { 616 #ifndef USE_POLL 617 fd_set rfds = readfds; 618 619 switch (select(maxfd, &rfds, NULL, NULL, NULL)) 620 #else 621 switch (poll(ufds, 2, -1)) 622 #endif 623 { 624 case 0: 625 /* timeout */ 626 case -1: 612 if (poll(ufds, 2, -1) < 0) { 627 613 /* error, ignore and/or log something, bay go to loop */ 628 if ( G.gotsig)629 con escape();614 if (bb_got_signal) 615 con_escape(); 630 616 else 631 617 sleep(1); 632 break; 633 default: 634 635 #ifdef USE_POLL 636 if (ufds[0].revents) /* well, should check POLLIN, but ... */ 637 #else 638 if (FD_ISSET(0, &rfds)) 639 #endif 640 { 641 len = read(0, G.buf, DATABUFSIZE); 642 if (len <= 0) 643 doexit(0); 644 TRACE(0, ("Read con: %d\n", len)); 645 handlenetoutput(len); 618 continue; 619 } 620 621 // FIXME: reads can block. Need full bidirectional buffering. 622 623 if (ufds[0].revents) { 624 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); 625 if (len <= 0) 626 doexit(EXIT_SUCCESS); 627 TRACE(0, ("Read con: %d\n", len)); 628 handle_net_output(len); 629 } 630 631 if (ufds[1].revents) { 632 len = safe_read(netfd, G.buf, DATABUFSIZE); 633 if (len <= 0) { 634 full_write1_str("Connection closed by foreign host\r\n"); 635 doexit(EXIT_FAILURE); 646 636 } 647 648 #ifdef USE_POLL 649 if (ufds[1].revents) /* well, should check POLLIN, but ... */ 650 #else 651 if (FD_ISSET(G.netfd, &rfds)) 652 #endif 653 { 654 len = read(G.netfd, G.buf, DATABUFSIZE); 655 if (len <= 0) { 656 write_str(1, "Connection closed by foreign host\r\n"); 657 doexit(1); 658 } 659 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); 660 handlenetinput(len); 661 } 662 } 663 } 664 } 637 TRACE(0, ("Read netfd (%d): %d\n", netfd, len)); 638 handle_net_input(len); 639 } 640 } /* while (1) */ 641 }
Note:
See TracChangeset
for help on using the changeset viewer.