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