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