16295Sroot #ifndef lint 2*8379Ssam static char sccsid[] = "@(#)telnetd.c 4.8 82/10/07"; 36295Sroot #endif 46295Sroot 56002Sroot /* 66002Sroot * Stripped-down telnet server. 76002Sroot */ 86002Sroot #include <stdio.h> 96002Sroot #include <signal.h> 106002Sroot #include <errno.h> 116002Sroot #include <sgtty.h> 126002Sroot #include <wait.h> 136002Sroot #include <sys/types.h> 146002Sroot #include <sys/socket.h> 156002Sroot #include <net/in.h> 168346Ssam #include <netdb.h> 176002Sroot #include "telnet.h" 186002Sroot 196002Sroot #define INFINITY 10000000 206002Sroot #define BELL '\07' 216002Sroot 226002Sroot char hisopts[256]; 236002Sroot char myopts[256]; 246002Sroot 256002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 266002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 276002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 286002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 296002Sroot 306002Sroot /* 316002Sroot * I/O data buffers, pointers, and counters. 326002Sroot */ 336002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 346002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 356002Sroot char netibuf[BUFSIZ], *netip = netibuf; 366388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 376002Sroot int pcc, ncc; 386002Sroot 396002Sroot int pty, net; 406002Sroot int inter; 416002Sroot extern int errno; 426002Sroot char line[] = "/dev/ptyp0"; 436002Sroot 448346Ssam struct sockaddr_in sin = { AF_INET }; 456287Sroot int options = SO_ACCEPTCONN|SO_KEEPALIVE; 466002Sroot 476002Sroot main(argc, argv) 486002Sroot char *argv[]; 496002Sroot { 506002Sroot int s, pid; 516002Sroot union wait status; 528346Ssam struct servent *sp; 536002Sroot 548346Ssam sp = getservbyname("telnet", "tcp"); 558346Ssam if (sp == 0) { 568346Ssam fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 578346Ssam exit(1); 588346Ssam } 598346Ssam sin.sin_port = sp->s_port; 606002Sroot argc--, argv++; 616002Sroot if (argc > 0 && !strcmp(argv[0], "-d")) 628346Ssam options |= SO_DEBUG, argc--, argv++; 638346Ssam if (argc > 0) { 648346Ssam sin.sin_port = atoi(*argv); 658346Ssam if (sin.sin_port <= 0) { 668346Ssam fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 678346Ssam exit(1); 688346Ssam } 698346Ssam } 706295Sroot sin.sin_port = htons(sin.sin_port); 716002Sroot for (;;) { 726002Sroot errno = 0; 736002Sroot if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) { 746002Sroot perror("socket"); 756002Sroot sleep(5); 766002Sroot continue; 776002Sroot } 786002Sroot if (accept(s, 0) < 0) { 796002Sroot perror("accept"); 806002Sroot close(s); 816002Sroot sleep(1); 826002Sroot continue; 836002Sroot } 846002Sroot if ((pid = fork()) < 0) 856002Sroot printf("Out of processes\n"); 866002Sroot else if (pid == 0) 876002Sroot doit(s); 886002Sroot close(s); 896002Sroot while (wait3(status, WNOHANG, 0) > 0) 906002Sroot continue; 916002Sroot } 926002Sroot /*NOTREACHED*/ 936002Sroot } 946002Sroot 956002Sroot int cleanup(); 966002Sroot 976002Sroot /* 986002Sroot * Get a pty, scan input lines. 996002Sroot */ 1006002Sroot doit(f) 1016002Sroot { 1026002Sroot char *cp = line; 1036002Sroot int i, p, cc, t; 1046002Sroot struct sgttyb b; 1056002Sroot 1066002Sroot for (i = 0; i < 16; i++) { 1076002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 1086002Sroot p = open(cp, 2); 1096002Sroot if (p > 0) 1106002Sroot goto gotpty; 1116002Sroot } 1126002Sroot dup2(f, 1); 1136002Sroot printf("All network ports in use.\n"); 1146002Sroot exit(1); 1156002Sroot gotpty: 1166002Sroot dup2(f, 0); 1176002Sroot cp[strlen("/dev/")] = 't'; 1186002Sroot t = open("/dev/tty", 2); 1196002Sroot if (t >= 0) { 1206002Sroot ioctl(t, TIOCNOTTY, 0); 1216002Sroot close(t); 1226002Sroot } 1236002Sroot t = open(cp, 2); 1246002Sroot if (t < 0) { 1256002Sroot dup2(f, 2); 1266002Sroot perror(cp); 1276002Sroot exit(1); 1286002Sroot } 1296002Sroot ioctl(t, TIOCGETP, &b); 1306388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1316002Sroot ioctl(t, TIOCSETP, &b); 1326388Ssam ioctl(p, TIOCGETP, &b); 133*8379Ssam b.sg_flags &= ~ECHO; 1346388Ssam ioctl(p, TIOCSETP, &b); 1356002Sroot if ((i = fork()) < 0) { 1366002Sroot dup2(f, 2); 1376002Sroot perror("fork"); 1386002Sroot exit(1); 1396002Sroot } 1406002Sroot if (i) 1416002Sroot telnet(f, p); 1426002Sroot close(f); 1436002Sroot close(p); 1446002Sroot dup2(t, 0); 1456002Sroot dup2(t, 1); 1466002Sroot dup2(t, 2); 1476002Sroot close(t); 1486002Sroot execl("/bin/login", "telnet-login", 0); 1496002Sroot perror("/bin/login"); 1506002Sroot exit(1); 1516002Sroot } 1526002Sroot 1536002Sroot /* 1546002Sroot * Main loop. Select from pty and network, and 1556002Sroot * hand data to telnet receiver finite state machine. 1566002Sroot */ 1576002Sroot telnet(f, p) 1586002Sroot { 1596002Sroot int on = 1; 1606002Sroot 1616002Sroot net = f, pty = p; 1626002Sroot ioctl(f, FIONBIO, &on); 1636002Sroot ioctl(p, FIONBIO, &on); 1646002Sroot signal(SIGTSTP, SIG_IGN); 1656002Sroot sigset(SIGCHLD, cleanup); 1666002Sroot 167*8379Ssam /* 168*8379Ssam * Request to do remote echo. 169*8379Ssam */ 170*8379Ssam dooption(TELOPT_ECHO); 171*8379Ssam myopts[TELOPT_ECHO] = 1; 1726002Sroot for (;;) { 1736002Sroot int ibits = 0, obits = 0; 1746002Sroot register int c; 1756002Sroot 1766002Sroot /* 1776002Sroot * Never look for input if there's still 1786002Sroot * stuff in the corresponding output buffer 1796002Sroot */ 1806002Sroot if (nfrontp - nbackp) 1816002Sroot obits |= (1 << f); 1826002Sroot else 1836002Sroot ibits |= (1 << p); 1846002Sroot if (pfrontp - pbackp) 1856002Sroot obits |= (1 << p); 1866002Sroot else 1876002Sroot ibits |= (1 << f); 1886002Sroot if (ncc < 0 && pcc < 0) 1896002Sroot break; 1906002Sroot select(32, &ibits, &obits, INFINITY); 1916002Sroot if (ibits == 0 && obits == 0) { 1926002Sroot sleep(5); 1936002Sroot continue; 1946002Sroot } 1956002Sroot 1966002Sroot /* 1976002Sroot * Something to read from the network... 1986002Sroot */ 1996002Sroot if (ibits & (1 << f)) { 2006002Sroot ncc = read(f, netibuf, BUFSIZ); 2016002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2026002Sroot ncc = 0; 2036002Sroot else { 2046002Sroot if (ncc <= 0) 2056002Sroot break; 2066002Sroot netip = netibuf; 2076002Sroot } 2086002Sroot } 2096002Sroot 2106002Sroot /* 2116002Sroot * Something to read from the pty... 2126002Sroot */ 2136002Sroot if (ibits & (1 << p)) { 2146002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2156002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2166002Sroot pcc = 0; 2176002Sroot else { 2186002Sroot if (pcc <= 0) 2196002Sroot break; 2206002Sroot ptyip = ptyibuf; 2216002Sroot } 2226002Sroot } 2236002Sroot 2246002Sroot while (pcc > 0) { 2256002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2266002Sroot break; 2276002Sroot c = *ptyip++ & 0377, pcc--; 2286002Sroot if (c == IAC) 2296002Sroot *nfrontp++ = c; 2306002Sroot *nfrontp++ = c; 2316002Sroot } 2326002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2336002Sroot netflush(); 2346002Sroot if (ncc > 0) 2356002Sroot telrcv(); 2366002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2376002Sroot ptyflush(); 2386002Sroot } 2396002Sroot cleanup(); 2406002Sroot } 2416002Sroot 2426002Sroot /* 2436002Sroot * State for recv fsm 2446002Sroot */ 2456002Sroot #define TS_DATA 0 /* base state */ 2466002Sroot #define TS_IAC 1 /* look for double IAC's */ 2476002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2486002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2496002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2506002Sroot #define TS_WILL 5 /* will option negotiation */ 2516002Sroot #define TS_WONT 6 /* wont " */ 2526002Sroot #define TS_DO 7 /* do " */ 2536002Sroot #define TS_DONT 8 /* dont " */ 2546002Sroot 2556002Sroot telrcv() 2566002Sroot { 2576002Sroot register int c; 2586002Sroot static int state = TS_DATA; 2596002Sroot struct sgttyb b; 2606002Sroot 2616002Sroot while (ncc > 0) { 2626002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2636002Sroot return; 2646002Sroot c = *netip++ & 0377, ncc--; 2656002Sroot switch (state) { 2666002Sroot 2676002Sroot case TS_DATA: 2686002Sroot if (c == IAC) { 2696002Sroot state = TS_IAC; 2706002Sroot break; 2716002Sroot } 2726002Sroot if (inter > 0) 2736002Sroot break; 2746002Sroot *pfrontp++ = c; 2756002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2766002Sroot state = TS_CR; 2776002Sroot break; 2786002Sroot 2796002Sroot case TS_CR: 2806002Sroot if (c && c != '\n') 2816002Sroot *pfrontp++ = c; 2826002Sroot state = TS_DATA; 2836002Sroot break; 2846002Sroot 2856002Sroot case TS_IAC: 2866002Sroot switch (c) { 2876002Sroot 2886002Sroot /* 2896002Sroot * Send the process on the pty side an 2906002Sroot * interrupt. Do this with a NULL or 2916002Sroot * interrupt char; depending on the tty mode. 2926002Sroot */ 2936002Sroot case BREAK: 2946002Sroot case IP: 2956002Sroot interrupt(); 2966002Sroot break; 2976002Sroot 2986002Sroot /* 2996002Sroot * Are You There? 3006002Sroot */ 3016002Sroot case AYT: 3026002Sroot *pfrontp++ = BELL; 3036002Sroot break; 3046002Sroot 3056002Sroot /* 3066002Sroot * Erase Character and 3076002Sroot * Erase Line 3086002Sroot */ 3096002Sroot case EC: 3106002Sroot case EL: 3116002Sroot ptyflush(); /* half-hearted */ 3126002Sroot ioctl(pty, TIOCGETP, &b); 3136002Sroot *pfrontp++ = (c == EC) ? 3146002Sroot b.sg_erase : b.sg_kill; 3156002Sroot break; 3166002Sroot 3176002Sroot /* 3186002Sroot * Check for urgent data... 3196002Sroot */ 3206002Sroot case DM: 3216002Sroot break; 3226002Sroot 3236002Sroot /* 3246002Sroot * Begin option subnegotiation... 3256002Sroot */ 3266002Sroot case SB: 3276002Sroot state = TS_BEGINNEG; 3286002Sroot continue; 3296002Sroot 3306002Sroot case WILL: 3316002Sroot case WONT: 3326002Sroot case DO: 3336002Sroot case DONT: 3346002Sroot state = TS_WILL + (c - WILL); 3356002Sroot continue; 3366002Sroot 3376002Sroot case IAC: 3386002Sroot *pfrontp++ = c; 3396002Sroot break; 3406002Sroot } 3416002Sroot state = TS_DATA; 3426002Sroot break; 3436002Sroot 3446002Sroot case TS_BEGINNEG: 3456002Sroot if (c == IAC) 3466002Sroot state = TS_ENDNEG; 3476002Sroot break; 3486002Sroot 3496002Sroot case TS_ENDNEG: 3506002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3516002Sroot break; 3526002Sroot 3536002Sroot case TS_WILL: 3546002Sroot if (!hisopts[c]) 3556002Sroot willoption(c); 3566002Sroot state = TS_DATA; 3576002Sroot continue; 3586002Sroot 3596002Sroot case TS_WONT: 3606002Sroot if (hisopts[c]) 3616002Sroot wontoption(c); 3626002Sroot state = TS_DATA; 3636002Sroot continue; 3646002Sroot 3656002Sroot case TS_DO: 3666002Sroot if (!myopts[c]) 3676002Sroot dooption(c); 3686002Sroot state = TS_DATA; 3696002Sroot continue; 3706002Sroot 3716002Sroot case TS_DONT: 3726002Sroot if (myopts[c]) { 3736002Sroot myopts[c] = 0; 3746002Sroot sprintf(nfrontp, wont, c); 375*8379Ssam nfrontp += sizeof (wont) - 2; 3766002Sroot } 3776002Sroot state = TS_DATA; 3786002Sroot continue; 3796002Sroot 3806002Sroot default: 3816002Sroot printf("netser: panic state=%d\n", state); 3826002Sroot exit(1); 3836002Sroot } 3846002Sroot } 3856002Sroot } 3866002Sroot 3876002Sroot willoption(option) 3886002Sroot int option; 3896002Sroot { 3906002Sroot char *fmt; 3916002Sroot 3926002Sroot switch (option) { 3936002Sroot 3946002Sroot case TELOPT_BINARY: 3956002Sroot mode(RAW, 0); 3966002Sroot goto common; 3976002Sroot 3986002Sroot case TELOPT_ECHO: 3996002Sroot mode(0, ECHO|CRMOD); 4006002Sroot /*FALL THRU*/ 4016002Sroot 4026002Sroot case TELOPT_SGA: 4036002Sroot common: 4046002Sroot hisopts[option] = 1; 4056002Sroot fmt = doopt; 4066002Sroot break; 4076002Sroot 4086002Sroot case TELOPT_TM: 4096002Sroot fmt = dont; 4106002Sroot break; 4116002Sroot 4126002Sroot default: 4136002Sroot fmt = dont; 4146002Sroot break; 4156002Sroot } 4166023Ssam sprintf(nfrontp, fmt, option); 417*8379Ssam nfrontp += sizeof (dont) - 2; 4186002Sroot } 4196002Sroot 4206002Sroot wontoption(option) 4216002Sroot int option; 4226002Sroot { 4236002Sroot char *fmt; 4246002Sroot 4256002Sroot switch (option) { 4266002Sroot 4276002Sroot case TELOPT_ECHO: 4286002Sroot mode(ECHO|CRMOD, 0); 4296002Sroot goto common; 4306002Sroot 4316002Sroot case TELOPT_BINARY: 4326002Sroot mode(0, RAW); 4336002Sroot /*FALL THRU*/ 4346002Sroot 4356002Sroot case TELOPT_SGA: 4366002Sroot common: 4376002Sroot hisopts[option] = 0; 4386002Sroot fmt = dont; 4396002Sroot break; 4406002Sroot 4416002Sroot default: 4426002Sroot fmt = dont; 4436002Sroot } 4446002Sroot sprintf(nfrontp, fmt, option); 445*8379Ssam nfrontp += sizeof (doopt) - 2; 4466002Sroot } 4476002Sroot 4486002Sroot dooption(option) 4496002Sroot int option; 4506002Sroot { 4516002Sroot char *fmt; 4526002Sroot 4536002Sroot switch (option) { 4546002Sroot 4556002Sroot case TELOPT_TM: 4566002Sroot fmt = wont; 4576002Sroot break; 4586002Sroot 4596002Sroot case TELOPT_ECHO: 4606002Sroot mode(ECHO|CRMOD, 0); 4616002Sroot goto common; 4626002Sroot 4636002Sroot case TELOPT_BINARY: 4646002Sroot mode(RAW, 0); 4656002Sroot /*FALL THRU*/ 4666002Sroot 4676002Sroot case TELOPT_SGA: 4686002Sroot common: 4696002Sroot fmt = will; 4706002Sroot break; 4716002Sroot 4726002Sroot default: 4736002Sroot fmt = wont; 4746002Sroot break; 4756002Sroot } 4766002Sroot sprintf(nfrontp, fmt, option); 477*8379Ssam nfrontp += sizeof (doopt) - 2; 4786002Sroot } 4796002Sroot 4806002Sroot mode(on, off) 4816002Sroot int on, off; 4826002Sroot { 4836002Sroot struct sgttyb b; 4846002Sroot 4856002Sroot ptyflush(); 4866002Sroot ioctl(pty, TIOCGETP, &b); 4876002Sroot b.sg_flags |= on; 4886002Sroot b.sg_flags &= ~off; 4896002Sroot ioctl(pty, TIOCSETP, &b); 4906002Sroot } 4916002Sroot 4926002Sroot /* 4936002Sroot * Send interrupt to process on other side of pty. 4946002Sroot * If it is in raw mode, just write NULL; 4956002Sroot * otherwise, write intr char. 4966002Sroot */ 4976002Sroot interrupt() 4986002Sroot { 4996002Sroot struct sgttyb b; 5006002Sroot struct tchars tchars; 5016002Sroot 5026002Sroot ptyflush(); /* half-hearted */ 5036002Sroot ioctl(pty, TIOCGETP, &b); 5046002Sroot if (b.sg_flags & RAW) { 5056002Sroot *pfrontp++ = '\0'; 5066002Sroot return; 5076002Sroot } 5086002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5096002Sroot '\177' : tchars.t_intrc; 5106002Sroot } 5116002Sroot 5126002Sroot ptyflush() 5136002Sroot { 5146002Sroot int n; 5156002Sroot 5166002Sroot if ((n = pfrontp - pbackp) > 0) 5176002Sroot n = write(pty, pbackp, n); 5188346Ssam if (n < 0) 5198346Ssam return; 5206002Sroot pbackp += n; 5216002Sroot if (pbackp == pfrontp) 5226002Sroot pbackp = pfrontp = ptyobuf; 5236002Sroot } 5246002Sroot 5256002Sroot netflush() 5266002Sroot { 5276002Sroot int n; 5286002Sroot 5296002Sroot if ((n = nfrontp - nbackp) > 0) 5306002Sroot n = write(net, nbackp, n); 5318346Ssam if (n < 0) { 5328346Ssam if (errno == EWOULDBLOCK) 5338346Ssam return; 5348346Ssam /* should blow this guy away... */ 5358346Ssam return; 5368346Ssam } 5376002Sroot nbackp += n; 5386002Sroot if (nbackp == nfrontp) 5396002Sroot nbackp = nfrontp = netobuf; 5406002Sroot } 5416002Sroot 5426002Sroot cleanup() 5436002Sroot { 5446002Sroot int how = 2; 5456002Sroot 5466002Sroot rmut(); 5476002Sroot vhangup(); 5486002Sroot ioctl(net, SIOCDONE, &how); 5496002Sroot kill(0, SIGKILL); 5506002Sroot exit(1); 5516002Sroot } 5526002Sroot 5536002Sroot #include <utmp.h> 5546002Sroot 5556002Sroot struct utmp wtmp; 5566002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5576002Sroot char utmp[] = "/etc/utmp"; 558*8379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 559*8379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5606002Sroot 5616002Sroot rmut() 5626002Sroot { 5636002Sroot register f; 5646002Sroot int found = 0; 5656002Sroot 5666002Sroot f = open(utmp, 2); 5676002Sroot if (f >= 0) { 568*8379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5696002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5706002Sroot continue; 571*8379Ssam lseek(f, -(long)sizeof (wtmp), 1); 5726002Sroot SCPYN(wtmp.ut_name, ""); 5736002Sroot time(&wtmp.ut_time); 574*8379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5756002Sroot found++; 5766002Sroot } 5776002Sroot close(f); 5786002Sroot } 5796002Sroot if (found) { 5806002Sroot f = open(wtmpf, 1); 5816002Sroot if (f >= 0) { 5826002Sroot SCPYN(wtmp.ut_line, line+5); 5836002Sroot SCPYN(wtmp.ut_name, ""); 5846002Sroot time(&wtmp.ut_time); 5856002Sroot lseek(f, (long)0, 2); 586*8379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5876002Sroot close(f); 5886002Sroot } 5896002Sroot } 5906002Sroot chmod(line, 0666); 5916002Sroot chown(line, 0, 0); 5926002Sroot line[strlen("/dev/")] = 'p'; 5936002Sroot chmod(line, 0666); 5946002Sroot chown(line, 0, 0); 5956002Sroot } 596