xref: /original-bsd/libexec/telnetd/telnetd.c (revision f0fd5f8a)
1 #ifndef lint
2 static char sccsid[] = "@(#)telnetd.c	4.12 82/11/17";
3 #endif
4 
5 /*
6  * Stripped-down telnet server.
7  */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 
11 #include <netinet/in.h>
12 
13 #include <stdio.h>
14 #include <signal.h>
15 #include <errno.h>
16 #include <sgtty.h>
17 #include <wait.h>
18 #include <netdb.h>
19 
20 #include "telnet.h"
21 
22 #define	BELL		'\07'
23 
24 char	hisopts[256];
25 char	myopts[256];
26 
27 char	doopt[] = { IAC, DO, '%', 'c', 0 };
28 char	dont[] = { IAC, DONT, '%', 'c', 0 };
29 char	will[] = { IAC, WILL, '%', 'c', 0 };
30 char	wont[] = { IAC, WONT, '%', 'c', 0 };
31 
32 /*
33  * I/O data buffers, pointers, and counters.
34  */
35 char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
36 char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
37 char	netibuf[BUFSIZ], *netip = netibuf;
38 char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
39 int	pcc, ncc;
40 
41 int	pty, net;
42 int	inter;
43 extern	int errno;
44 char	line[] = "/dev/ptyp0";
45 
46 struct	sockaddr_in sin = { AF_INET };
47 
48 main(argc, argv)
49 	char *argv[];
50 {
51 	int s, pid;
52 	union wait status;
53 	struct servent *sp;
54 
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;
61 	argc--, argv++;
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 	}
69 	sin.sin_port = htons((u_short)sin.sin_port);
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
85 again:
86 	s = socket(AF_INET, 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);
97 	for (;;) {
98 		int s2;
99 
100 		s2 = accept(s, (caddr_t)0, 0, 0);
101 		if (s2 < 0) {
102 			perror("telnetd: accept");
103 			sleep(1);
104 			continue;
105 		}
106 		if ((pid = fork()) < 0)
107 			printf("Out of processes\n");
108 		else if (pid == 0)
109 			doit(s2);
110 		close(s2);
111 		while (wait3(status, WNOHANG, 0) > 0)
112 			continue;
113 	}
114 	/*NOTREACHED*/
115 }
116 
117 int	cleanup();
118 
119 /*
120  * Get a pty, scan input lines.
121  */
122 doit(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 	fatal(f, "All network ports in use");
135 	/*NOTREACHED*/
136 gotpty:
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);
145 	if (t < 0)
146 		fatalperror(f, cp, errno);
147 	ioctl(t, TIOCGETP, &b);
148 	b.sg_flags = CRMOD|XTABS|ANYP;
149 	ioctl(t, TIOCSETP, &b);
150 	ioctl(p, TIOCGETP, &b);
151 	b.sg_flags &= ~ECHO;
152 	ioctl(p, TIOCSETP, &b);
153 	if ((i = fork()) < 0)
154 		fatalperror(f, "fork", errno);
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 	fatalperror(f, "/bin/login", errno);
165 	/*NOTREACHED*/
166 }
167 
168 fatal(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));
176 	exit(1);
177 }
178 
179 fatalperror(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 
191 /*
192  * Main loop.  Select from pty and network, and
193  * hand data to telnet receiver finite state machine.
194  */
195 telnet(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 
205 	/*
206 	 * Request to do remote echo.
207 	 */
208 	dooption(TELOPT_ECHO);
209 	myopts[TELOPT_ECHO] = 1;
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;
228 		select(16, &ibits, &obits, 0, 0);
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 
293 telrcv()
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);
413 				nfrontp += sizeof (wont) - 2;
414 			}
415 			state = TS_DATA;
416 			continue;
417 
418 		default:
419 			printf("telnetd: panic state=%d\n", state);
420 			exit(1);
421 		}
422 	}
423 }
424 
425 willoption(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 	}
454 	sprintf(nfrontp, fmt, option);
455 	nfrontp += sizeof (dont) - 2;
456 }
457 
458 wontoption(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);
483 	nfrontp += sizeof (doopt) - 2;
484 }
485 
486 dooption(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);
515 	nfrontp += sizeof (doopt) - 2;
516 }
517 
518 mode(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  */
535 interrupt()
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 
550 ptyflush()
551 {
552 	int n;
553 
554 	if ((n = pfrontp - pbackp) > 0)
555 		n = write(pty, pbackp, n);
556 	if (n < 0)
557 		return;
558 	pbackp += n;
559 	if (pbackp == pfrontp)
560 		pbackp = pfrontp = ptyobuf;
561 }
562 
563 netflush()
564 {
565 	int n;
566 
567 	if ((n = nfrontp - nbackp) > 0)
568 		n = write(net, nbackp, n);
569 	if (n < 0) {
570 		if (errno == EWOULDBLOCK)
571 			return;
572 		/* should blow this guy away... */
573 		return;
574 	}
575 	nbackp += n;
576 	if (nbackp == nfrontp)
577 		nbackp = nfrontp = netobuf;
578 }
579 
580 cleanup()
581 {
582 	int how = 2;
583 
584 	rmut();
585 #ifdef notdef
586 	vhangup();
587 #endif
588 	ioctl(net, SIOCDONE, &how);
589 	kill(0, SIGKILL);
590 	exit(1);
591 }
592 
593 #include <utmp.h>
594 
595 struct	utmp wtmp;
596 char	wtmpf[]	= "/usr/adm/wtmp";
597 char	utmp[] = "/etc/utmp";
598 #define SCPYN(a, b)	strncpy(a, b, sizeof (a))
599 #define SCMPN(a, b)	strncmp(a, b, sizeof (a))
600 
601 rmut()
602 {
603 	register f;
604 	int found = 0;
605 
606 	f = open(utmp, 2);
607 	if (f >= 0) {
608 		while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
609 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
610 				continue;
611 			lseek(f, -(long)sizeof (wtmp), 1);
612 			SCPYN(wtmp.ut_name, "");
613 			time(&wtmp.ut_time);
614 			write(f, (char *)&wtmp, sizeof (wtmp));
615 			found++;
616 		}
617 		close(f);
618 	}
619 	if (found) {
620 		f = open(wtmpf, 1);
621 		if (f >= 0) {
622 			SCPYN(wtmp.ut_line, line+5);
623 			SCPYN(wtmp.ut_name, "");
624 			time(&wtmp.ut_time);
625 			lseek(f, (long)0, 2);
626 			write(f, (char *)&wtmp, sizeof (wtmp));
627 			close(f);
628 		}
629 	}
630 	chmod(line, 0666);
631 	chown(line, 0, 0);
632 	line[strlen("/dev/")] = 'p';
633 	chmod(line, 0666);
634 	chown(line, 0, 0);
635 }
636