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