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