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