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