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