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