telnet.c 14 KB

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