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