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