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