|
|
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 <stdio.h>
9: #include <sys/param.h>
10: #include <sys/types.h>
11: #include <sys/stat.h>
12:
13: #include <errno.h>
14: #include <pwd.h>
15: #include <signal.h>
16: #include <sgtty.h>
17: #include <stdio.h>
18: #include <wait.h>
19: #include <sys/ttyld.h>
20: #include <sys/stream.h>
21: #include "telnet.h"
22:
23: #define BELL '\07'
24: #define DEBUG if (debug)
25:
26: /* option settings (remote and local) */
27: static char hisopts[256];
28: static char myopts[256];
29:
30: /* formats for option messages */
31: static char doopt[] = { IAC, DO, '%', 'c', 0 };
32: static char dont[] = { IAC, DONT, '%', 'c', 0 };
33: static char will[] = { IAC, WILL, '%', 'c', 0 };
34: static char wont[] = { IAC, WONT, '%', 'c', 0 };
35:
36: static int ptno; /* number of the pt */
37: static int ptfd, netfd; /* fd's to pt and tcp */
38: static int done; /* true if session is to be ended */
39: static int debug; /* true if debugging is to be output */
40: static struct sgttyb ptb; /* result of an IOCGETP on the pt */
41: static struct tchars ptt; /* result of an IOCGETC on the pt */
42:
43: /* predefined */
44: static reapchild();
45: static catchint();
46:
47: /* imported */
48: extern char **environ;
49: extern int errno;
50: extern char *strrchr(), *strchr();
51:
52:
53: /*
54: * The following macros and routines are used to
55: * manage the I/O buffers. They're a cross between
56: * stream buffers and standard I/O.
57: */
58: struct buffer {
59: char *b_rp; /* read pointer */
60: char *b_wp; /* write pointer */
61: char b_buf[BUFSIZ]; /* the buffer */
62: };
63: struct buffer ptibuf, *ptin = &ptibuf;
64: struct buffer ptobuf, *ptout = &ptobuf;
65: struct buffer netibuf, *netin = &netibuf;
66: struct buffer netobuf, *netout = &netobuf;
67:
68: #define binit(bp) (bp->b_rp = bp->b_wp = bp->b_buf)
69: #define bytes_filled(bp) (bp->b_wp - bp->b_rp)
70: #define space_left(bp) (bp->b_buf+sizeof(bp->b_buf) - bp->b_wp)
71: #define bput(bp, c) (*(bp->b_wp++) = c)
72: #define bget(bp) (*(bp->b_rp++) & 0377)
73:
74: /* read whatever the buffer can take */
75: static int
76: bread(bp, fd)
77: struct buffer *bp;
78: int fd;
79: {
80: int cc;
81:
82: /* normalize the buffer */
83: if (bytes_filled(bp) == 0)
84: bp->b_rp = bp->b_wp = bp->b_buf;
85:
86: /* fill it */
87: cc = read(fd, bp->b_wp, space_left(bp));
88: if (cc > 0)
89: bp->b_wp += cc;
90: return cc;
91: }
92:
93: /* read at most n bytes */
94: static int
95: breadn(bp, fd, n)
96: struct buffer *bp;
97: int fd, n;
98: {
99: int cc;
100:
101: /* normalize the buffer */
102: if (bytes_filled(bp) == 0)
103: bp->b_rp = bp->b_wp = bp->b_buf;
104: if (n > space_left(bp))
105: n = space_left(bp);
106:
107: /* fill it */
108: cc = read(fd, bp->b_wp, n);
109: if (cc > 0)
110: bp->b_wp += cc;
111: return cc;
112: }
113:
114: /* empty the buffer */
115: static int
116: bwrite(bp, fd)
117: struct buffer *bp;
118: int fd;
119: {
120: int cc;
121:
122:
123: cc = write(fd, bp->b_rp, bytes_filled(bp));
124:
125: /* normalize the buffer */
126: if (cc == bytes_filled(bp))
127: binit(bp);
128: return cc;
129: }
130: static int
131: bputs(bp, s)
132: struct buffer *bp;
133: char *s;
134: {
135: while (*s)
136: bput(bp, *s++);
137: }
138:
139: /*
140: * Establish a tcp socket and fork off a process for each connection.
141: */
142: main(argc, argv)
143: char *argv[];
144: {
145: int finish();
146: int f;
147:
148: doit(0);
149: rmut();
150: /*NOTREACHED*/
151: }
152:
153: static
154: reapchild()
155: {
156:
157: while (wait3((int *)0, WNOHANG, (struct vtimes *)0) > 0)
158: ;
159: }
160:
161: /*
162: * Get a pt. Put a login on one side and a telnet receiver on
163: * the other.
164: */
165: static
166: doit(f)
167: int f;
168: {
169: char *ptname();
170: int pfd[2];
171: # define TERMEND pfd[0]
172: # define PROCEND pfd[1]
173: extern int tty_ld, mesg_ld;
174:
175: /* get a pt pair */
176: if ((ptno = ptpipe(pfd)) < 0)
177: fatalperror(f, "out of pts", errno);
178:
179: /* make it really look like a terminal */
180: ioctl(TERMEND, FIOPUSHLD, (struct sgttyb *)&mesg_ld);
181: ioctl(PROCEND, FIOPUSHLD, (struct sgttyb *)&tty_ld);
182:
183: /* prepare for the death of a child */
184: signal(SIGCHLD, catchint);
185: switch (fork()) {
186: case -1:
187: fatalperror(f, "fork", errno);
188: case 0:
189: /* a process which is the remote login */
190: getty(PROCEND);
191: default:
192: /* the protocol process */
193: close(PROCEND);
194: netfd = f;
195: ptfd = TERMEND;
196: telnet();
197: }
198: /*NOTREACHED*/
199: }
200:
201: static
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: rmut();
211: exit(1);
212: }
213: static
214: finish()
215: {
216: if (debug) fprintf(stderr, "pipe closed\n");
217: rmut();
218: exit(1);
219: }
220:
221: static
222: fatalperror(f, msg, errno)
223: int f;
224: char *msg;
225: int errno;
226: {
227: char buf[BUFSIZ];
228: extern char *sys_errlist[];
229:
230: (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
231: fatal(f, buf);
232: }
233:
234: static
235: terminate(s)
236: char *s;
237: {
238: DEBUG fprintf(stderr, "session on %s terminated because of %s\n",
239: ptname(ptno), s);
240: done = 1;
241: }
242:
243: /* loop on input from the pt and the network */
244: static
245: telnet()
246: {
247: int n;
248: fd_set ibits, obits;
249:
250: binit(ptin);
251: binit(ptout);
252: binit(netin);
253: binit(netout);
254:
255: /* Request to do remote echo. */
256: dooption(TELOPT_ECHO);
257: myopts[TELOPT_ECHO] = 1;
258:
259: while(!done) {
260: FD_ZERO(ibits);
261: FD_ZERO(obits);
262:
263: /* process anything sitting in the input buffers */
264: if (bytes_filled(ptin) > 0)
265: ptprocess();
266: if (bytes_filled(netin) > 0)
267: netprocess();
268:
269: /* select for read only if there's room to read into */
270: if (space_left(netin) > 0)
271: FD_SET(netfd, ibits);
272: if (space_left(ptin) > 0)
273: FD_SET(ptfd, ibits);
274:
275: /* select for write only if there's something to write */
276: if (bytes_filled(ptout) > 0)
277: FD_SET(ptfd, obits);
278: if (bytes_filled(netout) > 0)
279: FD_SET(netfd, obits);
280:
281: n = select(NOFILE, &ibits, &obits, 100000);
282: if (n < 0)
283: break;
284: else if (n == 0)
285: continue;
286:
287: /* fill input buffers */
288: if (FD_ISSET(netfd, ibits))
289: netrcv();
290: if (FD_ISSET(ptfd, ibits))
291: ptrcv();
292:
293: /* flush output buffers */
294: if (FD_ISSET(netfd, obits) && bytes_filled(netout) > 0)
295: netflush();
296: if (FD_ISSET(ptfd, obits) && bytes_filled(ptout) > 0)
297: ptflush();
298: }
299: rmut();
300: exit(0);
301: }
302:
303:
304: /* read from the pt */
305: static
306: ptrcv()
307: {
308: static struct mesg m;
309: static int size = 0;
310:
311: /* get the header if we don't already have one */
312: if (size <= 0) {
313: if (read(ptfd, (char *)&m, MSGHLEN) != MSGHLEN) {
314: terminate("pt read");
315: return;
316: }
317: size = (m.losize & 0377) + ((m.hisize & 0377) << 8);
318: }
319: switch(m.type) {
320: case M_HANGUP:
321: terminate("pt hangup");
322: size=0;
323: break;
324: case M_DATA:
325: size=datamesg(size);
326: break;
327: case M_IOCTL:
328: ioctlmesg(size);
329: size=0;
330: break;
331: case M_IOCACK:
332: fprintf(stderr, "IOCACK\n");
333: flushmesg(size);
334: size=0;
335: break;
336: case M_IOCNAK:
337: fprintf(stderr, "IOCNAK\n");
338: flushmesg(size);
339: size=0;
340: break;
341: default:
342: othermesg(m, size);
343: size=0;
344: break;
345: }
346: }
347:
348: /* flush a message from the pt */
349: static
350: flushmesg(size)
351: {
352: char buf[64];
353: int cc;
354: /* flush it */
355: while (size > 0) {
356: fprintf(stderr, "flush\n");
357: cc = size > sizeof(buf) ? sizeof(buf) : size;
358: cc = read(ptfd, buf, cc);
359: if (cc < 0) {
360: terminate("pt read");
361: return;
362: }
363: size -= cc;
364: }
365: }
366:
367: /* handle an ioctl message from the pt */
368: static
369: ioctlmesg(size)
370: {
371: struct mesg rm;
372: union stmsg s;
373: int cc;
374:
375: if (size > 0) {
376: cc = read(ptfd, (char *)&s, size > sizeof(s) ? sizeof(s) : size);
377: if (cc < 0) {
378: terminate("pt read");
379: return;
380: }
381: if (size > cc)
382: flushmesg(size - cc);
383: }
384: rm.type = M_IOCACK;
385: switch (s.ioc0.com) {
386:
387: case TIOCSETN:
388: case TIOCSETP:
389: size = 0;
390: break;
391:
392: case TIOCGETP:
393: s.ioc1.sb.sg_ispeed = B9600;
394: s.ioc1.sb.sg_ospeed = B9600;
395: ptb = s.ioc1.sb; /* sic; remember what ttyld said */
396: /* leave size as it was */
397: break;
398:
399: default:
400: rm.type = M_IOCNAK;
401: size = 0;
402: break;
403: }
404: rm.magic = MSGMAGIC;
405: rm.losize = size;
406: rm.hisize = size>>8;
407: if (write(ptfd, (char *)&rm, MSGHLEN) != MSGHLEN) {
408: terminate("pt write");
409: return;
410: }
411: if (size > 0)
412: if (write(ptfd, (char *)&s, size) != size) {
413: terminate("pt write");
414: return;
415: }
416: }
417:
418: /* read bytes from the pt and write to the network connection */
419: static
420: datamesg(size)
421: {
422: int cc;
423:
424: cc = breadn(ptin, ptfd, size);
425: if (cc < 0)
426: terminate("pt read");
427: else
428: size -= cc;
429: return (size);
430: }
431:
432: /* handle an unrecognized type of message */
433: static
434: othermesg(m, size)
435: struct mesg m;
436: {
437: char buf[132];
438: int rcc, wcc;
439:
440: wcc = write(ptfd, (char *)&m, MSGHLEN);
441: if (wcc != MSGHLEN) {
442: terminate("pt write");
443: return;
444: }
445: while (size > 0) {
446: rcc = read(ptfd, buf, size > sizeof(buf) ? sizeof(buf) : size);
447: if (rcc <= 0) {
448: terminate("pt read");
449: return;
450: }
451: wcc = write(ptfd, buf, rcc);
452: if (wcc != rcc) {
453: terminate("pt write");
454: return;
455: }
456: size -= rcc;
457: }
458: }
459:
460: /* set pt mode */
461: static
462: mode(on, off)
463: int on, off;
464: {
465: ptflush();
466: ptb.sg_flags |= on;
467: ptb.sg_flags &= ~off;
468: if(ioctl(ptfd, TIOCSETP, &ptb)<0)
469: terminate("setting mode of pt");
470: }
471:
472: /* flush the pt's output buffer */
473: ptflush()
474: {
475: struct mesg rm;
476: int size;
477:
478: if ((size = bytes_filled(ptout)) > 0) {
479: rm.type = M_DATA;
480: rm.magic = MSGMAGIC;
481: rm.losize = size;
482: rm.hisize = size>>8;
483: if (write(ptfd, (char *)&rm, MSGHLEN) < MSGHLEN)
484: terminate("pt write");
485: else if (bwrite(ptout, ptfd) <= 0)
486: terminate("pt write");
487: }
488: }
489:
490: /* process bytes from the pt */
491: static
492: ptprocess()
493: {
494: register int c;
495:
496: while(bytes_filled(ptin) && space_left(netout) > 1) {
497: c = bget(ptin);
498: if (c == IAC)
499: bput(netout, c);
500: bput(netout, c);
501: }
502: }
503:
504: /* read bytes from the net */
505: static
506: netrcv()
507: {
508: if (bread(netin, netfd) < 0)
509: terminate("net read");
510: }
511:
512: /* flush the netwrk's output buffer */
513: netflush()
514: {
515: if (bwrite(netout, netfd) < 0)
516: terminate("net write");
517: }
518:
519: /*
520: * State for recv fsm
521: */
522: #define TS_DATA 0 /* base state */
523: #define TS_IAC 1 /* look for double IAC's */
524: #define TS_CR 2 /* CR-LF ->'s CR */
525: #define TS_BEGINNEG 3 /* throw away begin's... */
526: #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
527: #define TS_WILL 5 /* will option negotiation */
528: #define TS_WONT 6 /* wont " */
529: #define TS_DO 7 /* do " */
530: #define TS_DONT 8 /* dont " */
531:
532: static
533: netprocess()
534: {
535: static int state = TS_DATA;
536: register int c;
537: char buf[10];
538:
539: while (bytes_filled(netin)) {
540: c = bget(netin);
541: switch (state) {
542:
543: case TS_DATA:
544: if (c == IAC) {
545: state = TS_IAC;
546: break;
547: }
548: bput(ptout, c);
549: if (!myopts[TELOPT_BINARY] && c == '\r')
550: state = TS_CR;
551: break;
552:
553: case TS_CR:
554: if (c && c != '\n')
555: bput(ptout, c);
556: state = TS_DATA;
557: break;
558:
559: case TS_IAC:
560: switch (c) {
561:
562: /*
563: * Send the process on the pty side an
564: * interrupt. Do this with a NULL or
565: * interrupt char; depending on the tty mode.
566: */
567: case BREAK:
568: case IP:
569: interrupt();
570: break;
571:
572: /*
573: * Are You There?
574: */
575: case AYT:
576: bput(ptout, BELL);
577: break;
578:
579: /*
580: * Erase Character and
581: * Erase Line
582: */
583: case EC:
584: case EL:
585: ptflush();
586: bput(ptout, (c == EC) ? ptb.sg_erase : ptb.sg_kill);
587: break;
588:
589: /*
590: * Check for urgent data...
591: */
592: case DM:
593: break;
594:
595: /*
596: * Begin option subnegotiation...
597: */
598: case SB:
599: state = TS_BEGINNEG;
600: continue;
601:
602: case WILL:
603: case WONT:
604: case DO:
605: case DONT:
606: state = TS_WILL + (c - WILL);
607: continue;
608:
609: case IAC:
610: bput(ptout, c);
611: break;
612: }
613: state = TS_DATA;
614: break;
615:
616: case TS_BEGINNEG:
617: if (c == IAC)
618: state = TS_ENDNEG;
619: break;
620:
621: case TS_ENDNEG:
622: state = c == SE ? TS_DATA : TS_BEGINNEG;
623: break;
624:
625: case TS_WILL:
626: if (!hisopts[c])
627: willoption(c);
628: state = TS_DATA;
629: continue;
630:
631: case TS_WONT:
632: if (hisopts[c])
633: wontoption(c);
634: state = TS_DATA;
635: continue;
636:
637: case TS_DO:
638: if (!myopts[c])
639: dooption(c);
640: state = TS_DATA;
641: continue;
642:
643: case TS_DONT:
644: if (myopts[c]) {
645: myopts[c] = 0;
646: sprintf(buf, wont, c);
647: bputs(netout, buf);
648: }
649: state = TS_DATA;
650: continue;
651:
652: default:
653: printf("telnetd: panic state=%d\n", state);
654: exit(1);
655: }
656: }
657: }
658:
659: static
660: willoption(option)
661: int option;
662: {
663: char *fmt;
664: char buf[10];
665:
666: switch (option) {
667:
668: case TELOPT_BINARY:
669: mode(RAW, 0);
670: goto common;
671:
672: case TELOPT_ECHO:
673: mode(0, ECHO|CRMOD);
674: /*FALL THRU*/
675:
676: case TELOPT_SGA:
677: common:
678: hisopts[option] = 1;
679: fmt = doopt;
680: break;
681:
682: case TELOPT_TM:
683: fmt = dont;
684: break;
685:
686: default:
687: fmt = dont;
688: break;
689: }
690: sprintf(buf, fmt, option);
691: bputs(netout, buf);
692: }
693:
694: static
695: wontoption(option)
696: int option;
697: {
698: char *fmt;
699: char buf[10];
700:
701: switch (option) {
702:
703: case TELOPT_ECHO:
704: mode(ECHO|CRMOD, 0);
705: goto common;
706:
707: case TELOPT_BINARY:
708: mode(0, RAW);
709: /*FALL THRU*/
710:
711: case TELOPT_SGA:
712: common:
713: hisopts[option] = 0;
714: fmt = dont;
715: break;
716:
717: default:
718: fmt = dont;
719: }
720: sprintf(buf, fmt, option);
721: bputs(netout, buf);
722: }
723:
724: static
725: dooption(option)
726: int option;
727: {
728: char *fmt;
729: char buf[10];
730:
731: switch (option) {
732:
733: case TELOPT_TM:
734: fmt = wont;
735: break;
736:
737: case TELOPT_ECHO:
738: mode(ECHO|CRMOD, 0);
739: goto common;
740:
741: case TELOPT_BINARY:
742: mode(RAW, 0);
743: /*FALL THRU*/
744:
745: case TELOPT_SGA:
746: common:
747: fmt = will;
748: break;
749:
750: default:
751: fmt = wont;
752: break;
753: }
754: sprintf(buf, fmt, option);
755: bputs(netout, buf);
756: }
757:
758: /*
759: * Send interrupt to process on other side of pty.
760: * If it is in raw mode, just write NULL;
761: * otherwise, write intr char.
762: */
763: static
764: interrupt()
765: {
766: ptflush();
767: ioctl(ptfd, TIOCGETC, &ptt);
768: bput(ptout, ptb.sg_flags & RAW ? '\0' : ptt.t_intrc);
769: }
770:
771: static
772: catchint()
773: {
774: terminate("child death");
775: }
776:
777: #include <utmp.h>
778:
779: struct utmp wtmp;
780: char wtmpf[] = "/usr/adm/wtmp";
781: char utmp[] = "/etc/utmp";
782: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
783: #define SCMPN(a, b) strncmp(a, b, sizeof (a))
784:
785: static
786: rmut()
787: {
788: register f;
789: int found = 0;
790: char *line, *dev;
791: long time();
792:
793: dev = ptname(ptno);
794: if (dev == NULL)
795: return;
796: line = dev + 5;
797:
798: DEBUG fprintf(stderr, "closing connection on %s\n", line);
799:
800: f = open(utmp, 2);
801: if (f >= 0) {
802: while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
803: if (SCMPN(wtmp.ut_line, line) || wtmp.ut_name[0]==0)
804: continue;
805: lseek(f, -(long)sizeof (wtmp), 1);
806: SCPYN(wtmp.ut_name, "");
807: time(&wtmp.ut_time);
808: write(f, (char *)&wtmp, sizeof (wtmp));
809: found++;
810: }
811: close(f);
812: }
813: if (found) {
814: f = open(wtmpf, 1);
815: if (f >= 0) {
816: SCPYN(wtmp.ut_line, line);
817: SCPYN(wtmp.ut_name, "");
818: time(&wtmp.ut_time);
819: lseek(f, (long)0, 2);
820: write(f, (char *)&wtmp, sizeof (wtmp));
821: close(f);
822: }
823: }
824: chown(dev, 0, 0);
825: chmod(dev, 0666);
826: }
827:
828: static
829: getty(f)
830: {
831: int i;
832: struct sgttyb b;
833: char banner[128];
834: char *whoami();
835:
836: ioctl(f, TIOCSPGRP, 0);
837: signal(SIGTERM, SIG_DFL) ;
838: signal(SIGPIPE, SIG_DFL) ;
839: signal(SIGQUIT, SIG_DFL) ;
840: signal(SIGINT, SIG_DFL) ;
841: signal(SIGALRM, SIG_DFL) ;
842: signal(SIGHUP, SIG_DFL) ;
843: signal(SIGCHLD, SIG_DFL) ;
844: for (i = 0; i < NSYSFILE; i++)
845: if (i != f)
846: close(i);
847: for (i = 0; i < NSYSFILE; i++)
848: dup(f);
849: for (i=NSYSFILE; i<NOFILE; i++)
850: close(i) ;
851: ioctl(0, TIOCGETP, &b);
852: b.sg_flags |= CRMOD|XTABS|ANYP|ECHO;
853: b.sg_erase = '#';
854: b.sg_kill = '@';
855: ioctl(0, TIOCSETP, &b);
856: sprintf(banner, "%s\n", whoami());
857: write (netfd, banner, strlen(banner));
858: execl("/etc/login", "login", 0);
859: fatalperror(2, "/etc/login", errno);
860: exit(1);
861: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.