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