This version fixes ECHOing problems, discovers 4.2 (brain-damaged)
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
8c5eec2f
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
ac6e6727 13#ifndef lint
affdaa4e 14static char sccsid[] = "@(#)telnetd.c 5.14 (Berkeley) %G%";
8c5eec2f 15#endif not lint
ac6e6727 16
66b878f6
BJ
17/*
18 * Stripped-down telnet server.
19 */
de3b21e8
SL
20#include <sys/types.h>
21#include <sys/socket.h>
ce4fd43b 22#include <sys/wait.h>
1a33b848 23#include <sys/file.h>
c29f876c 24#include <sys/stat.h>
5d78ef73 25#include <sys/time.h>
de3b21e8
SL
26
27#include <netinet/in.h>
28
2b597a6b
SL
29#include <arpa/telnet.h>
30
66b878f6
BJ
31#include <stdio.h>
32#include <signal.h>
33#include <errno.h>
34#include <sgtty.h>
9f005877 35#include <netdb.h>
3f99c0f7 36#include <syslog.h>
affdaa4e 37#include <ctype.h>
de3b21e8 38
4898c5bf 39#define BELL '\07'
122f5efc 40#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
66b878f6 41
affdaa4e
GM
42#define OPT_DONT 0 /* don't do this option */
43#define OPT_WONT 0 /* won't do this option */
44#define OPT_DO 1 /* do this option */
45#define OPT_WILL 1 /* will do this option */
46#define OPT_ALWAYS_LOOK 2 /* special case for echo */
66b878f6
BJ
47char hisopts[256];
48char myopts[256];
49
50char doopt[] = { IAC, DO, '%', 'c', 0 };
51char dont[] = { IAC, DONT, '%', 'c', 0 };
52char will[] = { IAC, WILL, '%', 'c', 0 };
53char wont[] = { IAC, WONT, '%', 'c', 0 };
54
55/*
56 * I/O data buffers, pointers, and counters.
57 */
58char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
affdaa4e 59
66b878f6 60char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
affdaa4e 61
66b878f6 62char netibuf[BUFSIZ], *netip = netibuf;
affdaa4e
GM
63#define NIACCUM(c) { *netip++ = c; \
64 ncc++; \
65 }
66
9ef3087d 67char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
5d78ef73 68char *neturg = 0; /* one past last bye of urgent data */
affdaa4e
GM
69 /* the remote system seems to NOT be an old 4.2 */
70int not42 = 1;
71
72
73char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */
74#define SB_CLEAR() subpointer = subbuffer;
75#define SB_TERM() subend = subpointer;
76#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
77 *subpointer++ = (c); \
78 }
79
66b878f6
BJ
80int pcc, ncc;
81
82int pty, net;
83int inter;
fe5c5547 84extern char **environ;
66b878f6 85extern int errno;
c29f876c 86char *line;
5d78ef73
GM
87int SYNCHing = 0; /* we are in TELNET SYNCH mode */
88/*
89 * The following are some clocks used to decide how to interpret
90 * the relationship between various variables.
91 */
66b878f6 92
5d78ef73
GM
93struct {
94 int
95 system, /* what the current time is */
96 echotoggle, /* last time user entered echo character */
97 modenegotiated, /* last time operating mode negotiated */
98 didnetreceive, /* last time we read data from network */
99 gotDM; /* when did we last see a data mark */
100} clocks;
101
102#define settimer(x) clocks.x = clocks.system++
103\f
66b878f6
BJ
104main(argc, argv)
105 char *argv[];
106{
bb933cc2 107 struct sockaddr_in from;
bcb894cb 108 int on = 1, fromlen;
bb933cc2 109
5d78ef73
GM
110#if defined(DEBUG)
111 {
112 int s, ns, foo;
113 struct servent *sp;
114 static struct sockaddr_in sin = { AF_INET };
115
116 sp = getservbyname("telnet", "tcp");
117 if (sp == 0) {
118 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
119 exit(1);
120 }
121 sin.sin_port = sp->s_port;
122 argc--, argv++;
123 if (argc > 0) {
124 sin.sin_port = atoi(*argv);
125 sin.sin_port = htons((u_short)sin.sin_port);
126 }
127
128 s = socket(AF_INET, SOCK_STREAM, 0);
129 if (s < 0) {
130 perror("telnetd: socket");;
131 exit(1);
132 }
133 if (bind(s, &sin, sizeof sin) < 0) {
134 perror("bind");
135 exit(1);
136 }
137 if (listen(s, 1) < 0) {
138 perror("listen");
139 exit(1);
140 }
141 foo = sizeof sin;
142 ns = accept(s, &sin, &foo);
143 if (ns < 0) {
144 perror("accept");
145 exit(1);
146 }
147 dup2(ns, 0);
148 close(s);
149 }
150#endif /* defined(DEBUG) */
076ae92c 151 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
bb933cc2
MK
152 fromlen = sizeof (from);
153 if (getpeername(0, &from, &fromlen) < 0) {
154 fprintf(stderr, "%s: ", argv[0]);
155 perror("getpeername");
156 _exit(1);
de3b21e8 157 }
bcb894cb 158 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
3f99c0f7 159 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
de3b21e8 160 }
bb933cc2 161 doit(0, &from);
f553aca8
SL
162}
163
affdaa4e
GM
164
165/*
166 * Get()
167 *
168 * Return next character from file descriptor.
169 *
170 * This is not meant to be very efficient, since it is only
171 * run during startup.
172 */
173
174Get(f)
175int f; /* the file descriptor */
176{
177 char input;
178
179 if (read(f, &input, 1) != 1) {
180 syslog(LOG_ERR, "read: %m\n");
181 exit(1);
182 }
183 return input&0xff;
184}
185
186char *terminaltype;
187char *envinit[2];
66b878f6
BJ
188int cleanup();
189
190/*
191 * Get a pty, scan input lines.
192 */
37c640e2
SL
193doit(f, who)
194 int f;
195 struct sockaddr_in *who;
66b878f6 196{
c29f876c 197 char *host, *inet_ntoa();
1a33b848 198 int i, p, t;
66b878f6 199 struct sgttyb b;
37c640e2 200 struct hostent *hp;
affdaa4e
GM
201 int c;
202 int gotterminaltype = 0;
203
204 /*
205 * Try to get a terminal type from the foreign host.
206 */
207
208 {
209 static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
210
211 terminaltype = 0;
212 if (write(f, sbuf, sizeof sbuf) == -1) {
213 syslog(LOG_ERR, "write sbuf: %m\n");
214 exit(1);
215 }
216 for (;;) { /* ugly, but we are VERY early */
217 while ((c = Get(f)) != IAC) {
218 NIACCUM(c);
219 }
220 if ((c = Get(f)) == WILL) {
221 if ((c = Get(f)) == TELOPT_TTYPE) {
222 static char sbbuf[] = { IAC, SB, TELOPT_TTYPE,
223 TELQUAL_SEND, IAC, SE };
224 if (write(f, sbbuf, sizeof sbbuf) == -1) {
225 syslog(LOG_ERR, "write sbbuf: %m\n");
226 exit(1);
227 }
228 break;
229 } else {
230 NIACCUM(IAC);
231 NIACCUM(WILL);
232 NIACCUM(c);
233 }
234 } else if (c == WONT) {
235 if ((c = Get(f)) == TELOPT_TTYPE) {
236 terminaltype = "TERM=network";
237 break;
238 } else {
239 NIACCUM(IAC);
240 NIACCUM(WONT);
241 NIACCUM(c);
242 }
243 } else {
244 NIACCUM(IAC);
245 NIACCUM(c);
246 }
247 }
248 if (!terminaltype) {
249 for (;;) {
250 while ((c = Get(f)) != IAC) {
251 NIACCUM(c);
252 }
253 if ((c = Get(f)) != SB) {
254 NIACCUM(IAC);
255 NIACCUM(c);
256 } else if ((c = Get(f)) != TELOPT_TTYPE) {
257 NIACCUM(IAC);
258 NIACCUM(SB);
259 NIACCUM(c);
260 } else if ((c = Get(f)) != TELQUAL_IS) {
261 NIACCUM(IAC);
262 NIACCUM(SB);
263 NIACCUM(TELOPT_TTYPE);
264 NIACCUM(c);
265 } else { /* Yaaaay! */
266 static char terminalname[5+41] = "TERM=";
267
268 terminaltype = terminalname+strlen(terminalname);
269
270 while (terminaltype <
271 (terminalname + sizeof terminalname-1)) {
272 if ((c = Get(f)) == IAC) {
273 if ((c = Get(f)) == SE) {
274 break; /* done */
275 } else {
276 *terminaltype++ = IAC; /* ? */
277 if (isupper(c)) {
278 c = tolower(c);
279 }
280 *terminaltype++ = c;
281 }
282 } else {
283 if (isupper(c)) {
284 c = tolower(c);
285 }
286 *terminaltype++ = c; /* accumulate name */
287 }
288 }
289 *terminaltype = 0;
290 terminaltype = terminalname;
291 gotterminaltype = 1;
292 break;
293 }
294 }
295 }
296 envinit[0] = terminaltype;
297 envinit[1] = 0;
298 }
1a33b848 299
c29f876c
MK
300 for (c = 'p'; c <= 's'; c++) {
301 struct stat stb;
302
303 line = "/dev/ptyXX";
304 line[strlen("/dev/pty")] = c;
305 line[strlen("/dev/ptyp")] = '0';
306 if (stat(line, &stb) < 0)
307 break;
1a33b848 308 for (i = 0; i < 16; i++) {
c29f876c
MK
309 line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
310 p = open(line, 2);
1a33b848
SL
311 if (p > 0)
312 goto gotpty;
313 }
66b878f6 314 }
8f2758db
SL
315 fatal(f, "All network ports in use");
316 /*NOTREACHED*/
66b878f6
BJ
317gotpty:
318 dup2(f, 0);
c29f876c 319 line[strlen("/dev/")] = 't';
1a33b848 320 t = open("/dev/tty", O_RDWR);
66b878f6
BJ
321 if (t >= 0) {
322 ioctl(t, TIOCNOTTY, 0);
323 close(t);
324 }
c29f876c 325 t = open(line, O_RDWR);
8f2758db 326 if (t < 0)
c29f876c 327 fatalperror(f, line, errno);
66b878f6 328 ioctl(t, TIOCGETP, &b);
9ef3087d 329 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 330 ioctl(t, TIOCSETP, &b);
9ef3087d 331 ioctl(p, TIOCGETP, &b);
da96b661 332 b.sg_flags &= ~ECHO;
9ef3087d 333 ioctl(p, TIOCSETP, &b);
37c640e2
SL
334 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
335 who->sin_family);
336 if (hp)
337 host = hp->h_name;
338 else
05fa5465 339 host = inet_ntoa(who->sin_addr);
8f2758db
SL
340 if ((i = fork()) < 0)
341 fatalperror(f, "fork", errno);
66b878f6
BJ
342 if (i)
343 telnet(f, p);
344 close(f);
345 close(p);
346 dup2(t, 0);
347 dup2(t, 1);
348 dup2(t, 2);
349 close(t);
fe5c5547 350 environ = envinit;
affdaa4e
GM
351 /*
352 * -h : pass on name of host.
353 * -p : don't clobber the environment (so terminal type stays set).
354 */
355 execl("/bin/login", "login", "-h", host,
356 gotterminaltype ? "-p" : 0, 0);
8f2758db
SL
357 fatalperror(f, "/bin/login", errno);
358 /*NOTREACHED*/
359}
360
361fatal(f, msg)
362 int f;
363 char *msg;
364{
365 char buf[BUFSIZ];
366
1a33b848 367 (void) sprintf(buf, "telnetd: %s.\r\n", msg);
8f2758db 368 (void) write(f, buf, strlen(buf));
66b878f6
BJ
369 exit(1);
370}
371
8f2758db
SL
372fatalperror(f, msg, errno)
373 int f;
374 char *msg;
375 int errno;
376{
377 char buf[BUFSIZ];
378 extern char *sys_errlist[];
379
1a33b848 380 (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
8f2758db
SL
381 fatal(f, buf);
382}
383
5d78ef73
GM
384
385/*
386 * Check a descriptor to see if out of band data exists on it.
387 */
388
389
390stilloob(s)
391int s; /* socket number */
392{
393 static struct timeval timeout = { 0 };
394 fd_set excepts;
395 int value;
396
397 do {
398 FD_ZERO(&excepts);
399 FD_SET(s, &excepts);
400 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
401 } while ((value == -1) && (errno = EINTR));
402
403 if (value < 0) {
404 fatalperror(pty, "select", errno);
405 }
406 if (FD_ISSET(s, &excepts)) {
407 return 1;
408 } else {
409 return 0;
410 }
411}
412\f
66b878f6
BJ
413/*
414 * Main loop. Select from pty and network, and
415 * hand data to telnet receiver finite state machine.
416 */
417telnet(f, p)
418{
419 int on = 1;
0c285f22 420 char hostname[32];
66b878f6
BJ
421
422 net = f, pty = p;
423 ioctl(f, FIONBIO, &on);
424 ioctl(p, FIONBIO, &on);
affdaa4e
GM
425#if defined(SO_OOBINLINE)
426 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
427#endif /* defined(SO_OOBINLINE) */
66b878f6 428 signal(SIGTSTP, SIG_IGN);
8a53982e 429 signal(SIGCHLD, cleanup);
f4c5d9f9 430 setpgrp(0, 0);
66b878f6 431
da96b661 432 /*
5d78ef73 433 * Request to do remote echo and to suppress go ahead.
da96b661
SL
434 */
435 dooption(TELOPT_ECHO);
5d78ef73 436 dooption(TELOPT_SGA);
affdaa4e
GM
437 /*
438 * Is the client side a 4.2 (NOT 4.3) system? We need to know this
439 * because 4.2 clients are unable to deal with TCP urgent data.
440 *
441 * To find out, we send out a "DO ECHO". If the remote system
442 * answers "WILL ECHO" it is probably a 4.2 client, and we note
443 * that fact ("WILL ECHO" ==> that the client will echo what
444 * WE, the server, sends it; it does NOT mean that the client will
445 * echo the terminal input).
446 */
447 sprintf(nfrontp, doopt, TELOPT_ECHO);
448 nfrontp += sizeof doopt-2;
449 hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK;
450
0c285f22
SL
451 /*
452 * Show banner that getty never gave.
453 */
454 gethostname(hostname, sizeof (hostname));
455 sprintf(nfrontp, BANNER, hostname, "");
456 nfrontp += strlen(nfrontp);
affdaa4e
GM
457
458 /*
459 * Call telrcv() once to pick up anything received during
460 * terminal type negotiation.
461 */
462 telrcv();
463
66b878f6 464 for (;;) {
5d78ef73 465 fd_set ibits, obits, xbits;
66b878f6
BJ
466 register int c;
467
5d78ef73
GM
468 if (ncc < 0 && pcc < 0)
469 break;
470
471 FD_ZERO(&ibits);
472 FD_ZERO(&obits);
473 FD_ZERO(&xbits);
66b878f6
BJ
474 /*
475 * Never look for input if there's still
476 * stuff in the corresponding output buffer
477 */
5d78ef73
GM
478 if (nfrontp - nbackp || pcc > 0) {
479 FD_SET(f, &obits);
480 } else {
481 FD_SET(p, &ibits);
482 }
483 if (pfrontp - pbackp || ncc > 0) {
484 FD_SET(p, &obits);
485 } else {
486 FD_SET(f, &ibits);
487 }
488 if (!SYNCHing) {
489 FD_SET(f, &xbits);
490 }
491 if ((c = select(16, &ibits, &obits, &xbits,
492 (struct timeval *)0)) < 1) {
493 if (c == -1) {
494 if (errno == EINTR) {
495 continue;
496 }
497 }
66b878f6
BJ
498 sleep(5);
499 continue;
500 }
501
5d78ef73
GM
502 /*
503 * Any urgent data?
504 */
505 if (FD_ISSET(net, &xbits)) {
506 SYNCHing = 1;
507 }
508
66b878f6
BJ
509 /*
510 * Something to read from the network...
511 */
5d78ef73 512 if (FD_ISSET(net, &ibits)) {
affdaa4e 513#if !defined(SO_OOBINLINE)
5d78ef73
GM
514 /*
515 * In 4.2 (and some early 4.3) systems, the
516 * OOB indication and data handling in the kernel
517 * is such that if two separate TCP Urgent requests
518 * come in, one byte of TCP data will be overlaid.
519 * This is fatal for Telnet, but we try to live
520 * with it.
521 *
522 * In addition, in 4.2 (and...), a special protocol
523 * is needed to pick up the TCP Urgent data in
524 * the correct sequence.
525 *
526 * What we do is: if we think we are in urgent
527 * mode, we look to see if we are "at the mark".
528 * If we are, we do an OOB receive. If we run
529 * this twice, we will do the OOB receive twice,
530 * but the second will fail, since the second
531 * time we were "at the mark", but there wasn't
532 * any data there (the kernel doesn't reset
533 * "at the mark" until we do a normal read).
534 * Once we've read the OOB data, we go ahead
535 * and do normal reads.
536 *
537 * There is also another problem, which is that
538 * since the OOB byte we read doesn't put us
539 * out of OOB state, and since that byte is most
540 * likely the TELNET DM (data mark), we would
541 * stay in the TELNET SYNCH (SYNCHing) state.
542 * So, clocks to the rescue. If we've "just"
543 * received a DM, then we test for the
544 * presence of OOB data when the receive OOB
545 * fails (and AFTER we did the normal mode read
546 * to clear "at the mark").
547 */
548 if (SYNCHing) {
549 int atmark;
550
551 ioctl(net, SIOCATMARK, (char *)&atmark);
552 if (atmark) {
553 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
554 if ((ncc == -1) && (errno == EINVAL)) {
555 ncc = read(net, netibuf, sizeof (netibuf));
556 if (clocks.didnetreceive < clocks.gotDM) {
557 SYNCHing = stilloob(net);
558 }
559 }
560 } else {
561 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 562 }
5d78ef73
GM
563 } else {
564 ncc = read(net, netibuf, sizeof (netibuf));
565 }
566 settimer(didnetreceive);
affdaa4e 567#else /* !defined(SO_OOBINLINE)) */
5d78ef73 568 ncc = read(net, netibuf, sizeof (netibuf));
affdaa4e 569#endif /* !defined(SO_OOBINLINE)) */
5d78ef73
GM
570 if (ncc < 0 && errno == EWOULDBLOCK)
571 ncc = 0;
572 else {
573 if (ncc <= 0) {
574 break;
575 }
576 netip = netibuf;
577 }
66b878f6
BJ
578 }
579
580 /*
581 * Something to read from the pty...
582 */
5d78ef73 583 if (FD_ISSET(p, &ibits)) {
66b878f6
BJ
584 pcc = read(p, ptyibuf, BUFSIZ);
585 if (pcc < 0 && errno == EWOULDBLOCK)
586 pcc = 0;
587 else {
588 if (pcc <= 0)
589 break;
590 ptyip = ptyibuf;
591 }
592 }
593
594 while (pcc > 0) {
595 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
596 break;
597 c = *ptyip++ & 0377, pcc--;
598 if (c == IAC)
599 *nfrontp++ = c;
600 *nfrontp++ = c;
9f515693
GM
601 if (c == '\r') {
602 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
603 *nfrontp++ = *ptyip++ & 0377;
604 pcc--;
605 } else
606 *nfrontp++ = '\0';
607 }
66b878f6 608 }
5d78ef73 609 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
610 netflush();
611 if (ncc > 0)
612 telrcv();
5d78ef73 613 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
614 ptyflush();
615 }
616 cleanup();
617}
618
619/*
620 * State for recv fsm
621 */
622#define TS_DATA 0 /* base state */
623#define TS_IAC 1 /* look for double IAC's */
624#define TS_CR 2 /* CR-LF ->'s CR */
affdaa4e
GM
625#define TS_SB 3 /* throw away begin's... */
626#define TS_SE 4 /* ...end's (suboption negotiation) */
66b878f6
BJ
627#define TS_WILL 5 /* will option negotiation */
628#define TS_WONT 6 /* wont " */
629#define TS_DO 7 /* do " */
630#define TS_DONT 8 /* dont " */
631
632telrcv()
633{
634 register int c;
635 static int state = TS_DATA;
66b878f6
BJ
636
637 while (ncc > 0) {
638 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
639 return;
640 c = *netip++ & 0377, ncc--;
641 switch (state) {
642
8356bfad
GM
643 case TS_CR:
644 state = TS_DATA;
a6d8450f 645 if ((c == 0) || (c == '\n')) {
8356bfad 646 break;
a6d8450f 647 }
8356bfad
GM
648 /* FALL THROUGH */
649
66b878f6
BJ
650 case TS_DATA:
651 if (c == IAC) {
652 state = TS_IAC;
653 break;
654 }
655 if (inter > 0)
656 break;
9f515693
GM
657 /*
658 * We map \r\n ==> \n, since \r\n says
659 * that we want to be in column 1 of the next
660 * printable line, and \n is the standard
661 * unix way of saying that (\r is only good
662 * if CRMOD is set, which it normally is).
663 */
affdaa4e 664 if ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') {
9f515693
GM
665 if ((ncc > 0) && ('\n' == *netip)) {
666 netip++; ncc--;
667 c = '\n';
668 } else {
669 state = TS_CR;
670 }
a6d8450f
GM
671 }
672 *pfrontp++ = c;
66b878f6
BJ
673 break;
674
675 case TS_IAC:
676 switch (c) {
677
678 /*
679 * Send the process on the pty side an
680 * interrupt. Do this with a NULL or
681 * interrupt char; depending on the tty mode.
682 */
66b878f6
BJ
683 case IP:
684 interrupt();
685 break;
686
a65453b7
GM
687 case BREAK:
688 sendbrk();
689 break;
690
66b878f6
BJ
691 /*
692 * Are You There?
693 */
694 case AYT:
1a33b848
SL
695 strcpy(nfrontp, "\r\n[Yes]\r\n");
696 nfrontp += 9;
66b878f6
BJ
697 break;
698
5d78ef73
GM
699 /*
700 * Abort Output
701 */
702 case AO: {
703 struct ltchars tmpltc;
704
705 ptyflush(); /* half-hearted */
706 ioctl(pty, TIOCGLTC, &tmpltc);
707 if (tmpltc.t_flushc != '\377') {
708 *pfrontp++ = tmpltc.t_flushc;
709 }
615dc3cd 710 netclear(); /* clear buffer back */
5d78ef73
GM
711 *nfrontp++ = IAC;
712 *nfrontp++ = DM;
c77c3bec 713 neturg = nfrontp-1; /* off by one XXX */
5d78ef73
GM
714 break;
715 }
716
66b878f6
BJ
717 /*
718 * Erase Character and
719 * Erase Line
720 */
721 case EC:
5d78ef73
GM
722 case EL: {
723 struct sgttyb b;
724 char ch;
725
726 ptyflush(); /* half-hearted */
727 ioctl(pty, TIOCGETP, &b);
728 ch = (c == EC) ?
729 b.sg_erase : b.sg_kill;
730 if (ch != '\377') {
731 *pfrontp++ = ch;
732 }
733 break;
734 }
66b878f6
BJ
735
736 /*
737 * Check for urgent data...
738 */
739 case DM:
5d78ef73
GM
740 SYNCHing = stilloob(net);
741 settimer(gotDM);
66b878f6
BJ
742 break;
743
5d78ef73 744
66b878f6
BJ
745 /*
746 * Begin option subnegotiation...
747 */
748 case SB:
affdaa4e 749 state = TS_SB;
66b878f6
BJ
750 continue;
751
752 case WILL:
0e376915
GM
753 state = TS_WILL;
754 continue;
755
66b878f6 756 case WONT:
0e376915
GM
757 state = TS_WONT;
758 continue;
759
66b878f6 760 case DO:
0e376915
GM
761 state = TS_DO;
762 continue;
763
66b878f6 764 case DONT:
0e376915 765 state = TS_DONT;
66b878f6
BJ
766 continue;
767
768 case IAC:
769 *pfrontp++ = c;
770 break;
771 }
772 state = TS_DATA;
773 break;
774
affdaa4e
GM
775 case TS_SB:
776 if (c == IAC) {
777 state = TS_SE;
778 } else {
779 SB_ACCUM(c);
780 }
66b878f6
BJ
781 break;
782
affdaa4e
GM
783 case TS_SE:
784 if (c != SE) {
785 if (c != IAC) {
786 SB_ACCUM(IAC);
787 }
788 SB_ACCUM(c);
789 state = TS_SB;
790 } else {
791 SB_TERM();
792 suboption(); /* handle sub-option */
793 state = TS_DATA;
794 }
66b878f6
BJ
795 break;
796
797 case TS_WILL:
affdaa4e 798 if (hisopts[c] != OPT_WILL)
66b878f6
BJ
799 willoption(c);
800 state = TS_DATA;
801 continue;
802
803 case TS_WONT:
affdaa4e 804 if (hisopts[c] != OPT_WONT)
66b878f6
BJ
805 wontoption(c);
806 state = TS_DATA;
807 continue;
808
809 case TS_DO:
affdaa4e 810 if (myopts[c] != OPT_DO)
66b878f6
BJ
811 dooption(c);
812 state = TS_DATA;
813 continue;
814
815 case TS_DONT:
affdaa4e
GM
816 if (myopts[c] != OPT_DONT) {
817 dontoption(c);
66b878f6
BJ
818 }
819 state = TS_DATA;
820 continue;
821
822 default:
de3b21e8 823 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
824 exit(1);
825 }
826 }
827}
828
829willoption(option)
830 int option;
831{
832 char *fmt;
833
834 switch (option) {
835
836 case TELOPT_BINARY:
837 mode(RAW, 0);
0e376915
GM
838 fmt = doopt;
839 break;
66b878f6
BJ
840
841 case TELOPT_ECHO:
affdaa4e
GM
842 not42 = 0; /* looks like a 4.2 system */
843 /*
844 * Now, in a 4.2 system, to break them out of ECHOing
845 * (to the terminal) mode, we need to send a "WILL ECHO".
846 * Kludge upon kludge!
847 */
848 if (myopts[TELOPT_ECHO] == OPT_DO) {
849 dooption(TELOPT_ECHO);
850 }
851 fmt = dont;
0e376915 852 break;
66b878f6 853
affdaa4e 854 case TELOPT_TTYPE:
66b878f6 855 case TELOPT_SGA:
66b878f6
BJ
856 fmt = doopt;
857 break;
858
859 case TELOPT_TM:
860 fmt = dont;
861 break;
862
863 default:
864 fmt = dont;
865 break;
866 }
0e376915 867 if (fmt == doopt) {
affdaa4e 868 hisopts[option] = OPT_WILL;
0e376915 869 } else {
affdaa4e 870 hisopts[option] = OPT_WONT;
0e376915 871 }
13646f15 872 sprintf(nfrontp, fmt, option);
da96b661 873 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
874}
875
876wontoption(option)
877 int option;
878{
879 char *fmt;
880
881 switch (option) {
66b878f6 882 case TELOPT_ECHO:
affdaa4e 883 not42 = 1; /* doesn't seem to be a 4.2 system */
0e376915 884 break;
66b878f6
BJ
885
886 case TELOPT_BINARY:
887 mode(0, RAW);
66b878f6 888 break;
66b878f6 889 }
0e376915 890 fmt = dont;
affdaa4e 891 hisopts[option] = OPT_WONT;
66b878f6 892 sprintf(nfrontp, fmt, option);
da96b661 893 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
894}
895
896dooption(option)
897 int option;
898{
899 char *fmt;
900
901 switch (option) {
902
903 case TELOPT_TM:
904 fmt = wont;
905 break;
906
907 case TELOPT_ECHO:
908 mode(ECHO|CRMOD, 0);
0e376915
GM
909 fmt = will;
910 break;
66b878f6
BJ
911
912 case TELOPT_BINARY:
913 mode(RAW, 0);
0e376915
GM
914 fmt = will;
915 break;
66b878f6
BJ
916
917 case TELOPT_SGA:
66b878f6
BJ
918 fmt = will;
919 break;
920
921 default:
922 fmt = wont;
923 break;
924 }
0e376915 925 if (fmt == will) {
affdaa4e 926 myopts[option] = OPT_DO;
0e376915 927 } else {
affdaa4e 928 myopts[option] = OPT_DONT;
0e376915 929 }
66b878f6 930 sprintf(nfrontp, fmt, option);
da96b661 931 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
932}
933
affdaa4e
GM
934
935dontoption(option)
936int option;
937{
938 char *fmt;
939
940 switch (option) {
941 case TELOPT_ECHO: /* we should stop echoing */
942 mode(0, ECHO|CRMOD);
943 fmt = wont;
944 break;
945 default:
946 fmt = wont;
947 break;
948 }
949 if (fmt = wont) {
950 myopts[option] = OPT_DONT;
951 } else {
952 myopts[option] = OPT_DO;
953 }
954 sprintf(nfrontp, fmt, option);
955 nfrontp += sizeof (wont) - 2;
956}
957
958/*
959 * suboption()
960 *
961 * Look at the sub-option buffer, and try to be helpful to the other
962 * side.
963 *
964 * Currently we recognize:
965 *
966 * (nothing - we only do terminal type at start-up time)
967 */
968
969suboption()
970{
971 switch (subbuffer[0]&0xff) {
972 default:
973 ;
974 }
975}
976
66b878f6
BJ
977mode(on, off)
978 int on, off;
979{
980 struct sgttyb b;
981
982 ptyflush();
983 ioctl(pty, TIOCGETP, &b);
984 b.sg_flags |= on;
985 b.sg_flags &= ~off;
986 ioctl(pty, TIOCSETP, &b);
987}
988
989/*
990 * Send interrupt to process on other side of pty.
991 * If it is in raw mode, just write NULL;
992 * otherwise, write intr char.
993 */
994interrupt()
995{
996 struct sgttyb b;
997 struct tchars tchars;
998
999 ptyflush(); /* half-hearted */
1000 ioctl(pty, TIOCGETP, &b);
1001 if (b.sg_flags & RAW) {
1002 *pfrontp++ = '\0';
1003 return;
1004 }
1005 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1006 '\177' : tchars.t_intrc;
1007}
1008
a65453b7
GM
1009/*
1010 * Send quit to process on other side of pty.
1011 * If it is in raw mode, just write NULL;
1012 * otherwise, write quit char.
1013 */
1014sendbrk()
1015{
1016 struct sgttyb b;
1017 struct tchars tchars;
1018
1019 ptyflush(); /* half-hearted */
1020 ioctl(pty, TIOCGETP, &b);
1021 if (b.sg_flags & RAW) {
1022 *pfrontp++ = '\0';
1023 return;
1024 }
1025 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1026 '\034' : tchars.t_quitc;
1027}
1028
66b878f6
BJ
1029ptyflush()
1030{
1031 int n;
1032
1033 if ((n = pfrontp - pbackp) > 0)
1034 n = write(pty, pbackp, n);
9f005877
SL
1035 if (n < 0)
1036 return;
66b878f6
BJ
1037 pbackp += n;
1038 if (pbackp == pfrontp)
1039 pbackp = pfrontp = ptyobuf;
1040}
615dc3cd
GM
1041\f
1042/*
1043 * nextitem()
1044 *
1045 * Return the address of the next "item" in the TELNET data
1046 * stream. This will be the address of the next character if
1047 * the current address is a user data character, or it will
1048 * be the address of the character following the TELNET command
1049 * if the current address is a TELNET IAC ("I Am a Command")
1050 * character.
1051 */
66b878f6 1052
615dc3cd
GM
1053char *
1054nextitem(current)
1055char *current;
66b878f6 1056{
615dc3cd
GM
1057 if ((*current&0xff) != IAC) {
1058 return current+1;
1059 }
1060 switch (*(current+1)&0xff) {
1061 case DO:
1062 case DONT:
1063 case WILL:
1064 case WONT:
1065 return current+3;
1066 case SB: /* loop forever looking for the SE */
1067 {
1068 register char *look = current+2;
66b878f6 1069
615dc3cd
GM
1070 for (;;) {
1071 if ((*look++&0xff) == IAC) {
1072 if ((*look++&0xff) == SE) {
1073 return look;
1074 }
1075 }
1076 }
9f005877 1077 }
615dc3cd
GM
1078 default:
1079 return current+2;
1080 }
66b878f6 1081}
5d78ef73
GM
1082
1083
615dc3cd
GM
1084/*
1085 * netclear()
1086 *
1087 * We are about to do a TELNET SYNCH operation. Clear
1088 * the path to the network.
1089 *
1090 * Things are a bit tricky since we may have sent the first
1091 * byte or so of a previous TELNET command into the network.
1092 * So, we have to scan the network buffer from the beginning
1093 * until we are up to where we want to be.
1094 *
1095 * A side effect of what we do, just to keep things
1096 * simple, is to clear the urgent data pointer. The principal
1097 * caller should be setting the urgent data pointer AFTER calling
1098 * us in any case.
1099 */
1100
1101netclear()
1102{
1103 register char *thisitem, *next;
1104 char *good;
1105#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1106 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1107
1108 thisitem = netobuf;
1109
1110 while ((next = nextitem(thisitem)) <= nbackp) {
1111 thisitem = next;
1112 }
1113
1114 /* Now, thisitem is first before/at boundary. */
1115
1116 good = netobuf; /* where the good bytes go */
1117
1118 while (nfrontp > thisitem) {
1119 if (wewant(thisitem)) {
1120 int length;
1121
1122 next = thisitem;
1123 do {
1124 next = nextitem(next);
1125 } while (wewant(next) && (nfrontp > next));
1126 length = next-thisitem;
1127 bcopy(thisitem, good, length);
1128 good += length;
1129 thisitem = next;
1130 } else {
1131 thisitem = nextitem(thisitem);
1132 }
1133 }
1134
1135 nbackp = netobuf;
1136 nfrontp = good; /* next byte to be sent */
1137 neturg = 0;
1138}
1139\f
5d78ef73
GM
1140/*
1141 * netflush
1142 * Send as much data as possible to the network,
1143 * handling requests for urgent data.
1144 */
1145
1146
1147netflush()
1148{
1149 int n;
1150
1151 if ((n = nfrontp - nbackp) > 0) {
affdaa4e
GM
1152 /*
1153 * if no urgent data, or if the other side appears to be an
1154 * old 4.2 client (and thus unable to survive TCP urgent data),
1155 * write the entire buffer in non-OOB mode.
1156 */
1157 if ((neturg == 0) || (not42 == 0)) {
5d78ef73
GM
1158 n = write(net, nbackp, n); /* normal write */
1159 } else {
1160 n = neturg - nbackp;
1161 /*
1162 * In 4.2 (and 4.3) systems, there is some question about
1163 * what byte in a sendOOB operation is the "OOB" data.
1164 * To make ourselves compatible, we only send ONE byte
1165 * out of band, the one WE THINK should be OOB (though
1166 * we really have more the TCP philosophy of urgent data
1167 * rather than the Unix philosophy of OOB data).
1168 */
1169 if (n > 1) {
1170 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1171 } else {
1172 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1173 }
1174 }
1175 }
1176 if (n < 0) {
1177 if (errno == EWOULDBLOCK)
1178 return;
1179 /* should blow this guy away... */
1180 return;
1181 }
1182 nbackp += n;
1183 if (nbackp >= neturg) {
1184 neturg = 0;
1185 }
1186 if (nbackp == nfrontp) {
1187 nbackp = nfrontp = netobuf;
1188 }
1189}
66b878f6
BJ
1190
1191cleanup()
1192{
66b878f6
BJ
1193
1194 rmut();
a7f6263e 1195 vhangup(); /* XXX */
ff24c640 1196 shutdown(net, 2);
66b878f6
BJ
1197 exit(1);
1198}
1199
1200#include <utmp.h>
1201
1202struct utmp wtmp;
1203char wtmpf[] = "/usr/adm/wtmp";
122f5efc
JB
1204char utmpf[] = "/etc/utmp";
1205#define SCPYN(a, b) strncpy(a, b, sizeof(a))
1206#define SCMPN(a, b) strncmp(a, b, sizeof(a))
66b878f6
BJ
1207
1208rmut()
1209{
1210 register f;
1211 int found = 0;
122f5efc
JB
1212 struct utmp *u, *utmp;
1213 int nutmp;
1214 struct stat statbf;
66b878f6 1215
122f5efc 1216 f = open(utmpf, O_RDWR);
66b878f6 1217 if (f >= 0) {
122f5efc
JB
1218 fstat(f, &statbf);
1219 utmp = (struct utmp *)malloc(statbf.st_size);
1220 if (!utmp)
1221 syslog(LOG_ERR, "utmp malloc failed");
1222 if (statbf.st_size && utmp) {
1223 nutmp = read(f, utmp, statbf.st_size);
1224 nutmp /= sizeof(struct utmp);
1225
1226 for (u = utmp ; u < &utmp[nutmp] ; u++) {
1227 if (SCMPN(u->ut_line, line+5) ||
1228 u->ut_name[0]==0)
1229 continue;
1230 lseek(f, ((long)u)-((long)utmp), L_SET);
1231 SCPYN(u->ut_name, "");
1232 SCPYN(u->ut_host, "");
1233 time(&u->ut_time);
1234 write(f, (char *)u, sizeof(wtmp));
1235 found++;
1236 }
66b878f6
BJ
1237 }
1238 close(f);
1239 }
1240 if (found) {
1a33b848 1241 f = open(wtmpf, O_WRONLY|O_APPEND);
66b878f6
BJ
1242 if (f >= 0) {
1243 SCPYN(wtmp.ut_line, line+5);
1244 SCPYN(wtmp.ut_name, "");
37c640e2 1245 SCPYN(wtmp.ut_host, "");
66b878f6 1246 time(&wtmp.ut_time);
122f5efc 1247 write(f, (char *)&wtmp, sizeof(wtmp));
66b878f6
BJ
1248 close(f);
1249 }
1250 }
1251 chmod(line, 0666);
1252 chown(line, 0, 0);
1253 line[strlen("/dev/")] = 'p';
1254 chmod(line, 0666);
1255 chown(line, 0, 0);
1256}