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