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