3
0

telnet.c 13 KB

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