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