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