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