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