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