xref: /original-bsd/libexec/telnetd/telnetd.c (revision 014fe330)
1 #ifndef lint
2 static char sccsid[] = "@(#)telnetd.c	4.22 83/06/12";
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 <arpa/telnet.h>
14 
15 #include <stdio.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <sgtty.h>
19 #include <wait.h>
20 #include <netdb.h>
21 #include <getty.h>
22 
23 #define	BELL		'\07'
24 
25 char	hisopts[256];
26 char	myopts[256];
27 
28 char	doopt[] = { IAC, DO, '%', 'c', 0 };
29 char	dont[] = { IAC, DONT, '%', 'c', 0 };
30 char	will[] = { IAC, WILL, '%', 'c', 0 };
31 char	wont[] = { IAC, WONT, '%', 'c', 0 };
32 
33 /*
34  * I/O data buffers, pointers, and counters.
35  */
36 char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
37 char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
38 char	netibuf[BUFSIZ], *netip = netibuf;
39 char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
40 int	pcc, ncc;
41 
42 int	pty, net;
43 int	inter;
44 int	reapchild();
45 extern	int errno;
46 char	line[] = "/dev/ptyp0";
47 
48 struct	sockaddr_in sin = { AF_INET };
49 
50 main(argc, argv)
51 	char *argv[];
52 {
53 	int s, pid, options;
54 	struct servent *sp;
55 
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;
62 	argc--, argv++;
63 	if (argc > 0 && !strcmp(*argv, "-d")) {
64 		options |= SO_DEBUG;
65 		argc--, argv++;
66 	}
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 		}
73 		sin.sin_port = htons((u_short)sin.sin_port);
74 	}
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
90 again:
91 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
92 	if (s < 0) {
93 		perror("telnetd: socket");;
94 		sleep(5);
95 		goto again;
96 	}
97 	if (options & SO_DEBUG)
98 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
99 			perror("telnetd: setsockopt (SO_DEBUG)");
100 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
101 		perror("telnetd: setsockopt (SO_KEEPALIVE)");
102 	while (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) {
103 		perror("telnetd: bind");
104 		sleep(5);
105 	}
106 	signal(SIGCHLD, reapchild);
107 	listen(s, 10);
108 	for (;;) {
109 		struct sockaddr_in from;
110 		int s2, fromlen = sizeof (from);
111 
112 		s2 = accept(s, (caddr_t)&from, &fromlen);
113 		if (s2 < 0) {
114 			if (errno == EINTR)
115 				continue;
116 			perror("telnetd: accept");
117 			sleep(1);
118 			continue;
119 		}
120 		if ((pid = fork()) < 0)
121 			printf("Out of processes\n");
122 		else if (pid == 0) {
123 			signal(SIGCHLD, SIG_IGN);
124 			doit(s2, &from);
125 		}
126 		close(s2);
127 	}
128 	/*NOTREACHED*/
129 }
130 
131 reapchild()
132 {
133 	union wait status;
134 
135 	while (wait3(&status, WNOHANG, 0) > 0)
136 		;
137 }
138 
139 int	cleanup();
140 
141 /*
142  * Get a pty, scan input lines.
143  */
144 doit(f, who)
145 	int f;
146 	struct sockaddr_in *who;
147 {
148 	char *cp = line, *host, *ntoa();
149 	int i, p, cc, t;
150 	struct sgttyb b;
151 	struct hostent *hp;
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 	}
159 	fatal(f, "All network ports in use");
160 	/*NOTREACHED*/
161 gotpty:
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);
170 	if (t < 0)
171 		fatalperror(f, cp, errno);
172 	ioctl(t, TIOCGETP, &b);
173 	b.sg_flags = CRMOD|XTABS|ANYP;
174 	ioctl(t, TIOCSETP, &b);
175 	ioctl(p, TIOCGETP, &b);
176 	b.sg_flags &= ~ECHO;
177 	ioctl(p, TIOCSETP, &b);
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);
184 	if ((i = fork()) < 0)
185 		fatalperror(f, "fork", errno);
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);
194 	execl("/bin/login", "login", "-h", host, 0);
195 	fatalperror(f, "/bin/login", errno);
196 	/*NOTREACHED*/
197 }
198 
199 fatal(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));
207 	exit(1);
208 }
209 
210 fatalperror(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 
222 /*
223  * Main loop.  Select from pty and network, and
224  * hand data to telnet receiver finite state machine.
225  */
226 telnet(f, p)
227 {
228 	int on = 1;
229 	char hostname[32];
230 
231 	net = f, pty = p;
232 	ioctl(f, FIONBIO, &on);
233 	ioctl(p, FIONBIO, &on);
234 	signal(SIGTSTP, SIG_IGN);
235 	signal(SIGCHLD, cleanup);
236 
237 	/*
238 	 * Request to do remote echo.
239 	 */
240 	dooption(TELOPT_ECHO);
241 	myopts[TELOPT_ECHO] = 1;
242 	/*
243 	 * Show banner that getty never gave.
244 	 */
245 	gethostname(hostname, sizeof (hostname));
246 	sprintf(nfrontp, BANNER, hostname, "");
247 	nfrontp += strlen(nfrontp);
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;
266 		select(16, &ibits, &obits, 0, 0);
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 
331 telrcv()
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);
451 				nfrontp += sizeof (wont) - 2;
452 			}
453 			state = TS_DATA;
454 			continue;
455 
456 		default:
457 			printf("telnetd: panic state=%d\n", state);
458 			exit(1);
459 		}
460 	}
461 }
462 
463 willoption(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 	}
492 	sprintf(nfrontp, fmt, option);
493 	nfrontp += sizeof (dont) - 2;
494 }
495 
496 wontoption(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);
521 	nfrontp += sizeof (doopt) - 2;
522 }
523 
524 dooption(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);
553 	nfrontp += sizeof (doopt) - 2;
554 }
555 
556 mode(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  */
573 interrupt()
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 
588 ptyflush()
589 {
590 	int n;
591 
592 	if ((n = pfrontp - pbackp) > 0)
593 		n = write(pty, pbackp, n);
594 	if (n < 0)
595 		return;
596 	pbackp += n;
597 	if (pbackp == pfrontp)
598 		pbackp = pfrontp = ptyobuf;
599 }
600 
601 netflush()
602 {
603 	int n;
604 
605 	if ((n = nfrontp - nbackp) > 0)
606 		n = write(net, nbackp, n);
607 	if (n < 0) {
608 		if (errno == EWOULDBLOCK)
609 			return;
610 		/* should blow this guy away... */
611 		return;
612 	}
613 	nbackp += n;
614 	if (nbackp == nfrontp)
615 		nbackp = nfrontp = netobuf;
616 }
617 
618 cleanup()
619 {
620 
621 	rmut();
622 	vhangup();	/* XXX */
623 	shutdown(net, 2);
624 	kill(0, SIGKILL);
625 	exit(1);
626 }
627 
628 #include <utmp.h>
629 
630 struct	utmp wtmp;
631 char	wtmpf[]	= "/usr/adm/wtmp";
632 char	utmp[] = "/etc/utmp";
633 #define SCPYN(a, b)	strncpy(a, b, sizeof (a))
634 #define SCMPN(a, b)	strncmp(a, b, sizeof (a))
635 
636 rmut()
637 {
638 	register f;
639 	int found = 0;
640 
641 	f = open(utmp, 2);
642 	if (f >= 0) {
643 		while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
644 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
645 				continue;
646 			lseek(f, -(long)sizeof (wtmp), 1);
647 			SCPYN(wtmp.ut_name, "");
648 			SCPYN(wtmp.ut_host, "");
649 			time(&wtmp.ut_time);
650 			write(f, (char *)&wtmp, sizeof (wtmp));
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, "");
660 			SCPYN(wtmp.ut_host, "");
661 			time(&wtmp.ut_time);
662 			lseek(f, (long)0, 2);
663 			write(f, (char *)&wtmp, sizeof (wtmp));
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 }
673 
674 /*
675  * Convert network-format internet address
676  * to base 256 d.d.d.d representation.
677  */
678 char *
679 ntoa(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 }
690