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