don't init master pty to have ECHO on and don't send will ECHO option
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
ac6e6727 1#ifndef lint
9ef3087d 2static char sccsid[] = "@(#)telnetd.c 4.6 82/03/31";
ac6e6727
BJ
3#endif
4
66b878f6
BJ
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'
66b878f6
BJ
20
21char hisopts[256];
22char myopts[256];
23
24char doopt[] = { IAC, DO, '%', 'c', 0 };
25char dont[] = { IAC, DONT, '%', 'c', 0 };
26char will[] = { IAC, WILL, '%', 'c', 0 };
27char wont[] = { IAC, WONT, '%', 'c', 0 };
28
29/*
30 * I/O data buffers, pointers, and counters.
31 */
32char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
33char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
34char netibuf[BUFSIZ], *netip = netibuf;
9ef3087d 35char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
66b878f6
BJ
36int pcc, ncc;
37
38int pty, net;
39int inter;
40extern int errno;
41char line[] = "/dev/ptyp0";
42
ac6e6727 43struct sockaddr_in sin = { AF_INET, IPPORT_TELNET };
75a2ff26 44int options = SO_ACCEPTCONN|SO_KEEPALIVE;
66b878f6
BJ
45
46main(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;
ac6e6727
BJ
55#if vax || pdp11
56 sin.sin_port = htons(sin.sin_port);
57#endif
66b878f6
BJ
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
82int cleanup();
83
84/*
85 * Get a pty, scan input lines.
86 */
87doit(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);
102gotpty:
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);
9ef3087d 117 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 118 ioctl(t, TIOCSETP, &b);
9ef3087d
SL
119 ioctl(p, TIOCGETP, &b);
120 b.sg_flags &= ~ECHO; /* not until remote says to */
121 ioctl(p, TIOCSETP, &b);
66b878f6
BJ
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 */
144telnet(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
237telrcv()
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
369willoption(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 }
13646f15 398 sprintf(nfrontp, fmt, option);
66b878f6
BJ
399 nfrontp += sizeof(dont) - 2;
400}
401
402wontoption(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
430dooption(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
462mode(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 */
479interrupt()
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
494ptyflush()
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
507netflush()
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
520cleanup()
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
533struct utmp wtmp;
534char wtmpf[] = "/usr/adm/wtmp";
535char utmp[] = "/etc/utmp";
536#define SCPYN(a, b) strncpy(a, b, sizeof(a))
537#define SCMPN(a, b) strncmp(a, b, sizeof(a))
538
539rmut()
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}