upcase title line
[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
bdb993e7 14static char sccsid[] = "@(#)telnetd.c 5.19 (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);
affdaa4e
GM
385#if defined(SO_OOBINLINE)
386 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
387#endif /* defined(SO_OOBINLINE) */
66b878f6 388 signal(SIGTSTP, SIG_IGN);
8a53982e 389 signal(SIGCHLD, cleanup);
f4c5d9f9 390 setpgrp(0, 0);
66b878f6 391
da96b661 392 /*
5d78ef73 393 * Request to do remote echo and to suppress go ahead.
da96b661 394 */
d8b5e42c
GM
395 if (!myopts[TELOPT_ECHO]) {
396 dooption(TELOPT_ECHO);
397 }
398 if (!myopts[TELOPT_SGA]) {
399 dooption(TELOPT_SGA);
400 }
affdaa4e
GM
401 /*
402 * Is the client side a 4.2 (NOT 4.3) system? We need to know this
403 * because 4.2 clients are unable to deal with TCP urgent data.
404 *
405 * To find out, we send out a "DO ECHO". If the remote system
406 * answers "WILL ECHO" it is probably a 4.2 client, and we note
407 * that fact ("WILL ECHO" ==> that the client will echo what
408 * WE, the server, sends it; it does NOT mean that the client will
409 * echo the terminal input).
410 */
411 sprintf(nfrontp, doopt, TELOPT_ECHO);
412 nfrontp += sizeof doopt-2;
d8b5e42c 413 hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
affdaa4e 414
0c285f22
SL
415 /*
416 * Show banner that getty never gave.
10dc182f
GM
417 *
418 * The banner includes some null's (for TELNET CR disambiguation),
419 * so we have to be somewhat complicated.
0c285f22 420 */
10dc182f 421
0c285f22 422 gethostname(hostname, sizeof (hostname));
10dc182f
GM
423
424 bcopy(BANNER1, nfrontp, sizeof BANNER1 -1);
425 nfrontp += sizeof BANNER1 - 1;
426 bcopy(hostname, nfrontp, strlen(hostname));
427 nfrontp += strlen(hostname);
428 bcopy(BANNER2, nfrontp, sizeof BANNER2 -1);
429 nfrontp += sizeof BANNER2 - 1;
affdaa4e
GM
430
431 /*
432 * Call telrcv() once to pick up anything received during
433 * terminal type negotiation.
434 */
435 telrcv();
436
66b878f6 437 for (;;) {
5d78ef73 438 fd_set ibits, obits, xbits;
66b878f6
BJ
439 register int c;
440
5d78ef73
GM
441 if (ncc < 0 && pcc < 0)
442 break;
443
444 FD_ZERO(&ibits);
445 FD_ZERO(&obits);
446 FD_ZERO(&xbits);
66b878f6
BJ
447 /*
448 * Never look for input if there's still
449 * stuff in the corresponding output buffer
450 */
5d78ef73
GM
451 if (nfrontp - nbackp || pcc > 0) {
452 FD_SET(f, &obits);
453 } else {
454 FD_SET(p, &ibits);
455 }
456 if (pfrontp - pbackp || ncc > 0) {
457 FD_SET(p, &obits);
458 } else {
459 FD_SET(f, &ibits);
460 }
461 if (!SYNCHing) {
462 FD_SET(f, &xbits);
463 }
464 if ((c = select(16, &ibits, &obits, &xbits,
465 (struct timeval *)0)) < 1) {
466 if (c == -1) {
467 if (errno == EINTR) {
468 continue;
469 }
470 }
66b878f6
BJ
471 sleep(5);
472 continue;
473 }
474
5d78ef73
GM
475 /*
476 * Any urgent data?
477 */
478 if (FD_ISSET(net, &xbits)) {
479 SYNCHing = 1;
480 }
481
66b878f6
BJ
482 /*
483 * Something to read from the network...
484 */
5d78ef73 485 if (FD_ISSET(net, &ibits)) {
affdaa4e 486#if !defined(SO_OOBINLINE)
5d78ef73 487 /*
5eddff6d 488 * In 4.2 (and 4.3 beta) systems, the
5d78ef73
GM
489 * OOB indication and data handling in the kernel
490 * is such that if two separate TCP Urgent requests
491 * come in, one byte of TCP data will be overlaid.
492 * This is fatal for Telnet, but we try to live
493 * with it.
494 *
495 * In addition, in 4.2 (and...), a special protocol
496 * is needed to pick up the TCP Urgent data in
497 * the correct sequence.
498 *
499 * What we do is: if we think we are in urgent
500 * mode, we look to see if we are "at the mark".
501 * If we are, we do an OOB receive. If we run
502 * this twice, we will do the OOB receive twice,
503 * but the second will fail, since the second
504 * time we were "at the mark", but there wasn't
505 * any data there (the kernel doesn't reset
506 * "at the mark" until we do a normal read).
507 * Once we've read the OOB data, we go ahead
508 * and do normal reads.
509 *
510 * There is also another problem, which is that
511 * since the OOB byte we read doesn't put us
512 * out of OOB state, and since that byte is most
513 * likely the TELNET DM (data mark), we would
514 * stay in the TELNET SYNCH (SYNCHing) state.
515 * So, clocks to the rescue. If we've "just"
516 * received a DM, then we test for the
517 * presence of OOB data when the receive OOB
518 * fails (and AFTER we did the normal mode read
519 * to clear "at the mark").
520 */
521 if (SYNCHing) {
522 int atmark;
523
524 ioctl(net, SIOCATMARK, (char *)&atmark);
525 if (atmark) {
526 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
527 if ((ncc == -1) && (errno == EINVAL)) {
528 ncc = read(net, netibuf, sizeof (netibuf));
d8b5e42c 529 if (sequenceIs(didnetreceive, gotDM)) {
5d78ef73
GM
530 SYNCHing = stilloob(net);
531 }
532 }
533 } else {
534 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 535 }
5d78ef73
GM
536 } else {
537 ncc = read(net, netibuf, sizeof (netibuf));
538 }
539 settimer(didnetreceive);
affdaa4e 540#else /* !defined(SO_OOBINLINE)) */
5d78ef73 541 ncc = read(net, netibuf, sizeof (netibuf));
affdaa4e 542#endif /* !defined(SO_OOBINLINE)) */
5d78ef73
GM
543 if (ncc < 0 && errno == EWOULDBLOCK)
544 ncc = 0;
545 else {
546 if (ncc <= 0) {
547 break;
548 }
549 netip = netibuf;
550 }
66b878f6
BJ
551 }
552
553 /*
554 * Something to read from the pty...
555 */
5d78ef73 556 if (FD_ISSET(p, &ibits)) {
66b878f6
BJ
557 pcc = read(p, ptyibuf, BUFSIZ);
558 if (pcc < 0 && errno == EWOULDBLOCK)
559 pcc = 0;
560 else {
561 if (pcc <= 0)
562 break;
563 ptyip = ptyibuf;
564 }
565 }
566
567 while (pcc > 0) {
568 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
569 break;
570 c = *ptyip++ & 0377, pcc--;
571 if (c == IAC)
572 *nfrontp++ = c;
573 *nfrontp++ = c;
bdb993e7
KB
574 /* Don't do CR-NUL if we are in binary mode */
575 if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
9f515693
GM
576 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
577 *nfrontp++ = *ptyip++ & 0377;
578 pcc--;
579 } else
580 *nfrontp++ = '\0';
581 }
66b878f6 582 }
5d78ef73 583 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
584 netflush();
585 if (ncc > 0)
586 telrcv();
5d78ef73 587 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
588 ptyflush();
589 }
590 cleanup();
591}
592
593/*
594 * State for recv fsm
595 */
596#define TS_DATA 0 /* base state */
597#define TS_IAC 1 /* look for double IAC's */
598#define TS_CR 2 /* CR-LF ->'s CR */
affdaa4e
GM
599#define TS_SB 3 /* throw away begin's... */
600#define TS_SE 4 /* ...end's (suboption negotiation) */
66b878f6
BJ
601#define TS_WILL 5 /* will option negotiation */
602#define TS_WONT 6 /* wont " */
603#define TS_DO 7 /* do " */
604#define TS_DONT 8 /* dont " */
605
606telrcv()
607{
608 register int c;
609 static int state = TS_DATA;
66b878f6
BJ
610
611 while (ncc > 0) {
612 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
613 return;
614 c = *netip++ & 0377, ncc--;
615 switch (state) {
616
8356bfad
GM
617 case TS_CR:
618 state = TS_DATA;
a6d8450f 619 if ((c == 0) || (c == '\n')) {
8356bfad 620 break;
a6d8450f 621 }
8356bfad
GM
622 /* FALL THROUGH */
623
66b878f6
BJ
624 case TS_DATA:
625 if (c == IAC) {
626 state = TS_IAC;
627 break;
628 }
629 if (inter > 0)
630 break;
9f515693
GM
631 /*
632 * We map \r\n ==> \n, since \r\n says
633 * that we want to be in column 1 of the next
634 * printable line, and \n is the standard
635 * unix way of saying that (\r is only good
636 * if CRMOD is set, which it normally is).
637 */
bdb993e7 638 if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
9f515693
GM
639 if ((ncc > 0) && ('\n' == *netip)) {
640 netip++; ncc--;
641 c = '\n';
642 } else {
643 state = TS_CR;
644 }
a6d8450f
GM
645 }
646 *pfrontp++ = c;
66b878f6
BJ
647 break;
648
649 case TS_IAC:
650 switch (c) {
651
652 /*
653 * Send the process on the pty side an
654 * interrupt. Do this with a NULL or
655 * interrupt char; depending on the tty mode.
656 */
66b878f6
BJ
657 case IP:
658 interrupt();
659 break;
660
a65453b7
GM
661 case BREAK:
662 sendbrk();
663 break;
664
66b878f6
BJ
665 /*
666 * Are You There?
667 */
668 case AYT:
1a33b848
SL
669 strcpy(nfrontp, "\r\n[Yes]\r\n");
670 nfrontp += 9;
66b878f6
BJ
671 break;
672
5d78ef73
GM
673 /*
674 * Abort Output
675 */
676 case AO: {
677 struct ltchars tmpltc;
678
679 ptyflush(); /* half-hearted */
680 ioctl(pty, TIOCGLTC, &tmpltc);
681 if (tmpltc.t_flushc != '\377') {
682 *pfrontp++ = tmpltc.t_flushc;
683 }
615dc3cd 684 netclear(); /* clear buffer back */
5d78ef73
GM
685 *nfrontp++ = IAC;
686 *nfrontp++ = DM;
c77c3bec 687 neturg = nfrontp-1; /* off by one XXX */
5d78ef73
GM
688 break;
689 }
690
66b878f6
BJ
691 /*
692 * Erase Character and
693 * Erase Line
694 */
695 case EC:
5d78ef73
GM
696 case EL: {
697 struct sgttyb b;
698 char ch;
699
700 ptyflush(); /* half-hearted */
701 ioctl(pty, TIOCGETP, &b);
702 ch = (c == EC) ?
703 b.sg_erase : b.sg_kill;
704 if (ch != '\377') {
705 *pfrontp++ = ch;
706 }
707 break;
708 }
66b878f6
BJ
709
710 /*
711 * Check for urgent data...
712 */
713 case DM:
5d78ef73
GM
714 SYNCHing = stilloob(net);
715 settimer(gotDM);
66b878f6
BJ
716 break;
717
5d78ef73 718
66b878f6
BJ
719 /*
720 * Begin option subnegotiation...
721 */
722 case SB:
affdaa4e 723 state = TS_SB;
66b878f6
BJ
724 continue;
725
726 case WILL:
0e376915
GM
727 state = TS_WILL;
728 continue;
729
66b878f6 730 case WONT:
0e376915
GM
731 state = TS_WONT;
732 continue;
733
66b878f6 734 case DO:
0e376915
GM
735 state = TS_DO;
736 continue;
737
66b878f6 738 case DONT:
0e376915 739 state = TS_DONT;
66b878f6
BJ
740 continue;
741
742 case IAC:
743 *pfrontp++ = c;
744 break;
745 }
746 state = TS_DATA;
747 break;
748
affdaa4e
GM
749 case TS_SB:
750 if (c == IAC) {
751 state = TS_SE;
752 } else {
753 SB_ACCUM(c);
754 }
66b878f6
BJ
755 break;
756
affdaa4e
GM
757 case TS_SE:
758 if (c != SE) {
759 if (c != IAC) {
760 SB_ACCUM(IAC);
761 }
762 SB_ACCUM(c);
763 state = TS_SB;
764 } else {
765 SB_TERM();
766 suboption(); /* handle sub-option */
767 state = TS_DATA;
768 }
66b878f6
BJ
769 break;
770
771 case TS_WILL:
d8b5e42c 772 if (hisopts[c] != OPT_YES)
66b878f6
BJ
773 willoption(c);
774 state = TS_DATA;
775 continue;
776
777 case TS_WONT:
d8b5e42c 778 if (hisopts[c] != OPT_NO)
66b878f6
BJ
779 wontoption(c);
780 state = TS_DATA;
781 continue;
782
783 case TS_DO:
d8b5e42c 784 if (myopts[c] != OPT_YES)
66b878f6
BJ
785 dooption(c);
786 state = TS_DATA;
787 continue;
788
789 case TS_DONT:
d8b5e42c 790 if (myopts[c] != OPT_NO) {
affdaa4e 791 dontoption(c);
66b878f6
BJ
792 }
793 state = TS_DATA;
794 continue;
795
796 default:
5eddff6d 797 syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
de3b21e8 798 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
799 exit(1);
800 }
801 }
802}
803
804willoption(option)
805 int option;
806{
807 char *fmt;
808
809 switch (option) {
810
811 case TELOPT_BINARY:
812 mode(RAW, 0);
0e376915
GM
813 fmt = doopt;
814 break;
66b878f6
BJ
815
816 case TELOPT_ECHO:
affdaa4e
GM
817 not42 = 0; /* looks like a 4.2 system */
818 /*
819 * Now, in a 4.2 system, to break them out of ECHOing
820 * (to the terminal) mode, we need to send a "WILL ECHO".
821 * Kludge upon kludge!
822 */
d8b5e42c 823 if (myopts[TELOPT_ECHO] == OPT_YES) {
affdaa4e
GM
824 dooption(TELOPT_ECHO);
825 }
826 fmt = dont;
0e376915 827 break;
66b878f6 828
affdaa4e 829 case TELOPT_TTYPE:
d8b5e42c
GM
830 settimer(ttypeopt);
831 if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
832 hisopts[TELOPT_TTYPE] = OPT_YES;
833 return;
834 }
835 fmt = doopt;
836 break;
837
66b878f6 838 case TELOPT_SGA:
66b878f6
BJ
839 fmt = doopt;
840 break;
841
842 case TELOPT_TM:
843 fmt = dont;
844 break;
845
846 default:
847 fmt = dont;
848 break;
849 }
0e376915 850 if (fmt == doopt) {
d8b5e42c 851 hisopts[option] = OPT_YES;
0e376915 852 } else {
d8b5e42c 853 hisopts[option] = OPT_NO;
0e376915 854 }
13646f15 855 sprintf(nfrontp, fmt, option);
da96b661 856 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
857}
858
859wontoption(option)
860 int option;
861{
862 char *fmt;
863
864 switch (option) {
66b878f6 865 case TELOPT_ECHO:
affdaa4e 866 not42 = 1; /* doesn't seem to be a 4.2 system */
0e376915 867 break;
66b878f6
BJ
868
869 case TELOPT_BINARY:
870 mode(0, RAW);
66b878f6 871 break;
10a3b37e
GM
872
873 case TELOPT_TTYPE:
874 settimer(ttypeopt);
875 break;
66b878f6 876 }
10a3b37e 877
0e376915 878 fmt = dont;
d8b5e42c 879 hisopts[option] = OPT_NO;
66b878f6 880 sprintf(nfrontp, fmt, option);
da96b661 881 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
882}
883
884dooption(option)
885 int option;
886{
887 char *fmt;
888
889 switch (option) {
890
891 case TELOPT_TM:
892 fmt = wont;
893 break;
894
895 case TELOPT_ECHO:
896 mode(ECHO|CRMOD, 0);
0e376915
GM
897 fmt = will;
898 break;
66b878f6
BJ
899
900 case TELOPT_BINARY:
901 mode(RAW, 0);
0e376915
GM
902 fmt = will;
903 break;
66b878f6
BJ
904
905 case TELOPT_SGA:
66b878f6
BJ
906 fmt = will;
907 break;
908
909 default:
910 fmt = wont;
911 break;
912 }
0e376915 913 if (fmt == will) {
d8b5e42c 914 myopts[option] = OPT_YES;
0e376915 915 } else {
d8b5e42c 916 myopts[option] = OPT_NO;
0e376915 917 }
66b878f6 918 sprintf(nfrontp, fmt, option);
da96b661 919 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
920}
921
affdaa4e
GM
922
923dontoption(option)
924int option;
925{
926 char *fmt;
927
928 switch (option) {
929 case TELOPT_ECHO: /* we should stop echoing */
930 mode(0, ECHO|CRMOD);
931 fmt = wont;
932 break;
d8b5e42c 933
affdaa4e
GM
934 default:
935 fmt = wont;
936 break;
937 }
d8b5e42c 938
affdaa4e 939 if (fmt = wont) {
d8b5e42c 940 myopts[option] = OPT_NO;
affdaa4e 941 } else {
d8b5e42c 942 myopts[option] = OPT_YES;
affdaa4e
GM
943 }
944 sprintf(nfrontp, fmt, option);
945 nfrontp += sizeof (wont) - 2;
946}
947
948/*
949 * suboption()
950 *
951 * Look at the sub-option buffer, and try to be helpful to the other
952 * side.
953 *
954 * Currently we recognize:
955 *
d8b5e42c 956 * Terminal type is
affdaa4e
GM
957 */
958
959suboption()
960{
d8b5e42c
GM
961 switch (SB_GET()) {
962 case TELOPT_TTYPE: { /* Yaaaay! */
963 static char terminalname[5+41] = "TERM=";
964
965 settimer(ttypesubopt);
966
967 if (SB_GET() != TELQUAL_IS) {
968 return; /* ??? XXX but, this is the most robust */
969 }
970
971 terminaltype = terminalname+strlen(terminalname);
972
973 while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
974 !SB_EOF()) {
975 register int c;
976
977 c = SB_GET();
978 if (isupper(c)) {
979 c = tolower(c);
980 }
981 *terminaltype++ = c; /* accumulate name */
982 }
983 *terminaltype = 0;
984 terminaltype = terminalname;
985 break;
986 }
987
affdaa4e
GM
988 default:
989 ;
990 }
991}
992
66b878f6
BJ
993mode(on, off)
994 int on, off;
995{
996 struct sgttyb b;
997
998 ptyflush();
999 ioctl(pty, TIOCGETP, &b);
1000 b.sg_flags |= on;
1001 b.sg_flags &= ~off;
1002 ioctl(pty, TIOCSETP, &b);
1003}
1004
1005/*
1006 * Send interrupt to process on other side of pty.
1007 * If it is in raw mode, just write NULL;
1008 * otherwise, write intr char.
1009 */
1010interrupt()
1011{
1012 struct sgttyb b;
1013 struct tchars tchars;
1014
1015 ptyflush(); /* half-hearted */
1016 ioctl(pty, TIOCGETP, &b);
1017 if (b.sg_flags & RAW) {
1018 *pfrontp++ = '\0';
1019 return;
1020 }
1021 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1022 '\177' : tchars.t_intrc;
1023}
1024
a65453b7
GM
1025/*
1026 * Send quit to process on other side of pty.
1027 * If it is in raw mode, just write NULL;
1028 * otherwise, write quit char.
1029 */
1030sendbrk()
1031{
1032 struct sgttyb b;
1033 struct tchars tchars;
1034
1035 ptyflush(); /* half-hearted */
1036 ioctl(pty, TIOCGETP, &b);
1037 if (b.sg_flags & RAW) {
1038 *pfrontp++ = '\0';
1039 return;
1040 }
1041 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1042 '\034' : tchars.t_quitc;
1043}
1044
66b878f6
BJ
1045ptyflush()
1046{
1047 int n;
1048
1049 if ((n = pfrontp - pbackp) > 0)
1050 n = write(pty, pbackp, n);
9f005877
SL
1051 if (n < 0)
1052 return;
66b878f6
BJ
1053 pbackp += n;
1054 if (pbackp == pfrontp)
1055 pbackp = pfrontp = ptyobuf;
1056}
615dc3cd
GM
1057\f
1058/*
1059 * nextitem()
1060 *
1061 * Return the address of the next "item" in the TELNET data
1062 * stream. This will be the address of the next character if
1063 * the current address is a user data character, or it will
1064 * be the address of the character following the TELNET command
1065 * if the current address is a TELNET IAC ("I Am a Command")
1066 * character.
1067 */
66b878f6 1068
615dc3cd
GM
1069char *
1070nextitem(current)
1071char *current;
66b878f6 1072{
615dc3cd
GM
1073 if ((*current&0xff) != IAC) {
1074 return current+1;
1075 }
1076 switch (*(current+1)&0xff) {
1077 case DO:
1078 case DONT:
1079 case WILL:
1080 case WONT:
1081 return current+3;
1082 case SB: /* loop forever looking for the SE */
1083 {
1084 register char *look = current+2;
66b878f6 1085
615dc3cd
GM
1086 for (;;) {
1087 if ((*look++&0xff) == IAC) {
1088 if ((*look++&0xff) == SE) {
1089 return look;
1090 }
1091 }
1092 }
9f005877 1093 }
615dc3cd
GM
1094 default:
1095 return current+2;
1096 }
66b878f6 1097}
5d78ef73
GM
1098
1099
615dc3cd
GM
1100/*
1101 * netclear()
1102 *
1103 * We are about to do a TELNET SYNCH operation. Clear
1104 * the path to the network.
1105 *
1106 * Things are a bit tricky since we may have sent the first
1107 * byte or so of a previous TELNET command into the network.
1108 * So, we have to scan the network buffer from the beginning
1109 * until we are up to where we want to be.
1110 *
1111 * A side effect of what we do, just to keep things
1112 * simple, is to clear the urgent data pointer. The principal
1113 * caller should be setting the urgent data pointer AFTER calling
1114 * us in any case.
1115 */
1116
1117netclear()
1118{
1119 register char *thisitem, *next;
1120 char *good;
1121#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1122 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1123
1124 thisitem = netobuf;
1125
1126 while ((next = nextitem(thisitem)) <= nbackp) {
1127 thisitem = next;
1128 }
1129
1130 /* Now, thisitem is first before/at boundary. */
1131
1132 good = netobuf; /* where the good bytes go */
1133
1134 while (nfrontp > thisitem) {
1135 if (wewant(thisitem)) {
1136 int length;
1137
1138 next = thisitem;
1139 do {
1140 next = nextitem(next);
1141 } while (wewant(next) && (nfrontp > next));
1142 length = next-thisitem;
1143 bcopy(thisitem, good, length);
1144 good += length;
1145 thisitem = next;
1146 } else {
1147 thisitem = nextitem(thisitem);
1148 }
1149 }
1150
1151 nbackp = netobuf;
1152 nfrontp = good; /* next byte to be sent */
1153 neturg = 0;
1154}
1155\f
5d78ef73
GM
1156/*
1157 * netflush
1158 * Send as much data as possible to the network,
1159 * handling requests for urgent data.
1160 */
1161
1162
1163netflush()
1164{
1165 int n;
1166
1167 if ((n = nfrontp - nbackp) > 0) {
affdaa4e
GM
1168 /*
1169 * if no urgent data, or if the other side appears to be an
1170 * old 4.2 client (and thus unable to survive TCP urgent data),
1171 * write the entire buffer in non-OOB mode.
1172 */
1173 if ((neturg == 0) || (not42 == 0)) {
5d78ef73
GM
1174 n = write(net, nbackp, n); /* normal write */
1175 } else {
1176 n = neturg - nbackp;
1177 /*
1178 * In 4.2 (and 4.3) systems, there is some question about
1179 * what byte in a sendOOB operation is the "OOB" data.
1180 * To make ourselves compatible, we only send ONE byte
1181 * out of band, the one WE THINK should be OOB (though
1182 * we really have more the TCP philosophy of urgent data
1183 * rather than the Unix philosophy of OOB data).
1184 */
1185 if (n > 1) {
1186 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1187 } else {
1188 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1189 }
1190 }
1191 }
1192 if (n < 0) {
1193 if (errno == EWOULDBLOCK)
1194 return;
1195 /* should blow this guy away... */
1196 return;
1197 }
1198 nbackp += n;
1199 if (nbackp >= neturg) {
1200 neturg = 0;
1201 }
1202 if (nbackp == nfrontp) {
1203 nbackp = nfrontp = netobuf;
1204 }
1205}
66b878f6
BJ
1206
1207cleanup()
1208{
66b878f6
BJ
1209
1210 rmut();
a7f6263e 1211 vhangup(); /* XXX */
ff24c640 1212 shutdown(net, 2);
66b878f6
BJ
1213 exit(1);
1214}
1215
1216#include <utmp.h>
1217
1218struct utmp wtmp;
1219char wtmpf[] = "/usr/adm/wtmp";
122f5efc
JB
1220char utmpf[] = "/etc/utmp";
1221#define SCPYN(a, b) strncpy(a, b, sizeof(a))
1222#define SCMPN(a, b) strncmp(a, b, sizeof(a))
66b878f6
BJ
1223
1224rmut()
1225{
1226 register f;
1227 int found = 0;
122f5efc
JB
1228 struct utmp *u, *utmp;
1229 int nutmp;
1230 struct stat statbf;
66b878f6 1231
122f5efc 1232 f = open(utmpf, O_RDWR);
66b878f6 1233 if (f >= 0) {
122f5efc
JB
1234 fstat(f, &statbf);
1235 utmp = (struct utmp *)malloc(statbf.st_size);
1236 if (!utmp)
1237 syslog(LOG_ERR, "utmp malloc failed");
1238 if (statbf.st_size && utmp) {
1239 nutmp = read(f, utmp, statbf.st_size);
1240 nutmp /= sizeof(struct utmp);
1241
1242 for (u = utmp ; u < &utmp[nutmp] ; u++) {
1243 if (SCMPN(u->ut_line, line+5) ||
1244 u->ut_name[0]==0)
1245 continue;
1246 lseek(f, ((long)u)-((long)utmp), L_SET);
1247 SCPYN(u->ut_name, "");
1248 SCPYN(u->ut_host, "");
1249 time(&u->ut_time);
1250 write(f, (char *)u, sizeof(wtmp));
1251 found++;
1252 }
66b878f6
BJ
1253 }
1254 close(f);
1255 }
1256 if (found) {
1a33b848 1257 f = open(wtmpf, O_WRONLY|O_APPEND);
66b878f6
BJ
1258 if (f >= 0) {
1259 SCPYN(wtmp.ut_line, line+5);
1260 SCPYN(wtmp.ut_name, "");
37c640e2 1261 SCPYN(wtmp.ut_host, "");
66b878f6 1262 time(&wtmp.ut_time);
122f5efc 1263 write(f, (char *)&wtmp, sizeof(wtmp));
66b878f6
BJ
1264 close(f);
1265 }
1266 }
1267 chmod(line, 0666);
1268 chown(line, 0, 0);
1269 line[strlen("/dev/")] = 'p';
1270 chmod(line, 0666);
1271 chown(line, 0, 0);
1272}