|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)telnet.c 4.24 (Berkeley) 7/20/83";
3: #endif
4:
5: /*
6: * User telnet program.
7: */
8: #include <sys/types.h>
9: #include <sys/ioctl.h>
10:
11: #define TELOPTS
12: #include "telnet.h"
13:
14: #include <stdio.h>
15: #include <ctype.h>
16: #include <errno.h>
17: #include <signal.h>
18: #include <setjmp.h>
19: #include <ipc.h>
20:
21: #define strip(x) ((x)&0177)
22:
23: char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
24: char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
25:
26: char hisopts[256];
27: char myopts[256];
28:
29: char doopt[] = { IAC, DO, '%', 'c', 0 };
30: char dont[] = { IAC, DONT, '%', 'c', 0 };
31: char will[] = { IAC, WILL, '%', 'c', 0 };
32: char wont[] = { IAC, WONT, '%', 'c', 0 };
33:
34: int connected;
35: int net;
36: int showoptions = 0;
37: int options;
38: int debug = 0;
39: int crmod = 0;
40: char *prompt;
41: char escape = 035;
42:
43: char line[200];
44: int margc;
45: char *margv[20];
46:
47: jmp_buf toplevel;
48:
49: extern int errno;
50:
51: int tn(), quit(), bye(), help();
52: int setescape(), status(), toggle(), setoptions();
53: int setcrmod(), setdebug();
54:
55: #define HELPINDENT (sizeof ("connect"))
56:
57: struct cmd {
58: char *name; /* command name */
59: char *help; /* help string */
60: int (*handler)(); /* routine which executes command */
61: };
62:
63: char openhelp[] = "open connection to a site";
64: char closehelp[] = "close current connection";
65: char quithelp[] = "exit telnet";
66: char debughelp[] = "toggle debugging";
67: char escapehelp[] = "set escape character";
68: char statushelp[] = "print status information";
69: char helphelp[] = "print help information";
70: char optionshelp[] = "toggle viewing of options processing";
71: char crmodhelp[] = "toggle mapping of received carriage returns";
72:
73: struct cmd cmdtab[] = {
74: { "open", openhelp, tn },
75: { "close", closehelp, bye },
76: { "quit", quithelp, quit },
77: { "escape", escapehelp, setescape },
78: { "status", statushelp, status },
79: { "options", optionshelp, setoptions },
80: { "crmod", crmodhelp, setcrmod },
81: { "debug", debughelp, setdebug },
82: { "?", helphelp, help },
83: 0
84: };
85:
86: int intr(), deadpeer();
87: char *control();
88: struct cmd *getcmd();
89:
90: struct tchars otc;
91: struct ltchars oltc;
92: struct sgttyb ottyb;
93:
94: main(argc, argv)
95: int argc;
96: char *argv[];
97: {
98: ioctl(0, TIOCGETP, (char *)&ottyb);
99: ioctl(0, TIOCGETC, (char *)&otc);
100: ioctl(0, TIOCGLTC, (char *)&oltc);
101: setbuf(stdin, 0);
102: setbuf(stdout, 0);
103: prompt = argv[0];
104: if (argc > 1 && !strcmp(argv[1], "-d"))
105: debug++, argv++, argc--;
106: if (argc != 1) {
107: if (setjmp(toplevel) != 0)
108: exit(0);
109: tn(argc, argv);
110: }
111: setjmp(toplevel);
112: for (;;)
113: command(1);
114: }
115:
116: char *hostname;
117:
118: tn(argc, argv)
119: int argc;
120: char *argv[];
121: {
122: register int c;
123: char *serv;
124: char *tcptofs();
125:
126: if (connected) {
127: printf("?Already connected to %s\n", hostname);
128: return;
129: }
130: if (argc < 2) {
131: strcpy(line, "Connect ");
132: printf("(to) ");
133: gets(&line[strlen(line)]);
134: makeargv();
135: argc = margc;
136: argv = margv;
137: }
138: if (argc > 3) {
139: printf("usage: %s host-name [port]\n", argv[0]);
140: return;
141: }
142: hostname = argv[1];
143: if (argc == 3)
144: serv = tcptofs(atoi(argv[2]));
145: else
146: serv = tcptofs(23);
147: signal(SIGINT, intr);
148: signal(SIGPIPE, SIG_IGN);
149: signal(SIGHUP, SIG_IGN);
150: printf("Trying...\n");
151: net = ipcopen(ipcpath(hostname, "tcp", serv), "light hup");
152: if(net < 0){
153: perror("telnet: connect");
154: signal(SIGINT, SIG_DFL);
155: return;
156: }
157: connected++;
158: call(status, "status", 0);
159: telnet(net);
160: fprintf(stderr, "Connection closed by foreign host.\n");
161: exit(1);
162: }
163:
164: /*
165: * Print status about the connection.
166: */
167: /*VARARGS*/
168: status()
169: {
170: if (connected)
171: printf("Connected to %s.\n", hostname);
172: else
173: printf("No connection.\n");
174: printf("Escape character is '%s'.\n", control(escape));
175: fflush(stdout);
176: }
177:
178: makeargv()
179: {
180: register char *cp;
181: register char **argp = margv;
182:
183: margc = 0;
184: for (cp = line; *cp;) {
185: while (isspace(*cp))
186: cp++;
187: if (*cp == '\0')
188: break;
189: *argp++ = cp;
190: margc += 1;
191: while (*cp != '\0' && !isspace(*cp))
192: cp++;
193: if (*cp == '\0')
194: break;
195: *cp++ = '\0';
196: }
197: *argp++ = 0;
198: }
199:
200: /*VARARGS*/
201: bye()
202: {
203: register char *op;
204:
205: (void) mode(0);
206: if (connected) {
207: printf("Connection closed.\n");
208: close(net);
209: connected = 0;
210: /* reset his options */
211: for (op = hisopts; op < &hisopts[256]; op++)
212: *op = 0;
213: }
214: }
215:
216: /*VARARGS*/
217: quit()
218: {
219: call(bye, "bye", 0);
220: exit(0);
221: }
222:
223: /*
224: * Help command.
225: */
226: help(argc, argv)
227: int argc;
228: char *argv[];
229: {
230: register struct cmd *c;
231:
232: if (argc == 1) {
233: printf("Commands may be abbreviated. Commands are:\n\n");
234: for (c = cmdtab; c->name; c++)
235: printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
236: return;
237: }
238: while (--argc > 0) {
239: register char *arg;
240: arg = *++argv;
241: c = getcmd(arg);
242: if (c == (struct cmd *)-1)
243: printf("?Ambiguous help command %s\n", arg);
244: else if (c == (struct cmd *)0)
245: printf("?Invalid help command %s\n", arg);
246: else
247: printf("%s\n", c->help);
248: }
249: }
250:
251: /*
252: * Call routine with argc, argv set from args (terminated by 0).
253: * VARARGS2
254: */
255: call(routine, args)
256: int (*routine)();
257: int args;
258: {
259: register int *argp;
260: register int argc;
261:
262: for (argc = 0, argp = &args; *argp++ != 0; argc++)
263: ;
264: (*routine)(argc, &args);
265: }
266:
267: struct tchars notc = { -1, -1, -1, -1, -1, -1 };
268: struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
269:
270: mode(f)
271: register int f;
272: {
273: static int prevmode = 0;
274: struct tchars *tc;
275: struct ltchars *ltc;
276: struct sgttyb sb;
277: int onoff, old;
278:
279: if (prevmode == f)
280: return (f);
281: old = prevmode;
282: prevmode = f;
283: sb = ottyb;
284: switch (f) {
285:
286: case 0:
287: onoff = 0;
288: tc = &otc;
289: ltc = &oltc;
290: break;
291:
292: case 1:
293: case 2:
294: sb.sg_flags |= CBREAK;
295: if (f == 1)
296: sb.sg_flags &= ~(ECHO|CRMOD);
297: else
298: sb.sg_flags |= ECHO|CRMOD;
299: sb.sg_erase = sb.sg_kill = -1;
300: tc = ¬c;
301: ltc = &noltc;
302: onoff = 1;
303: break;
304:
305: default:
306: return;
307: }
308: ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
309: ioctl(fileno(stdin), TIOCSETC, (char *)tc);
310: ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
311: return (old);
312: }
313:
314: char sibuf[BUFSIZ], *sbp;
315: char tibuf[BUFSIZ], *tbp;
316: int scc, tcc;
317: int hungup;
318:
319: catchhup()
320: {
321: hungup = 1;
322: signal(SIGHUP, SIG_IGN);
323: }
324:
325: /*
326: * Select from tty and network...
327: */
328: telnet(s)
329: int s;
330: {
331: register int c;
332: int tin = fileno(stdin), tout = fileno(stdout);
333: int on = 1;
334:
335: hungup = 0;
336: (void) mode(2);
337: signal(SIGHUP, catchhup);
338: while(!hungup) {
339: fd_set ibits, obits;
340: int nfds;
341:
342: FD_ZERO(ibits);
343: FD_ZERO(obits);
344: if (nfrontp - nbackp)
345: FD_SET(s, obits);
346: else
347: FD_SET(tin, ibits);
348: if (tfrontp - tbackp)
349: FD_SET(tout, obits);
350: else
351: FD_SET(s, ibits);
352: if (scc < 0 && tcc < 0)
353: break;
354: nfds = select(NOFILE, &ibits, &obits, 1000);
355: if (nfds==0)
356: continue;
357: else if (nfds<0)
358: break;
359:
360: /*
361: * Something to read from the network...
362: */
363: if (FD_ISSET(s, ibits)) {
364: scc = read(s, sibuf, sizeof (sibuf));
365: if (scc <= 0)
366: break;
367: sbp = sibuf;
368: }
369:
370: /*
371: * Something to read from the tty...
372: */
373: if (FD_ISSET(tin, ibits)) {
374: tcc = read(tin, tibuf, sizeof (tibuf));
375: if (tcc < 0)
376: break;
377: tbp = tibuf;
378: }
379:
380: while (tcc > 0) {
381: register int c;
382:
383: if ((&netobuf[BUFSIZ] - nfrontp) < 2)
384: break;
385: c = *tbp++ & 0377, tcc--;
386: if (strip(c) == escape) {
387: command(0);
388: tcc = 0;
389: break;
390: }
391: if (c == IAC)
392: *nfrontp++ = c;
393: *nfrontp++ = c;
394: }
395: if (FD_ISSET(s, obits) && (nfrontp - nbackp) > 0)
396: netflush(s);
397: if (scc > 0)
398: telrcv();
399: if (FD_ISSET(tout, obits) && (tfrontp - tbackp) > 0)
400: ttyflush(tout);
401: }
402: signal(SIGHUP, SIG_IGN);
403: (void) mode(0);
404: }
405:
406: command(top)
407: int top;
408: {
409: register struct cmd *c;
410: int oldmode, wasopen;
411:
412: oldmode = mode(0);
413: if (!top)
414: putchar('\n');
415: else
416: signal(SIGINT, SIG_DFL);
417: for (;;) {
418: printf("%s> ", prompt);
419: if (gets(line) == 0) {
420: if (feof(stdin)) {
421: clearerr(stdin);
422: putchar('\n');
423: }
424: break;
425: }
426: if (line[0] == 0)
427: break;
428: makeargv();
429: c = getcmd(margv[0]);
430: if (c == (struct cmd *)-1) {
431: printf("?Ambiguous command\n");
432: continue;
433: }
434: if (c == 0) {
435: printf("?Invalid command\n");
436: continue;
437: }
438: (*c->handler)(margc, margv);
439: if (c->handler != help)
440: break;
441: }
442: if (!top) {
443: if (!connected)
444: longjmp(toplevel, 1);
445: (void) mode(oldmode);
446: }
447: }
448:
449: /*
450: * Telnet receiver states for fsm
451: */
452: #define TS_DATA 0
453: #define TS_IAC 1
454: #define TS_WILL 2
455: #define TS_WONT 3
456: #define TS_DO 4
457: #define TS_DONT 5
458:
459: telrcv()
460: {
461: register int c;
462: static int state = TS_DATA;
463:
464: while (scc > 0) {
465: c = *sbp++ & 0377, scc--;
466: switch (state) {
467:
468: case TS_DATA:
469: if (c == IAC) {
470: state = TS_IAC;
471: continue;
472: }
473: *tfrontp++ = c;
474: /*
475: * This hack is needed since we can't set
476: * CRMOD on output only. Machines like MULTICS
477: * like to send \r without \n; since we must
478: * turn off CRMOD to get proper input, the mapping
479: * is done here (sigh).
480: */
481: if (c == '\r' && crmod)
482: *tfrontp++ = '\n';
483: continue;
484:
485: case TS_IAC:
486: switch (c) {
487:
488: case WILL:
489: state = TS_WILL;
490: continue;
491:
492: case WONT:
493: state = TS_WONT;
494: continue;
495:
496: case DO:
497: state = TS_DO;
498: continue;
499:
500: case DONT:
501: state = TS_DONT;
502: continue;
503:
504: case DM:
505: ioctl(fileno(stdout), TIOCFLUSH, 0);
506: break;
507:
508: case NOP:
509: case GA:
510: break;
511:
512: default:
513: break;
514: }
515: state = TS_DATA;
516: continue;
517:
518: case TS_WILL:
519: printoption("RCVD", will, c, !hisopts[c]);
520: if (!hisopts[c])
521: willoption(c);
522: state = TS_DATA;
523: continue;
524:
525: case TS_WONT:
526: printoption("RCVD", wont, c, hisopts[c]);
527: if (hisopts[c])
528: wontoption(c);
529: state = TS_DATA;
530: continue;
531:
532: case TS_DO:
533: printoption("RCVD", doopt, c, !myopts[c]);
534: if (!myopts[c])
535: dooption(c);
536: state = TS_DATA;
537: continue;
538:
539: case TS_DONT:
540: printoption("RCVD", dont, c, myopts[c]);
541: if (myopts[c]) {
542: myopts[c] = 0;
543: sprintf(nfrontp, wont, c);
544: nfrontp += sizeof (wont) - 2;
545: printoption("SENT", wont, c);
546: }
547: state = TS_DATA;
548: continue;
549: }
550: }
551: }
552:
553: willoption(option)
554: int option;
555: {
556: char *fmt;
557:
558: switch (option) {
559:
560: case TELOPT_ECHO:
561: (void) mode(1);
562:
563: case TELOPT_SGA:
564: hisopts[option] = 1;
565: fmt = doopt;
566: break;
567:
568: case TELOPT_TM:
569: fmt = dont;
570: break;
571:
572: default:
573: fmt = dont;
574: break;
575: }
576: sprintf(nfrontp, fmt, option);
577: nfrontp += sizeof (dont) - 2;
578: printoption("SENT", fmt, option);
579: }
580:
581: wontoption(option)
582: int option;
583: {
584: char *fmt;
585:
586: switch (option) {
587:
588: case TELOPT_ECHO:
589: (void) mode(2);
590:
591: case TELOPT_SGA:
592: hisopts[option] = 0;
593: fmt = dont;
594: break;
595:
596: default:
597: fmt = dont;
598: }
599: sprintf(nfrontp, fmt, option);
600: nfrontp += sizeof (doopt) - 2;
601: printoption("SENT", fmt, option);
602: }
603:
604: dooption(option)
605: int option;
606: {
607: char *fmt;
608:
609: switch (option) {
610:
611: case TELOPT_TM:
612: fmt = wont;
613: break;
614:
615: case TELOPT_ECHO:
616: (void) mode(2);
617: fmt = will;
618: hisopts[option] = 0;
619: break;
620:
621: case TELOPT_SGA:
622: fmt = will;
623: break;
624:
625: default:
626: fmt = wont;
627: break;
628: }
629: sprintf(nfrontp, fmt, option);
630: nfrontp += sizeof (doopt) - 2;
631: printoption("SENT", fmt, option);
632: }
633:
634: /*
635: * Set the escape character.
636: */
637: setescape(argc, argv)
638: int argc;
639: char *argv[];
640: {
641: register char *arg;
642: char buf[50];
643:
644: if (argc > 2)
645: arg = argv[1];
646: else {
647: printf("new escape character: ");
648: gets(buf);
649: arg = buf;
650: }
651: if (arg[0] != '\0')
652: escape = arg[0];
653: printf("Escape character is '%s'.\n", control(escape));
654: fflush(stdout);
655: }
656:
657: /*VARARGS*/
658: setoptions()
659: {
660:
661: showoptions = !showoptions;
662: printf("%s show option processing.\n", showoptions ? "Will" : "Wont");
663: fflush(stdout);
664: }
665:
666: /*VARARGS*/
667: setcrmod()
668: {
669:
670: crmod = !crmod;
671: printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont");
672: fflush(stdout);
673: }
674:
675: /*VARARGS*/
676: setdebug()
677: {
678:
679: debug = !debug;
680: printf("%s turn on socket level debugging.\n",
681: debug ? "Will" : "Wont");
682: fflush(stdout);
683: }
684:
685: /*
686: * Construct a control character sequence
687: * for a special character.
688: */
689: char *
690: control(c)
691: register int c;
692: {
693: static char buf[3];
694:
695: if (c == 0177)
696: return ("^?");
697: if (c >= 040) {
698: buf[0] = c;
699: buf[1] = 0;
700: } else {
701: buf[0] = '^';
702: buf[1] = '@'+c;
703: buf[2] = 0;
704: }
705: return (buf);
706: }
707:
708: struct cmd *
709: getcmd(name)
710: register char *name;
711: {
712: register char *p, *q;
713: register struct cmd *c, *found;
714: register int nmatches, longest;
715:
716: longest = 0;
717: nmatches = 0;
718: found = 0;
719: for (c = cmdtab; p = c->name; c++) {
720: for (q = name; *q == *p++; q++)
721: if (*q == 0) /* exact match? */
722: return (c);
723: if (!*q) { /* the name was a prefix */
724: if (q - name > longest) {
725: longest = q - name;
726: nmatches = 1;
727: found = c;
728: } else if (q - name == longest)
729: nmatches++;
730: }
731: }
732: if (nmatches > 1)
733: return ((struct cmd *)-1);
734: return (found);
735: }
736:
737: intr()
738: {
739: (void) mode(0);
740: longjmp(toplevel, -1);
741: }
742:
743: ttyflush(fd)
744: {
745: int n;
746:
747: if ((n = tfrontp - tbackp) > 0)
748: n = write(fd, tbackp, n);
749: if (n < 0)
750: return;
751: tbackp += n;
752: if (tbackp == tfrontp)
753: tbackp = tfrontp = ttyobuf;
754: }
755:
756: netflush(fd)
757: {
758: int n;
759:
760: if ((n = nfrontp - nbackp) > 0)
761: n = write(fd, nbackp, n);
762: if (n > 0)
763: nbackp += n;
764: if (nbackp == nfrontp)
765: nbackp = nfrontp = netobuf;
766: }
767:
768: /*VARARGS*/
769: printoption(direction, fmt, option, what)
770: char *direction, *fmt;
771: int option, what;
772: {
773: if (!showoptions)
774: return;
775: printf("%s ", direction);
776: if (fmt == doopt)
777: fmt = "do";
778: else if (fmt == dont)
779: fmt = "dont";
780: else if (fmt == will)
781: fmt = "will";
782: else if (fmt == wont)
783: fmt = "wont";
784: else
785: fmt = "???";
786: if (option < TELOPT_SUPDUP)
787: printf("%s %s", fmt, telopts[option]);
788: else
789: printf("%s %d", fmt, option);
790: if (*direction == '<') {
791: printf("\r\n");
792: return;
793: }
794: printf(" (%s)\r\n", what ? "reply" : "don't reply");
795: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.