telnet.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * telnet implementation for busybox
  4. *
  5. * Author: Tomi Ollila <too@iki.fi>
  6. * Copyright (C) 1994-2000 by Tomi Ollila
  7. *
  8. * Created: Thu Apr 7 13:29:41 1994 too
  9. * Last modified: Fri Jun 9 14:34:24 2000 too
  10. *
  11. * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  12. *
  13. * HISTORY
  14. * Revision 3.1 1994/04/17 11:31:54 too
  15. * initial revision
  16. * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
  17. * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
  18. * <jam@ltsp.org>
  19. * Modified 2004/02/11 to add ability to pass the USER variable to remote host
  20. * by Fernando Silveira <swrh@gmx.net>
  21. *
  22. */
  23. #include <arpa/telnet.h>
  24. #include <netinet/in.h>
  25. #include "libbb.h"
  26. #ifdef DOTRACE
  27. #define TRACE(x, y) do { if (x) printf y; } while (0)
  28. #else
  29. #define TRACE(x, y)
  30. #endif
  31. enum {
  32. DATABUFSIZE = 128,
  33. IACBUFSIZE = 128,
  34. CHM_TRY = 0,
  35. CHM_ON = 1,
  36. CHM_OFF = 2,
  37. UF_ECHO = 0x01,
  38. UF_SGA = 0x02,
  39. TS_0 = 1,
  40. TS_IAC = 2,
  41. TS_OPT = 3,
  42. TS_SUB1 = 4,
  43. TS_SUB2 = 5,
  44. };
  45. typedef unsigned char byte;
  46. enum { netfd = 3 };
  47. struct globals {
  48. int iaclen; /* could even use byte, but it's a loss on x86 */
  49. byte telstate; /* telnet negotiation state from network input */
  50. byte telwish; /* DO, DONT, WILL, WONT */
  51. byte charmode;
  52. byte telflags;
  53. byte do_termios;
  54. #if ENABLE_FEATURE_TELNET_TTYPE
  55. char *ttype;
  56. #endif
  57. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  58. const char *autologin;
  59. #endif
  60. #if ENABLE_FEATURE_AUTOWIDTH
  61. unsigned win_width, win_height;
  62. #endif
  63. /* same buffer used both for network and console read/write */
  64. char buf[DATABUFSIZE];
  65. /* buffer to handle telnet negotiations */
  66. char iacbuf[IACBUFSIZE];
  67. struct termios termios_def;
  68. struct termios termios_raw;
  69. };
  70. #define G (*(struct globals*)&bb_common_bufsiz1)
  71. void BUG_telnet_globals_too_big(void);
  72. #define INIT_G() do { \
  73. if (sizeof(G) > COMMON_BUFSIZE) \
  74. BUG_telnet_globals_too_big(); \
  75. /* memset(&G, 0, sizeof G); - already is */ \
  76. } while (0)
  77. /* Function prototypes */
  78. static void rawmode(void);
  79. static void cookmode(void);
  80. static void do_linemode(void);
  81. static void will_charmode(void);
  82. static void telopt(byte c);
  83. static int subneg(byte c);
  84. static void iac_flush(void)
  85. {
  86. write(netfd, G.iacbuf, G.iaclen);
  87. G.iaclen = 0;
  88. }
  89. #define write_str(fd, str) write(fd, str, sizeof(str) - 1)
  90. static void doexit(int ev) NORETURN;
  91. static void doexit(int ev)
  92. {
  93. cookmode();
  94. exit(ev);
  95. }
  96. static void con_escape(void)
  97. {
  98. char b;
  99. if (bb_got_signal) /* came from line mode... go raw */
  100. rawmode();
  101. write_str(1, "\r\nConsole escape. Commands are:\r\n\n"
  102. " l go to line mode\r\n"
  103. " c go to character mode\r\n"
  104. " z suspend telnet\r\n"
  105. " e exit telnet\r\n");
  106. if (read(STDIN_FILENO, &b, 1) <= 0)
  107. doexit(EXIT_FAILURE);
  108. switch (b) {
  109. case 'l':
  110. if (!bb_got_signal) {
  111. do_linemode();
  112. goto ret;
  113. }
  114. break;
  115. case 'c':
  116. if (bb_got_signal) {
  117. will_charmode();
  118. goto ret;
  119. }
  120. break;
  121. case 'z':
  122. cookmode();
  123. kill(0, SIGTSTP);
  124. rawmode();
  125. break;
  126. case 'e':
  127. doexit(EXIT_SUCCESS);
  128. }
  129. write_str(1, "continuing...\r\n");
  130. if (bb_got_signal)
  131. cookmode();
  132. ret:
  133. bb_got_signal = 0;
  134. }
  135. static void handle_net_output(int len)
  136. {
  137. /* here we could do smart tricks how to handle 0xFF:s in output
  138. * stream like writing twice every sequence of FF:s (thus doing
  139. * many write()s. But I think interactive telnet application does
  140. * not need to be 100% 8-bit clean, so changing every 0xff:s to
  141. * 0x7f:s
  142. *
  143. * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
  144. * I don't agree.
  145. * first - I cannot use programs like sz/rz
  146. * second - the 0x0D is sent as one character and if the next
  147. * char is 0x0A then it's eaten by a server side.
  148. * third - why do you have to make 'many write()s'?
  149. * I don't understand.
  150. * So I implemented it. It's really useful for me. I hope that
  151. * other people will find it interesting too.
  152. */
  153. int i, j;
  154. byte *p = (byte*)G.buf;
  155. byte outbuf[4*DATABUFSIZE];
  156. for (i = len, j = 0; i > 0; i--, p++) {
  157. if (*p == 0x1d) {
  158. con_escape();
  159. return;
  160. }
  161. outbuf[j++] = *p;
  162. if (*p == 0xff)
  163. outbuf[j++] = 0xff;
  164. else if (*p == 0x0d)
  165. outbuf[j++] = 0x00;
  166. }
  167. if (j > 0)
  168. write(netfd, outbuf, j);
  169. }
  170. static void handle_net_input(int len)
  171. {
  172. int i;
  173. int cstart = 0;
  174. for (i = 0; i < len; i++) {
  175. byte c = G.buf[i];
  176. if (G.telstate == 0) { /* most of the time state == 0 */
  177. if (c == IAC) {
  178. cstart = i;
  179. G.telstate = TS_IAC;
  180. }
  181. continue;
  182. }
  183. switch (G.telstate) {
  184. case TS_0:
  185. if (c == IAC)
  186. G.telstate = TS_IAC;
  187. else
  188. G.buf[cstart++] = c;
  189. break;
  190. case TS_IAC:
  191. if (c == IAC) { /* IAC IAC -> 0xFF */
  192. G.buf[cstart++] = c;
  193. G.telstate = TS_0;
  194. break;
  195. }
  196. /* else */
  197. switch (c) {
  198. case SB:
  199. G.telstate = TS_SUB1;
  200. break;
  201. case DO:
  202. case DONT:
  203. case WILL:
  204. case WONT:
  205. G.telwish = c;
  206. G.telstate = TS_OPT;
  207. break;
  208. default:
  209. G.telstate = TS_0; /* DATA MARK must be added later */
  210. }
  211. break;
  212. case TS_OPT: /* WILL, WONT, DO, DONT */
  213. telopt(c);
  214. G.telstate = TS_0;
  215. break;
  216. case TS_SUB1: /* Subnegotiation */
  217. case TS_SUB2: /* Subnegotiation */
  218. if (subneg(c))
  219. G.telstate = TS_0;
  220. break;
  221. }
  222. }
  223. if (G.telstate) {
  224. if (G.iaclen)
  225. iac_flush();
  226. if (G.telstate == TS_0)
  227. G.telstate = 0;
  228. len = cstart;
  229. }
  230. if (len)
  231. write(STDOUT_FILENO, G.buf, len);
  232. }
  233. static void put_iac(int c)
  234. {
  235. G.iacbuf[G.iaclen++] = c;
  236. }
  237. static void put_iac2(byte wwdd, byte c)
  238. {
  239. if (G.iaclen + 3 > IACBUFSIZE)
  240. iac_flush();
  241. put_iac(IAC);
  242. put_iac(wwdd);
  243. put_iac(c);
  244. }
  245. #if ENABLE_FEATURE_TELNET_TTYPE
  246. static void put_iac_subopt(byte c, char *str)
  247. {
  248. int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
  249. if (G.iaclen + len > IACBUFSIZE)
  250. iac_flush();
  251. put_iac(IAC);
  252. put_iac(SB);
  253. put_iac(c);
  254. put_iac(0);
  255. while (*str)
  256. put_iac(*str++);
  257. put_iac(IAC);
  258. put_iac(SE);
  259. }
  260. #endif
  261. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  262. static void put_iac_subopt_autologin(void)
  263. {
  264. int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2)
  265. const char *user = "USER";
  266. if (G.iaclen + len > IACBUFSIZE)
  267. iac_flush();
  268. put_iac(IAC);
  269. put_iac(SB);
  270. put_iac(TELOPT_NEW_ENVIRON);
  271. put_iac(TELQUAL_IS);
  272. put_iac(NEW_ENV_VAR);
  273. while (*user)
  274. put_iac(*user++);
  275. put_iac(NEW_ENV_VALUE);
  276. while (*G.autologin)
  277. put_iac(*G.autologin++);
  278. put_iac(IAC);
  279. put_iac(SE);
  280. }
  281. #endif
  282. #if ENABLE_FEATURE_AUTOWIDTH
  283. static void put_iac_naws(byte c, int x, int y)
  284. {
  285. if (G.iaclen + 9 > IACBUFSIZE)
  286. iac_flush();
  287. put_iac(IAC);
  288. put_iac(SB);
  289. put_iac(c);
  290. put_iac((x >> 8) & 0xff);
  291. put_iac(x & 0xff);
  292. put_iac((y >> 8) & 0xff);
  293. put_iac(y & 0xff);
  294. put_iac(IAC);
  295. put_iac(SE);
  296. }
  297. #endif
  298. static char const escapecharis[] ALIGN1 = "\r\nEscape character is ";
  299. static void setConMode(void)
  300. {
  301. if (G.telflags & UF_ECHO) {
  302. if (G.charmode == CHM_TRY) {
  303. G.charmode = CHM_ON;
  304. printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
  305. rawmode();
  306. }
  307. } else {
  308. if (G.charmode != CHM_OFF) {
  309. G.charmode = CHM_OFF;
  310. printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
  311. cookmode();
  312. }
  313. }
  314. }
  315. static void will_charmode(void)
  316. {
  317. G.charmode = CHM_TRY;
  318. G.telflags |= (UF_ECHO | UF_SGA);
  319. setConMode();
  320. put_iac2(DO, TELOPT_ECHO);
  321. put_iac2(DO, TELOPT_SGA);
  322. iac_flush();
  323. }
  324. static void do_linemode(void)
  325. {
  326. G.charmode = CHM_TRY;
  327. G.telflags &= ~(UF_ECHO | UF_SGA);
  328. setConMode();
  329. put_iac2(DONT, TELOPT_ECHO);
  330. put_iac2(DONT, TELOPT_SGA);
  331. iac_flush();
  332. }
  333. static void to_notsup(char c)
  334. {
  335. if (G.telwish == WILL)
  336. put_iac2(DONT, c);
  337. else if (G.telwish == DO)
  338. put_iac2(WONT, c);
  339. }
  340. static void to_echo(void)
  341. {
  342. /* if server requests ECHO, don't agree */
  343. if (G.telwish == DO) {
  344. put_iac2(WONT, TELOPT_ECHO);
  345. return;
  346. }
  347. if (G.telwish == DONT)
  348. return;
  349. if (G.telflags & UF_ECHO) {
  350. if (G.telwish == WILL)
  351. return;
  352. } else if (G.telwish == WONT)
  353. return;
  354. if (G.charmode != CHM_OFF)
  355. G.telflags ^= UF_ECHO;
  356. if (G.telflags & UF_ECHO)
  357. put_iac2(DO, TELOPT_ECHO);
  358. else
  359. put_iac2(DONT, TELOPT_ECHO);
  360. setConMode();
  361. write_str(1, "\r\n"); /* sudden modec */
  362. }
  363. static void to_sga(void)
  364. {
  365. /* daemon always sends will/wont, client do/dont */
  366. if (G.telflags & UF_SGA) {
  367. if (G.telwish == WILL)
  368. return;
  369. } else if (G.telwish == WONT)
  370. return;
  371. G.telflags ^= UF_SGA; /* toggle */
  372. if (G.telflags & UF_SGA)
  373. put_iac2(DO, TELOPT_SGA);
  374. else
  375. put_iac2(DONT, TELOPT_SGA);
  376. }
  377. #if ENABLE_FEATURE_TELNET_TTYPE
  378. static void to_ttype(void)
  379. {
  380. /* Tell server we will (or won't) do TTYPE */
  381. if (G.ttype)
  382. put_iac2(WILL, TELOPT_TTYPE);
  383. else
  384. put_iac2(WONT, TELOPT_TTYPE);
  385. }
  386. #endif
  387. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  388. static void to_new_environ(void)
  389. {
  390. /* Tell server we will (or will not) do AUTOLOGIN */
  391. if (G.autologin)
  392. put_iac2(WILL, TELOPT_NEW_ENVIRON);
  393. else
  394. put_iac2(WONT, TELOPT_NEW_ENVIRON);
  395. }
  396. #endif
  397. #if ENABLE_FEATURE_AUTOWIDTH
  398. static void to_naws(void)
  399. {
  400. /* Tell server we will do NAWS */
  401. put_iac2(WILL, TELOPT_NAWS);
  402. }
  403. #endif
  404. static void telopt(byte c)
  405. {
  406. switch (c) {
  407. case TELOPT_ECHO:
  408. to_echo(); break;
  409. case TELOPT_SGA:
  410. to_sga(); break;
  411. #if ENABLE_FEATURE_TELNET_TTYPE
  412. case TELOPT_TTYPE:
  413. to_ttype(); break;
  414. #endif
  415. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  416. case TELOPT_NEW_ENVIRON:
  417. to_new_environ(); break;
  418. #endif
  419. #if ENABLE_FEATURE_AUTOWIDTH
  420. case TELOPT_NAWS:
  421. to_naws();
  422. put_iac_naws(c, G.win_width, G.win_height);
  423. break;
  424. #endif
  425. default:
  426. to_notsup(c);
  427. break;
  428. }
  429. }
  430. /* subnegotiation -- ignore all (except TTYPE,NAWS) */
  431. static int subneg(byte c)
  432. {
  433. switch (G.telstate) {
  434. case TS_SUB1:
  435. if (c == IAC)
  436. G.telstate = TS_SUB2;
  437. #if ENABLE_FEATURE_TELNET_TTYPE
  438. else
  439. if (c == TELOPT_TTYPE)
  440. put_iac_subopt(TELOPT_TTYPE, G.ttype);
  441. #endif
  442. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  443. else
  444. if (c == TELOPT_NEW_ENVIRON)
  445. put_iac_subopt_autologin();
  446. #endif
  447. break;
  448. case TS_SUB2:
  449. if (c == SE)
  450. return TRUE;
  451. G.telstate = TS_SUB1;
  452. /* break; */
  453. }
  454. return FALSE;
  455. }
  456. static void rawmode(void)
  457. {
  458. if (G.do_termios)
  459. tcsetattr(0, TCSADRAIN, &G.termios_raw);
  460. }
  461. static void cookmode(void)
  462. {
  463. if (G.do_termios)
  464. tcsetattr(0, TCSADRAIN, &G.termios_def);
  465. }
  466. /* poll gives smaller (-70 bytes) code */
  467. #define USE_POLL 1
  468. int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  469. int telnet_main(int argc UNUSED_PARAM, char **argv)
  470. {
  471. char *host;
  472. int port;
  473. int len;
  474. #ifdef USE_POLL
  475. struct pollfd ufds[2];
  476. #else
  477. fd_set readfds;
  478. int maxfd;
  479. #endif
  480. INIT_G();
  481. #if ENABLE_FEATURE_AUTOWIDTH
  482. get_terminal_width_height(0, &G.win_width, &G.win_height);
  483. #endif
  484. #if ENABLE_FEATURE_TELNET_TTYPE
  485. G.ttype = getenv("TERM");
  486. #endif
  487. if (tcgetattr(0, &G.termios_def) >= 0) {
  488. G.do_termios = 1;
  489. G.termios_raw = G.termios_def;
  490. cfmakeraw(&G.termios_raw);
  491. }
  492. #if ENABLE_FEATURE_TELNET_AUTOLOGIN
  493. if (1 & getopt32(argv, "al:", &G.autologin))
  494. G.autologin = getenv("USER");
  495. argv += optind;
  496. #else
  497. argv++;
  498. #endif
  499. if (!*argv)
  500. bb_show_usage();
  501. host = *argv++;
  502. port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
  503. if (*argv) /* extra params?? */
  504. bb_show_usage();
  505. xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
  506. setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
  507. signal(SIGINT, record_signo);
  508. #ifdef USE_POLL
  509. ufds[0].fd = 0; ufds[1].fd = netfd;
  510. ufds[0].events = ufds[1].events = POLLIN;
  511. #else
  512. FD_ZERO(&readfds);
  513. FD_SET(STDIN_FILENO, &readfds);
  514. FD_SET(netfd, &readfds);
  515. maxfd = netfd + 1;
  516. #endif
  517. while (1) {
  518. #ifndef USE_POLL
  519. fd_set rfds = readfds;
  520. switch (select(maxfd, &rfds, NULL, NULL, NULL))
  521. #else
  522. switch (poll(ufds, 2, -1))
  523. #endif
  524. {
  525. case 0:
  526. /* timeout */
  527. case -1:
  528. /* error, ignore and/or log something, bay go to loop */
  529. if (bb_got_signal)
  530. con_escape();
  531. else
  532. sleep(1);
  533. break;
  534. default:
  535. #ifdef USE_POLL
  536. if (ufds[0].revents) /* well, should check POLLIN, but ... */
  537. #else
  538. if (FD_ISSET(STDIN_FILENO, &rfds))
  539. #endif
  540. {
  541. len = read(STDIN_FILENO, G.buf, DATABUFSIZE);
  542. if (len <= 0)
  543. doexit(EXIT_SUCCESS);
  544. TRACE(0, ("Read con: %d\n", len));
  545. handle_net_output(len);
  546. }
  547. #ifdef USE_POLL
  548. if (ufds[1].revents) /* well, should check POLLIN, but ... */
  549. #else
  550. if (FD_ISSET(netfd, &rfds))
  551. #endif
  552. {
  553. len = read(netfd, G.buf, DATABUFSIZE);
  554. if (len <= 0) {
  555. write_str(1, "Connection closed by foreign host\r\n");
  556. doexit(EXIT_FAILURE);
  557. }
  558. TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
  559. handle_net_input(len);
  560. }
  561. }
  562. } /* while (1) */
  563. }