typo
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
ac6e6727 1#ifndef lint
d05f347d 2static char sccsid[] = "@(#)telnetd.c 4.18 83/02/21";
ac6e6727
BJ
3#endif
4
66b878f6
BJ
5/*
6 * Stripped-down telnet server.
7 */
de3b21e8
SL
8#include <sys/types.h>
9#include <sys/socket.h>
10
11#include <netinet/in.h>
12
66b878f6
BJ
13#include <stdio.h>
14#include <signal.h>
15#include <errno.h>
16#include <sgtty.h>
17#include <wait.h>
9f005877 18#include <netdb.h>
de3b21e8 19
66b878f6
BJ
20#include "telnet.h"
21
66b878f6 22#define BELL '\07'
66b878f6
BJ
23
24char hisopts[256];
25char myopts[256];
26
27char doopt[] = { IAC, DO, '%', 'c', 0 };
28char dont[] = { IAC, DONT, '%', 'c', 0 };
29char will[] = { IAC, WILL, '%', 'c', 0 };
30char wont[] = { IAC, WONT, '%', 'c', 0 };
31
32/*
33 * I/O data buffers, pointers, and counters.
34 */
35char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
36char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
37char netibuf[BUFSIZ], *netip = netibuf;
9ef3087d 38char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
66b878f6
BJ
39int pcc, ncc;
40
41int pty, net;
42int inter;
f553aca8 43int reapchild();
66b878f6
BJ
44extern int errno;
45char line[] = "/dev/ptyp0";
46
9f005877 47struct sockaddr_in sin = { AF_INET };
66b878f6
BJ
48
49main(argc, argv)
50 char *argv[];
51{
f553aca8 52 int s, pid, options;
9f005877 53 struct servent *sp;
66b878f6 54
9f005877
SL
55 sp = getservbyname("telnet", "tcp");
56 if (sp == 0) {
57 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
58 exit(1);
59 }
60 sin.sin_port = sp->s_port;
66b878f6 61 argc--, argv++;
f553aca8
SL
62 if (argc > 0 && !strcmp(*argv, "-d")) {
63 options |= SO_DEBUG;
64 argc--, argv++;
65 }
9f005877
SL
66 if (argc > 0) {
67 sin.sin_port = atoi(*argv);
68 if (sin.sin_port <= 0) {
69 fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
70 exit(1);
71 }
5eba30a3 72 sin.sin_port = htons((u_short)sin.sin_port);
9f005877 73 }
16f092bf
SL
74#ifndef DEBUG
75 if (fork())
76 exit(0);
77 for (s = 0; s < 10; s++)
78 (void) close(s);
79 (void) open("/", 0);
80 (void) dup2(0, 1);
81 (void) dup2(0, 2);
82 { int tt = open("/dev/tty", 2);
83 if (tt > 0) {
84 ioctl(tt, TIOCNOTTY, 0);
85 close(tt);
86 }
87 }
88#endif
de3b21e8 89again:
9de2cbb6 90 s = socket(AF_INET, SOCK_STREAM, 0, 0);
de3b21e8
SL
91 if (s < 0) {
92 perror("telnetd: socket");;
93 sleep(5);
94 goto again;
95 }
f553aca8
SL
96 if (options & SO_DEBUG)
97 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
98 perror("telnetd: setsockopt (SO_DEBUG)");
99#ifdef notdef
100 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
101 perror("telnetd: setsockopt (SO_KEEPALIVE)");
102#endif
de3b21e8
SL
103 while (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) {
104 perror("telnetd: bind");
105 sleep(5);
106 }
349e2d84 107 sigset(SIGCHLD, reapchild);
de3b21e8 108 listen(s, 10);
66b878f6 109 for (;;) {
de3b21e8
SL
110 int s2;
111
8f2758db 112 s2 = accept(s, (caddr_t)0, 0, 0);
de3b21e8 113 if (s2 < 0) {
f553aca8
SL
114 if (errno == EINTR)
115 continue;
8f2758db 116 perror("telnetd: accept");
66b878f6
BJ
117 sleep(1);
118 continue;
119 }
120 if ((pid = fork()) < 0)
121 printf("Out of processes\n");
d05f347d
SL
122 else if (pid == 0) {
123 signal(SIGCHLD, SIG_IGN);
de3b21e8 124 doit(s2);
d05f347d 125 }
de3b21e8 126 close(s2);
66b878f6
BJ
127 }
128 /*NOTREACHED*/
129}
130
f553aca8
SL
131reapchild()
132{
133 union wait status;
134
135 while (wait3(&status, WNOHANG, 0) > 0)
136 ;
137}
138
66b878f6
BJ
139int cleanup();
140
141/*
142 * Get a pty, scan input lines.
143 */
144doit(f)
145{
146 char *cp = line;
147 int i, p, cc, t;
148 struct sgttyb b;
149
150 for (i = 0; i < 16; i++) {
151 cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
152 p = open(cp, 2);
153 if (p > 0)
154 goto gotpty;
155 }
8f2758db
SL
156 fatal(f, "All network ports in use");
157 /*NOTREACHED*/
66b878f6
BJ
158gotpty:
159 dup2(f, 0);
160 cp[strlen("/dev/")] = 't';
161 t = open("/dev/tty", 2);
162 if (t >= 0) {
163 ioctl(t, TIOCNOTTY, 0);
164 close(t);
165 }
166 t = open(cp, 2);
8f2758db
SL
167 if (t < 0)
168 fatalperror(f, cp, errno);
66b878f6 169 ioctl(t, TIOCGETP, &b);
9ef3087d 170 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 171 ioctl(t, TIOCSETP, &b);
9ef3087d 172 ioctl(p, TIOCGETP, &b);
da96b661 173 b.sg_flags &= ~ECHO;
9ef3087d 174 ioctl(p, TIOCSETP, &b);
8f2758db
SL
175 if ((i = fork()) < 0)
176 fatalperror(f, "fork", errno);
66b878f6
BJ
177 if (i)
178 telnet(f, p);
179 close(f);
180 close(p);
181 dup2(t, 0);
182 dup2(t, 1);
183 dup2(t, 2);
184 close(t);
185 execl("/bin/login", "telnet-login", 0);
8f2758db
SL
186 fatalperror(f, "/bin/login", errno);
187 /*NOTREACHED*/
188}
189
190fatal(f, msg)
191 int f;
192 char *msg;
193{
194 char buf[BUFSIZ];
195
196 (void) sprintf(buf, "telnetd: %s.\n", msg);
197 (void) write(f, buf, strlen(buf));
66b878f6
BJ
198 exit(1);
199}
200
8f2758db
SL
201fatalperror(f, msg, errno)
202 int f;
203 char *msg;
204 int errno;
205{
206 char buf[BUFSIZ];
207 extern char *sys_errlist[];
208
209 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
210 fatal(f, buf);
211}
212
66b878f6
BJ
213/*
214 * Main loop. Select from pty and network, and
215 * hand data to telnet receiver finite state machine.
216 */
217telnet(f, p)
218{
219 int on = 1;
220
221 net = f, pty = p;
222 ioctl(f, FIONBIO, &on);
223 ioctl(p, FIONBIO, &on);
224 signal(SIGTSTP, SIG_IGN);
225 sigset(SIGCHLD, cleanup);
226
da96b661
SL
227 /*
228 * Request to do remote echo.
229 */
230 dooption(TELOPT_ECHO);
231 myopts[TELOPT_ECHO] = 1;
66b878f6
BJ
232 for (;;) {
233 int ibits = 0, obits = 0;
234 register int c;
235
236 /*
237 * Never look for input if there's still
238 * stuff in the corresponding output buffer
239 */
240 if (nfrontp - nbackp)
241 obits |= (1 << f);
242 else
243 ibits |= (1 << p);
244 if (pfrontp - pbackp)
245 obits |= (1 << p);
246 else
247 ibits |= (1 << f);
248 if (ncc < 0 && pcc < 0)
249 break;
de3b21e8 250 select(16, &ibits, &obits, 0, 0);
66b878f6
BJ
251 if (ibits == 0 && obits == 0) {
252 sleep(5);
253 continue;
254 }
255
256 /*
257 * Something to read from the network...
258 */
259 if (ibits & (1 << f)) {
260 ncc = read(f, netibuf, BUFSIZ);
261 if (ncc < 0 && errno == EWOULDBLOCK)
262 ncc = 0;
263 else {
264 if (ncc <= 0)
265 break;
266 netip = netibuf;
267 }
268 }
269
270 /*
271 * Something to read from the pty...
272 */
273 if (ibits & (1 << p)) {
274 pcc = read(p, ptyibuf, BUFSIZ);
275 if (pcc < 0 && errno == EWOULDBLOCK)
276 pcc = 0;
277 else {
278 if (pcc <= 0)
279 break;
280 ptyip = ptyibuf;
281 }
282 }
283
284 while (pcc > 0) {
285 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
286 break;
287 c = *ptyip++ & 0377, pcc--;
288 if (c == IAC)
289 *nfrontp++ = c;
290 *nfrontp++ = c;
291 }
292 if ((obits & (1 << f)) && (nfrontp - nbackp) > 0)
293 netflush();
294 if (ncc > 0)
295 telrcv();
296 if ((obits & (1 << p)) && (pfrontp - pbackp) > 0)
297 ptyflush();
298 }
299 cleanup();
300}
301
302/*
303 * State for recv fsm
304 */
305#define TS_DATA 0 /* base state */
306#define TS_IAC 1 /* look for double IAC's */
307#define TS_CR 2 /* CR-LF ->'s CR */
308#define TS_BEGINNEG 3 /* throw away begin's... */
309#define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
310#define TS_WILL 5 /* will option negotiation */
311#define TS_WONT 6 /* wont " */
312#define TS_DO 7 /* do " */
313#define TS_DONT 8 /* dont " */
314
315telrcv()
316{
317 register int c;
318 static int state = TS_DATA;
319 struct sgttyb b;
320
321 while (ncc > 0) {
322 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
323 return;
324 c = *netip++ & 0377, ncc--;
325 switch (state) {
326
327 case TS_DATA:
328 if (c == IAC) {
329 state = TS_IAC;
330 break;
331 }
332 if (inter > 0)
333 break;
334 *pfrontp++ = c;
335 if (!myopts[TELOPT_BINARY] && c == '\r')
336 state = TS_CR;
337 break;
338
339 case TS_CR:
340 if (c && c != '\n')
341 *pfrontp++ = c;
342 state = TS_DATA;
343 break;
344
345 case TS_IAC:
346 switch (c) {
347
348 /*
349 * Send the process on the pty side an
350 * interrupt. Do this with a NULL or
351 * interrupt char; depending on the tty mode.
352 */
353 case BREAK:
354 case IP:
355 interrupt();
356 break;
357
358 /*
359 * Are You There?
360 */
361 case AYT:
362 *pfrontp++ = BELL;
363 break;
364
365 /*
366 * Erase Character and
367 * Erase Line
368 */
369 case EC:
370 case EL:
371 ptyflush(); /* half-hearted */
372 ioctl(pty, TIOCGETP, &b);
373 *pfrontp++ = (c == EC) ?
374 b.sg_erase : b.sg_kill;
375 break;
376
377 /*
378 * Check for urgent data...
379 */
380 case DM:
381 break;
382
383 /*
384 * Begin option subnegotiation...
385 */
386 case SB:
387 state = TS_BEGINNEG;
388 continue;
389
390 case WILL:
391 case WONT:
392 case DO:
393 case DONT:
394 state = TS_WILL + (c - WILL);
395 continue;
396
397 case IAC:
398 *pfrontp++ = c;
399 break;
400 }
401 state = TS_DATA;
402 break;
403
404 case TS_BEGINNEG:
405 if (c == IAC)
406 state = TS_ENDNEG;
407 break;
408
409 case TS_ENDNEG:
410 state = c == SE ? TS_DATA : TS_BEGINNEG;
411 break;
412
413 case TS_WILL:
414 if (!hisopts[c])
415 willoption(c);
416 state = TS_DATA;
417 continue;
418
419 case TS_WONT:
420 if (hisopts[c])
421 wontoption(c);
422 state = TS_DATA;
423 continue;
424
425 case TS_DO:
426 if (!myopts[c])
427 dooption(c);
428 state = TS_DATA;
429 continue;
430
431 case TS_DONT:
432 if (myopts[c]) {
433 myopts[c] = 0;
434 sprintf(nfrontp, wont, c);
da96b661 435 nfrontp += sizeof (wont) - 2;
66b878f6
BJ
436 }
437 state = TS_DATA;
438 continue;
439
440 default:
de3b21e8 441 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
442 exit(1);
443 }
444 }
445}
446
447willoption(option)
448 int option;
449{
450 char *fmt;
451
452 switch (option) {
453
454 case TELOPT_BINARY:
455 mode(RAW, 0);
456 goto common;
457
458 case TELOPT_ECHO:
459 mode(0, ECHO|CRMOD);
460 /*FALL THRU*/
461
462 case TELOPT_SGA:
463 common:
464 hisopts[option] = 1;
465 fmt = doopt;
466 break;
467
468 case TELOPT_TM:
469 fmt = dont;
470 break;
471
472 default:
473 fmt = dont;
474 break;
475 }
13646f15 476 sprintf(nfrontp, fmt, option);
da96b661 477 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
478}
479
480wontoption(option)
481 int option;
482{
483 char *fmt;
484
485 switch (option) {
486
487 case TELOPT_ECHO:
488 mode(ECHO|CRMOD, 0);
489 goto common;
490
491 case TELOPT_BINARY:
492 mode(0, RAW);
493 /*FALL THRU*/
494
495 case TELOPT_SGA:
496 common:
497 hisopts[option] = 0;
498 fmt = dont;
499 break;
500
501 default:
502 fmt = dont;
503 }
504 sprintf(nfrontp, fmt, option);
da96b661 505 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
506}
507
508dooption(option)
509 int option;
510{
511 char *fmt;
512
513 switch (option) {
514
515 case TELOPT_TM:
516 fmt = wont;
517 break;
518
519 case TELOPT_ECHO:
520 mode(ECHO|CRMOD, 0);
521 goto common;
522
523 case TELOPT_BINARY:
524 mode(RAW, 0);
525 /*FALL THRU*/
526
527 case TELOPT_SGA:
528 common:
529 fmt = will;
530 break;
531
532 default:
533 fmt = wont;
534 break;
535 }
536 sprintf(nfrontp, fmt, option);
da96b661 537 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
538}
539
540mode(on, off)
541 int on, off;
542{
543 struct sgttyb b;
544
545 ptyflush();
546 ioctl(pty, TIOCGETP, &b);
547 b.sg_flags |= on;
548 b.sg_flags &= ~off;
549 ioctl(pty, TIOCSETP, &b);
550}
551
552/*
553 * Send interrupt to process on other side of pty.
554 * If it is in raw mode, just write NULL;
555 * otherwise, write intr char.
556 */
557interrupt()
558{
559 struct sgttyb b;
560 struct tchars tchars;
561
562 ptyflush(); /* half-hearted */
563 ioctl(pty, TIOCGETP, &b);
564 if (b.sg_flags & RAW) {
565 *pfrontp++ = '\0';
566 return;
567 }
568 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
569 '\177' : tchars.t_intrc;
570}
571
572ptyflush()
573{
574 int n;
575
576 if ((n = pfrontp - pbackp) > 0)
577 n = write(pty, pbackp, n);
9f005877
SL
578 if (n < 0)
579 return;
66b878f6
BJ
580 pbackp += n;
581 if (pbackp == pfrontp)
582 pbackp = pfrontp = ptyobuf;
583}
584
585netflush()
586{
587 int n;
588
589 if ((n = nfrontp - nbackp) > 0)
590 n = write(net, nbackp, n);
9f005877
SL
591 if (n < 0) {
592 if (errno == EWOULDBLOCK)
593 return;
594 /* should blow this guy away... */
595 return;
596 }
66b878f6
BJ
597 nbackp += n;
598 if (nbackp == nfrontp)
599 nbackp = nfrontp = netobuf;
600}
601
602cleanup()
603{
66b878f6
BJ
604
605 rmut();
a7f6263e 606 vhangup(); /* XXX */
ff24c640 607 shutdown(net, 2);
66b878f6
BJ
608 kill(0, SIGKILL);
609 exit(1);
610}
611
612#include <utmp.h>
613
614struct utmp wtmp;
615char wtmpf[] = "/usr/adm/wtmp";
616char utmp[] = "/etc/utmp";
da96b661
SL
617#define SCPYN(a, b) strncpy(a, b, sizeof (a))
618#define SCMPN(a, b) strncmp(a, b, sizeof (a))
66b878f6
BJ
619
620rmut()
621{
622 register f;
623 int found = 0;
624
625 f = open(utmp, 2);
626 if (f >= 0) {
da96b661 627 while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
66b878f6
BJ
628 if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
629 continue;
da96b661 630 lseek(f, -(long)sizeof (wtmp), 1);
66b878f6
BJ
631 SCPYN(wtmp.ut_name, "");
632 time(&wtmp.ut_time);
da96b661 633 write(f, (char *)&wtmp, sizeof (wtmp));
66b878f6
BJ
634 found++;
635 }
636 close(f);
637 }
638 if (found) {
639 f = open(wtmpf, 1);
640 if (f >= 0) {
641 SCPYN(wtmp.ut_line, line+5);
642 SCPYN(wtmp.ut_name, "");
643 time(&wtmp.ut_time);
644 lseek(f, (long)0, 2);
da96b661 645 write(f, (char *)&wtmp, sizeof (wtmp));
66b878f6
BJ
646 close(f);
647 }
648 }
649 chmod(line, 0666);
650 chown(line, 0, 0);
651 line[strlen("/dev/")] = 'p';
652 chmod(line, 0666);
653 chown(line, 0, 0);
654}