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