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