telnet.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <bio.h>
  12. #include "telnet.h"
  13. int ctl = -1; /* control fd (for break's) */
  14. int consctl = -1; /* consctl fd */
  15. int ttypid; /* pid's if the 2 processes (used to kill them) */
  16. int netpid;
  17. int interrupted;
  18. int localecho;
  19. int notkbd;
  20. static char *srv;
  21. typedef struct Comm Comm;
  22. struct Comm {
  23. int returns;
  24. int stopped;
  25. };
  26. Comm *comm;
  27. int dodial(char*);
  28. void fromkbd(int);
  29. void fromnet(int);
  30. int menu(Biobuf*, int);
  31. void notifyf(void*, char*);
  32. void rawoff(void);
  33. void rawon(void);
  34. void telnet(int);
  35. char* system(int, char*);
  36. int echochange(Biobuf*, int);
  37. int termsub(Biobuf*, uint8_t*, int);
  38. int xlocsub(Biobuf*, uint8_t*, int);
  39. void* share(uint32_t);
  40. static int islikeatty(int);
  41. void
  42. usage(void)
  43. {
  44. fatal("usage: telnet [-Cdnr] [-s srv] net!host[!service]", 0, 0);
  45. }
  46. void
  47. main(int argc, char *argv[])
  48. {
  49. int returns;
  50. returns = 1;
  51. ARGBEGIN{
  52. case 'C':
  53. opt[Echo].noway = 1;
  54. break;
  55. case 'd':
  56. debug = 1;
  57. break;
  58. case 'n':
  59. notkbd = 1;
  60. break;
  61. case 'r':
  62. returns = 0;
  63. break;
  64. case 's':
  65. srv = EARGF(usage());
  66. break;
  67. default:
  68. usage();
  69. }ARGEND
  70. if(argc != 1)
  71. usage();
  72. /* options we need routines for */
  73. opt[Echo].change = echochange;
  74. opt[Term].sub = termsub;
  75. opt[Xloc].sub = xlocsub;
  76. comm = share(sizeof(comm));
  77. comm->returns = returns;
  78. telnet(dodial(argv[0]));
  79. }
  80. /*
  81. * dial and return a data connection
  82. */
  83. int
  84. dodial(char *dest)
  85. {
  86. char *name;
  87. int data;
  88. char devdir[NETPATHLEN];
  89. name = netmkaddr(dest, "tcp", "telnet");
  90. data = dial(name, 0, devdir, 0);
  91. if(data < 0)
  92. fatal("%s: %r", name, 0);
  93. fprint(2, "connected to %s on %s\n", name, devdir);
  94. return data;
  95. }
  96. void
  97. post(char *srv, int fd)
  98. {
  99. int f;
  100. char buf[32];
  101. f = create(srv, OWRITE, 0666);
  102. if(f < 0)
  103. sysfatal("create %s: %r", srv);
  104. snprint(buf, sizeof buf, "%d", fd);
  105. if(write(f, buf, strlen(buf)) != strlen(buf))
  106. sysfatal("write %s: %r", srv);
  107. close(f);
  108. }
  109. /*
  110. * two processes pass bytes back and forth between the
  111. * terminal and the network.
  112. */
  113. void
  114. telnet(int net)
  115. {
  116. int pid;
  117. int p[2];
  118. char *svc;
  119. rawoff();
  120. svc = nil;
  121. if (srv) {
  122. if(pipe(p) < 0)
  123. sysfatal("pipe: %r");
  124. if (srv[0] != '/')
  125. svc = smprint("/srv/%s", srv);
  126. else
  127. svc = srv;
  128. post(svc, p[0]);
  129. close(p[0]);
  130. dup(p[1], 0);
  131. dup(p[1], 1);
  132. /* pipe is now std in & out */
  133. }
  134. ttypid = getpid();
  135. switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
  136. case -1:
  137. perror("con");
  138. exits("fork");
  139. case 0:
  140. rawoff();
  141. notify(notifyf);
  142. fromnet(net);
  143. if (svc)
  144. remove(svc);
  145. sendnote(ttypid, "die");
  146. exits(0);
  147. default:
  148. netpid = pid;
  149. notify(notifyf);
  150. fromkbd(net);
  151. if(notkbd)
  152. for(;;)
  153. sleep(1000); // sleep(0) is a cpuhog
  154. if (svc)
  155. remove(svc);
  156. sendnote(netpid, "die");
  157. exits(0);
  158. }
  159. }
  160. /*
  161. * Read the keyboard and write it to the network. '^\' gets us into
  162. * the menu.
  163. */
  164. void
  165. fromkbd(int net)
  166. {
  167. Biobuf ib, ob;
  168. int c, likeatty;
  169. int eofs;
  170. Binit(&ib, 0, OREAD);
  171. Binit(&ob, net, OWRITE);
  172. likeatty = islikeatty(0);
  173. eofs = 0;
  174. for(;;){
  175. c = Bgetc(&ib);
  176. /*
  177. * with raw off, all ^D's get turned into Eof's.
  178. * change them back.
  179. * 10 in a row implies that the terminal is really gone so
  180. * just hang up.
  181. */
  182. if(c < 0){
  183. if(notkbd)
  184. return;
  185. if(eofs++ > 10)
  186. return;
  187. c = 004;
  188. } else
  189. eofs = 0;
  190. /*
  191. * if not in binary mode, look for the ^\ escape to menu.
  192. * also turn \n into \r\n
  193. */
  194. if(likeatty || !opt[Binary].local){
  195. if(c == 0034){ /* CTRL \ */
  196. if(Bflush(&ob) < 0)
  197. return;
  198. if(menu(&ib, net) < 0)
  199. return;
  200. continue;
  201. }
  202. }
  203. if(!opt[Binary].local){
  204. if(c == '\n'){
  205. /*
  206. * This is a very strange use of the SGA option.
  207. * I did this because some systems that don't
  208. * announce a willingness to supress-go-ahead
  209. * need the \r\n sequence to recognize input.
  210. * If someone can explain this to me, please
  211. * send me mail. - presotto
  212. */
  213. if(opt[SGA].remote){
  214. c = '\r';
  215. } else {
  216. if(Bputc(&ob, '\r') < 0)
  217. return;
  218. }
  219. }
  220. }
  221. if(Bputc(&ob, c) < 0)
  222. return;
  223. if(Bbuffered(&ib) == 0)
  224. if(Bflush(&ob) < 0)
  225. return;
  226. }
  227. }
  228. /*
  229. * Read from the network and write to the screen. If 'stopped' is set
  230. * spin and don't read. Filter out spurious carriage returns.
  231. */
  232. void
  233. fromnet(int net)
  234. {
  235. int c;
  236. int crnls = 0, freenl = 0, eofs;
  237. Biobuf ib, ob;
  238. Binit(&ib, net, OREAD);
  239. Binit(&ob, 1, OWRITE);
  240. eofs = 0;
  241. for(;;){
  242. if(Bbuffered(&ib) == 0)
  243. Bflush(&ob);
  244. if(interrupted){
  245. interrupted = 0;
  246. send2(net, Iac, Interrupt);
  247. }
  248. c = Bgetc(&ib);
  249. if(c < 0){
  250. if(eofs++ >= 2)
  251. return;
  252. continue;
  253. }
  254. eofs = 0;
  255. switch(c){
  256. case '\n': /* skip nl after string of cr's */
  257. if(!opt[Binary].local && !comm->returns){
  258. ++crnls;
  259. if(freenl == 0)
  260. break;
  261. freenl = 0;
  262. continue;
  263. }
  264. break;
  265. case '\r': /* first cr becomes nl, remainder dropped */
  266. if(!opt[Binary].local && !comm->returns){
  267. if(crnls++ == 0){
  268. freenl = 1;
  269. c = '\n';
  270. break;
  271. }
  272. continue;
  273. }
  274. break;
  275. case 0: /* remove nulls from crnl string */
  276. if(crnls)
  277. continue;
  278. break;
  279. case Iac:
  280. crnls = 0;
  281. freenl = 0;
  282. c = Bgetc(&ib);
  283. if(c == Iac)
  284. break;
  285. if(Bflush(&ob) < 0)
  286. return;
  287. if(control(&ib, c) < 0)
  288. return;
  289. continue;
  290. default:
  291. crnls = 0;
  292. freenl = 0;
  293. break;
  294. }
  295. if(Bputc(&ob, c) < 0)
  296. return;
  297. }
  298. }
  299. /*
  300. * turn keyboard raw mode on
  301. */
  302. void
  303. rawon(void)
  304. {
  305. if(debug)
  306. fprint(2, "rawon\n");
  307. if(consctl < 0)
  308. consctl = open("/dev/consctl", OWRITE);
  309. if(consctl < 0){
  310. fprint(2, "%s: can't open consctl: %r\n", argv0);
  311. return;
  312. }
  313. write(consctl, "rawon", 5);
  314. }
  315. /*
  316. * turn keyboard raw mode off
  317. */
  318. void
  319. rawoff(void)
  320. {
  321. if(debug)
  322. fprint(2, "rawoff\n");
  323. if(consctl < 0)
  324. consctl = open("/dev/consctl", OWRITE);
  325. if(consctl < 0){
  326. fprint(2, "%s: can't open consctl: %r\n", argv0);
  327. return;
  328. }
  329. write(consctl, "rawoff", 6);
  330. }
  331. /*
  332. * control menu
  333. */
  334. #define STDHELP "\t(b)reak, (i)nterrupt, (q)uit, (r)eturns, (!cmd), (.)continue\n"
  335. int
  336. menu(Biobuf *bp, int net)
  337. {
  338. char *cp;
  339. int done;
  340. comm->stopped = 1;
  341. rawoff();
  342. fprint(2, ">>> ");
  343. for(done = 0; !done; ){
  344. cp = Brdline(bp, '\n');
  345. if(cp == 0){
  346. comm->stopped = 0;
  347. return -1;
  348. }
  349. cp[Blinelen(bp)-1] = 0;
  350. switch(*cp){
  351. case '!':
  352. system(Bfildes(bp), cp+1);
  353. done = 1;
  354. break;
  355. case '.':
  356. done = 1;
  357. break;
  358. case 'q':
  359. comm->stopped = 0;
  360. return -1;
  361. case 'o':
  362. switch(*(cp+1)){
  363. case 'd':
  364. send3(net, Iac, Do, atoi(cp+2));
  365. break;
  366. case 'w':
  367. send3(net, Iac, Will, atoi(cp+2));
  368. break;
  369. }
  370. break;
  371. case 'r':
  372. comm->returns = !comm->returns;
  373. done = 1;
  374. break;
  375. case 'i':
  376. send2(net, Iac, Interrupt);
  377. break;
  378. case 'b':
  379. send2(net, Iac, Break);
  380. break;
  381. default:
  382. fprint(2, STDHELP);
  383. break;
  384. }
  385. if(!done)
  386. fprint(2, ">>> ");
  387. }
  388. rawon();
  389. comm->stopped = 0;
  390. return 0;
  391. }
  392. /*
  393. * ignore interrupts
  394. */
  395. void
  396. notifyf(void *a, char *msg)
  397. {
  398. USED(a);
  399. if(strcmp(msg, "interrupt") == 0){
  400. interrupted = 1;
  401. noted(NCONT);
  402. }
  403. if(strcmp(msg, "hangup") == 0)
  404. noted(NCONT);
  405. noted(NDFLT);
  406. }
  407. /*
  408. * run a command with the network connection as standard IO
  409. */
  410. char *
  411. system(int fd, char *cmd)
  412. {
  413. int pid;
  414. int p;
  415. static Waitmsg msg;
  416. if((pid = fork()) == -1){
  417. perror("con");
  418. return "fork failed";
  419. }
  420. else if(pid == 0){
  421. dup(fd, 0);
  422. close(ctl);
  423. close(fd);
  424. if(*cmd)
  425. execl("/bin/rc", "rc", "-c", cmd, nil);
  426. else
  427. execl("/bin/rc", "rc", nil);
  428. perror("con");
  429. exits("exec");
  430. }
  431. for(p = waitpid(); p >= 0; p = waitpid()){
  432. if(p == pid)
  433. return msg.msg;
  434. }
  435. return "lost child";
  436. }
  437. /*
  438. * suppress local echo if the remote side is doing it
  439. */
  440. int
  441. echochange(Biobuf *bp, int cmd)
  442. {
  443. USED(bp);
  444. switch(cmd){
  445. case Will:
  446. rawon();
  447. break;
  448. case Wont:
  449. rawoff();
  450. break;
  451. }
  452. return 0;
  453. }
  454. /*
  455. * send terminal type to the other side
  456. */
  457. int
  458. termsub(Biobuf *bp, uint8_t *sub, int n)
  459. {
  460. char buf[64];
  461. char *term;
  462. char *p = buf;
  463. if(n < 1)
  464. return 0;
  465. if(sub[0] == 1){
  466. *p++ = Iac;
  467. *p++ = Sb;
  468. *p++ = opt[Term].code;
  469. *p++ = 0;
  470. term = getenv("TERM");
  471. if(term == 0 || *term == 0)
  472. term = "p9win";
  473. strncpy(p, term, sizeof(buf) - (p - buf) - 2);
  474. buf[sizeof(buf)-2] = 0;
  475. p += strlen(p);
  476. *p++ = Iac;
  477. *p++ = Se;
  478. return iwrite(Bfildes(bp), buf, p-buf);
  479. }
  480. return 0;
  481. }
  482. /*
  483. * send an x display location to the other side
  484. */
  485. int
  486. xlocsub(Biobuf *bp, uint8_t *sub, int n)
  487. {
  488. char buf[64];
  489. char *term;
  490. char *p = buf;
  491. if(n < 1)
  492. return 0;
  493. if(sub[0] == 1){
  494. *p++ = Iac;
  495. *p++ = Sb;
  496. *p++ = opt[Xloc].code;
  497. *p++ = 0;
  498. term = getenv("XDISP");
  499. if(term == 0 || *term == 0)
  500. term = "unknown";
  501. strncpy(p, term, p - buf - 2);
  502. p += strlen(term);
  503. *p++ = Iac;
  504. *p++ = Se;
  505. return iwrite(Bfildes(bp), buf, p-buf);
  506. }
  507. return 0;
  508. }
  509. static int
  510. islikeatty(int fd)
  511. {
  512. char buf[64];
  513. if(fd2path(fd, buf, sizeof buf) != 0)
  514. return 0;
  515. /* might be /mnt/term/dev/cons */
  516. return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
  517. }
  518. /*
  519. * create a shared segment. Make is start 2 meg higher than the current
  520. * end of process memory.
  521. */
  522. void*
  523. share(uint32_t len)
  524. {
  525. uint8_t *vastart;
  526. vastart = sbrk(0);
  527. if(vastart == (void*)-1)
  528. return 0;
  529. vastart += 2*1024*1024;
  530. if(segattach(0, "shared", vastart, len) == (void*)-1)
  531. return 0;
  532. return vastart;
  533. }