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