16295Sroot #ifndef lint 2*8456Ssam static char sccsid[] = "@(#)telnetd.c 4.9 82/10/10"; 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); 71*8456Ssam #ifndef DEBUG 72*8456Ssam if (fork()) 73*8456Ssam exit(0); 74*8456Ssam for (s = 0; s < 10; s++) 75*8456Ssam (void) close(s); 76*8456Ssam (void) open("/", 0); 77*8456Ssam (void) dup2(0, 1); 78*8456Ssam (void) dup2(0, 2); 79*8456Ssam { int tt = open("/dev/tty", 2); 80*8456Ssam if (tt > 0) { 81*8456Ssam ioctl(tt, TIOCNOTTY, 0); 82*8456Ssam close(tt); 83*8456Ssam } 84*8456Ssam } 85*8456Ssam #endif 866002Sroot for (;;) { 876002Sroot errno = 0; 886002Sroot if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) { 896002Sroot perror("socket"); 906002Sroot sleep(5); 916002Sroot continue; 926002Sroot } 936002Sroot if (accept(s, 0) < 0) { 946002Sroot perror("accept"); 956002Sroot close(s); 966002Sroot sleep(1); 976002Sroot continue; 986002Sroot } 996002Sroot if ((pid = fork()) < 0) 1006002Sroot printf("Out of processes\n"); 1016002Sroot else if (pid == 0) 1026002Sroot doit(s); 1036002Sroot close(s); 1046002Sroot while (wait3(status, WNOHANG, 0) > 0) 1056002Sroot continue; 1066002Sroot } 1076002Sroot /*NOTREACHED*/ 1086002Sroot } 1096002Sroot 1106002Sroot int cleanup(); 1116002Sroot 1126002Sroot /* 1136002Sroot * Get a pty, scan input lines. 1146002Sroot */ 1156002Sroot doit(f) 1166002Sroot { 1176002Sroot char *cp = line; 1186002Sroot int i, p, cc, t; 1196002Sroot struct sgttyb b; 1206002Sroot 1216002Sroot for (i = 0; i < 16; i++) { 1226002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 1236002Sroot p = open(cp, 2); 1246002Sroot if (p > 0) 1256002Sroot goto gotpty; 1266002Sroot } 1276002Sroot dup2(f, 1); 1286002Sroot printf("All network ports in use.\n"); 1296002Sroot exit(1); 1306002Sroot gotpty: 1316002Sroot dup2(f, 0); 1326002Sroot cp[strlen("/dev/")] = 't'; 1336002Sroot t = open("/dev/tty", 2); 1346002Sroot if (t >= 0) { 1356002Sroot ioctl(t, TIOCNOTTY, 0); 1366002Sroot close(t); 1376002Sroot } 1386002Sroot t = open(cp, 2); 1396002Sroot if (t < 0) { 1406002Sroot dup2(f, 2); 1416002Sroot perror(cp); 1426002Sroot exit(1); 1436002Sroot } 1446002Sroot ioctl(t, TIOCGETP, &b); 1456388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1466002Sroot ioctl(t, TIOCSETP, &b); 1476388Ssam ioctl(p, TIOCGETP, &b); 1488379Ssam b.sg_flags &= ~ECHO; 1496388Ssam ioctl(p, TIOCSETP, &b); 1506002Sroot if ((i = fork()) < 0) { 1516002Sroot dup2(f, 2); 1526002Sroot perror("fork"); 1536002Sroot exit(1); 1546002Sroot } 1556002Sroot if (i) 1566002Sroot telnet(f, p); 1576002Sroot close(f); 1586002Sroot close(p); 1596002Sroot dup2(t, 0); 1606002Sroot dup2(t, 1); 1616002Sroot dup2(t, 2); 1626002Sroot close(t); 1636002Sroot execl("/bin/login", "telnet-login", 0); 1646002Sroot perror("/bin/login"); 1656002Sroot exit(1); 1666002Sroot } 1676002Sroot 1686002Sroot /* 1696002Sroot * Main loop. Select from pty and network, and 1706002Sroot * hand data to telnet receiver finite state machine. 1716002Sroot */ 1726002Sroot telnet(f, p) 1736002Sroot { 1746002Sroot int on = 1; 1756002Sroot 1766002Sroot net = f, pty = p; 1776002Sroot ioctl(f, FIONBIO, &on); 1786002Sroot ioctl(p, FIONBIO, &on); 1796002Sroot signal(SIGTSTP, SIG_IGN); 1806002Sroot sigset(SIGCHLD, cleanup); 1816002Sroot 1828379Ssam /* 1838379Ssam * Request to do remote echo. 1848379Ssam */ 1858379Ssam dooption(TELOPT_ECHO); 1868379Ssam myopts[TELOPT_ECHO] = 1; 1876002Sroot for (;;) { 1886002Sroot int ibits = 0, obits = 0; 1896002Sroot register int c; 1906002Sroot 1916002Sroot /* 1926002Sroot * Never look for input if there's still 1936002Sroot * stuff in the corresponding output buffer 1946002Sroot */ 1956002Sroot if (nfrontp - nbackp) 1966002Sroot obits |= (1 << f); 1976002Sroot else 1986002Sroot ibits |= (1 << p); 1996002Sroot if (pfrontp - pbackp) 2006002Sroot obits |= (1 << p); 2016002Sroot else 2026002Sroot ibits |= (1 << f); 2036002Sroot if (ncc < 0 && pcc < 0) 2046002Sroot break; 2056002Sroot select(32, &ibits, &obits, INFINITY); 2066002Sroot if (ibits == 0 && obits == 0) { 2076002Sroot sleep(5); 2086002Sroot continue; 2096002Sroot } 2106002Sroot 2116002Sroot /* 2126002Sroot * Something to read from the network... 2136002Sroot */ 2146002Sroot if (ibits & (1 << f)) { 2156002Sroot ncc = read(f, netibuf, BUFSIZ); 2166002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2176002Sroot ncc = 0; 2186002Sroot else { 2196002Sroot if (ncc <= 0) 2206002Sroot break; 2216002Sroot netip = netibuf; 2226002Sroot } 2236002Sroot } 2246002Sroot 2256002Sroot /* 2266002Sroot * Something to read from the pty... 2276002Sroot */ 2286002Sroot if (ibits & (1 << p)) { 2296002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2306002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2316002Sroot pcc = 0; 2326002Sroot else { 2336002Sroot if (pcc <= 0) 2346002Sroot break; 2356002Sroot ptyip = ptyibuf; 2366002Sroot } 2376002Sroot } 2386002Sroot 2396002Sroot while (pcc > 0) { 2406002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2416002Sroot break; 2426002Sroot c = *ptyip++ & 0377, pcc--; 2436002Sroot if (c == IAC) 2446002Sroot *nfrontp++ = c; 2456002Sroot *nfrontp++ = c; 2466002Sroot } 2476002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2486002Sroot netflush(); 2496002Sroot if (ncc > 0) 2506002Sroot telrcv(); 2516002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2526002Sroot ptyflush(); 2536002Sroot } 2546002Sroot cleanup(); 2556002Sroot } 2566002Sroot 2576002Sroot /* 2586002Sroot * State for recv fsm 2596002Sroot */ 2606002Sroot #define TS_DATA 0 /* base state */ 2616002Sroot #define TS_IAC 1 /* look for double IAC's */ 2626002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2636002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2646002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2656002Sroot #define TS_WILL 5 /* will option negotiation */ 2666002Sroot #define TS_WONT 6 /* wont " */ 2676002Sroot #define TS_DO 7 /* do " */ 2686002Sroot #define TS_DONT 8 /* dont " */ 2696002Sroot 2706002Sroot telrcv() 2716002Sroot { 2726002Sroot register int c; 2736002Sroot static int state = TS_DATA; 2746002Sroot struct sgttyb b; 2756002Sroot 2766002Sroot while (ncc > 0) { 2776002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2786002Sroot return; 2796002Sroot c = *netip++ & 0377, ncc--; 2806002Sroot switch (state) { 2816002Sroot 2826002Sroot case TS_DATA: 2836002Sroot if (c == IAC) { 2846002Sroot state = TS_IAC; 2856002Sroot break; 2866002Sroot } 2876002Sroot if (inter > 0) 2886002Sroot break; 2896002Sroot *pfrontp++ = c; 2906002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2916002Sroot state = TS_CR; 2926002Sroot break; 2936002Sroot 2946002Sroot case TS_CR: 2956002Sroot if (c && c != '\n') 2966002Sroot *pfrontp++ = c; 2976002Sroot state = TS_DATA; 2986002Sroot break; 2996002Sroot 3006002Sroot case TS_IAC: 3016002Sroot switch (c) { 3026002Sroot 3036002Sroot /* 3046002Sroot * Send the process on the pty side an 3056002Sroot * interrupt. Do this with a NULL or 3066002Sroot * interrupt char; depending on the tty mode. 3076002Sroot */ 3086002Sroot case BREAK: 3096002Sroot case IP: 3106002Sroot interrupt(); 3116002Sroot break; 3126002Sroot 3136002Sroot /* 3146002Sroot * Are You There? 3156002Sroot */ 3166002Sroot case AYT: 3176002Sroot *pfrontp++ = BELL; 3186002Sroot break; 3196002Sroot 3206002Sroot /* 3216002Sroot * Erase Character and 3226002Sroot * Erase Line 3236002Sroot */ 3246002Sroot case EC: 3256002Sroot case EL: 3266002Sroot ptyflush(); /* half-hearted */ 3276002Sroot ioctl(pty, TIOCGETP, &b); 3286002Sroot *pfrontp++ = (c == EC) ? 3296002Sroot b.sg_erase : b.sg_kill; 3306002Sroot break; 3316002Sroot 3326002Sroot /* 3336002Sroot * Check for urgent data... 3346002Sroot */ 3356002Sroot case DM: 3366002Sroot break; 3376002Sroot 3386002Sroot /* 3396002Sroot * Begin option subnegotiation... 3406002Sroot */ 3416002Sroot case SB: 3426002Sroot state = TS_BEGINNEG; 3436002Sroot continue; 3446002Sroot 3456002Sroot case WILL: 3466002Sroot case WONT: 3476002Sroot case DO: 3486002Sroot case DONT: 3496002Sroot state = TS_WILL + (c - WILL); 3506002Sroot continue; 3516002Sroot 3526002Sroot case IAC: 3536002Sroot *pfrontp++ = c; 3546002Sroot break; 3556002Sroot } 3566002Sroot state = TS_DATA; 3576002Sroot break; 3586002Sroot 3596002Sroot case TS_BEGINNEG: 3606002Sroot if (c == IAC) 3616002Sroot state = TS_ENDNEG; 3626002Sroot break; 3636002Sroot 3646002Sroot case TS_ENDNEG: 3656002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3666002Sroot break; 3676002Sroot 3686002Sroot case TS_WILL: 3696002Sroot if (!hisopts[c]) 3706002Sroot willoption(c); 3716002Sroot state = TS_DATA; 3726002Sroot continue; 3736002Sroot 3746002Sroot case TS_WONT: 3756002Sroot if (hisopts[c]) 3766002Sroot wontoption(c); 3776002Sroot state = TS_DATA; 3786002Sroot continue; 3796002Sroot 3806002Sroot case TS_DO: 3816002Sroot if (!myopts[c]) 3826002Sroot dooption(c); 3836002Sroot state = TS_DATA; 3846002Sroot continue; 3856002Sroot 3866002Sroot case TS_DONT: 3876002Sroot if (myopts[c]) { 3886002Sroot myopts[c] = 0; 3896002Sroot sprintf(nfrontp, wont, c); 3908379Ssam nfrontp += sizeof (wont) - 2; 3916002Sroot } 3926002Sroot state = TS_DATA; 3936002Sroot continue; 3946002Sroot 3956002Sroot default: 3966002Sroot printf("netser: panic state=%d\n", state); 3976002Sroot exit(1); 3986002Sroot } 3996002Sroot } 4006002Sroot } 4016002Sroot 4026002Sroot willoption(option) 4036002Sroot int option; 4046002Sroot { 4056002Sroot char *fmt; 4066002Sroot 4076002Sroot switch (option) { 4086002Sroot 4096002Sroot case TELOPT_BINARY: 4106002Sroot mode(RAW, 0); 4116002Sroot goto common; 4126002Sroot 4136002Sroot case TELOPT_ECHO: 4146002Sroot mode(0, ECHO|CRMOD); 4156002Sroot /*FALL THRU*/ 4166002Sroot 4176002Sroot case TELOPT_SGA: 4186002Sroot common: 4196002Sroot hisopts[option] = 1; 4206002Sroot fmt = doopt; 4216002Sroot break; 4226002Sroot 4236002Sroot case TELOPT_TM: 4246002Sroot fmt = dont; 4256002Sroot break; 4266002Sroot 4276002Sroot default: 4286002Sroot fmt = dont; 4296002Sroot break; 4306002Sroot } 4316023Ssam sprintf(nfrontp, fmt, option); 4328379Ssam nfrontp += sizeof (dont) - 2; 4336002Sroot } 4346002Sroot 4356002Sroot wontoption(option) 4366002Sroot int option; 4376002Sroot { 4386002Sroot char *fmt; 4396002Sroot 4406002Sroot switch (option) { 4416002Sroot 4426002Sroot case TELOPT_ECHO: 4436002Sroot mode(ECHO|CRMOD, 0); 4446002Sroot goto common; 4456002Sroot 4466002Sroot case TELOPT_BINARY: 4476002Sroot mode(0, RAW); 4486002Sroot /*FALL THRU*/ 4496002Sroot 4506002Sroot case TELOPT_SGA: 4516002Sroot common: 4526002Sroot hisopts[option] = 0; 4536002Sroot fmt = dont; 4546002Sroot break; 4556002Sroot 4566002Sroot default: 4576002Sroot fmt = dont; 4586002Sroot } 4596002Sroot sprintf(nfrontp, fmt, option); 4608379Ssam nfrontp += sizeof (doopt) - 2; 4616002Sroot } 4626002Sroot 4636002Sroot dooption(option) 4646002Sroot int option; 4656002Sroot { 4666002Sroot char *fmt; 4676002Sroot 4686002Sroot switch (option) { 4696002Sroot 4706002Sroot case TELOPT_TM: 4716002Sroot fmt = wont; 4726002Sroot break; 4736002Sroot 4746002Sroot case TELOPT_ECHO: 4756002Sroot mode(ECHO|CRMOD, 0); 4766002Sroot goto common; 4776002Sroot 4786002Sroot case TELOPT_BINARY: 4796002Sroot mode(RAW, 0); 4806002Sroot /*FALL THRU*/ 4816002Sroot 4826002Sroot case TELOPT_SGA: 4836002Sroot common: 4846002Sroot fmt = will; 4856002Sroot break; 4866002Sroot 4876002Sroot default: 4886002Sroot fmt = wont; 4896002Sroot break; 4906002Sroot } 4916002Sroot sprintf(nfrontp, fmt, option); 4928379Ssam nfrontp += sizeof (doopt) - 2; 4936002Sroot } 4946002Sroot 4956002Sroot mode(on, off) 4966002Sroot int on, off; 4976002Sroot { 4986002Sroot struct sgttyb b; 4996002Sroot 5006002Sroot ptyflush(); 5016002Sroot ioctl(pty, TIOCGETP, &b); 5026002Sroot b.sg_flags |= on; 5036002Sroot b.sg_flags &= ~off; 5046002Sroot ioctl(pty, TIOCSETP, &b); 5056002Sroot } 5066002Sroot 5076002Sroot /* 5086002Sroot * Send interrupt to process on other side of pty. 5096002Sroot * If it is in raw mode, just write NULL; 5106002Sroot * otherwise, write intr char. 5116002Sroot */ 5126002Sroot interrupt() 5136002Sroot { 5146002Sroot struct sgttyb b; 5156002Sroot struct tchars tchars; 5166002Sroot 5176002Sroot ptyflush(); /* half-hearted */ 5186002Sroot ioctl(pty, TIOCGETP, &b); 5196002Sroot if (b.sg_flags & RAW) { 5206002Sroot *pfrontp++ = '\0'; 5216002Sroot return; 5226002Sroot } 5236002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5246002Sroot '\177' : tchars.t_intrc; 5256002Sroot } 5266002Sroot 5276002Sroot ptyflush() 5286002Sroot { 5296002Sroot int n; 5306002Sroot 5316002Sroot if ((n = pfrontp - pbackp) > 0) 5326002Sroot n = write(pty, pbackp, n); 5338346Ssam if (n < 0) 5348346Ssam return; 5356002Sroot pbackp += n; 5366002Sroot if (pbackp == pfrontp) 5376002Sroot pbackp = pfrontp = ptyobuf; 5386002Sroot } 5396002Sroot 5406002Sroot netflush() 5416002Sroot { 5426002Sroot int n; 5436002Sroot 5446002Sroot if ((n = nfrontp - nbackp) > 0) 5456002Sroot n = write(net, nbackp, n); 5468346Ssam if (n < 0) { 5478346Ssam if (errno == EWOULDBLOCK) 5488346Ssam return; 5498346Ssam /* should blow this guy away... */ 5508346Ssam return; 5518346Ssam } 5526002Sroot nbackp += n; 5536002Sroot if (nbackp == nfrontp) 5546002Sroot nbackp = nfrontp = netobuf; 5556002Sroot } 5566002Sroot 5576002Sroot cleanup() 5586002Sroot { 5596002Sroot int how = 2; 5606002Sroot 5616002Sroot rmut(); 5626002Sroot vhangup(); 5636002Sroot ioctl(net, SIOCDONE, &how); 5646002Sroot kill(0, SIGKILL); 5656002Sroot exit(1); 5666002Sroot } 5676002Sroot 5686002Sroot #include <utmp.h> 5696002Sroot 5706002Sroot struct utmp wtmp; 5716002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5726002Sroot char utmp[] = "/etc/utmp"; 5738379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5748379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5756002Sroot 5766002Sroot rmut() 5776002Sroot { 5786002Sroot register f; 5796002Sroot int found = 0; 5806002Sroot 5816002Sroot f = open(utmp, 2); 5826002Sroot if (f >= 0) { 5838379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5846002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5856002Sroot continue; 5868379Ssam lseek(f, -(long)sizeof (wtmp), 1); 5876002Sroot SCPYN(wtmp.ut_name, ""); 5886002Sroot time(&wtmp.ut_time); 5898379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5906002Sroot found++; 5916002Sroot } 5926002Sroot close(f); 5936002Sroot } 5946002Sroot if (found) { 5956002Sroot f = open(wtmpf, 1); 5966002Sroot if (f >= 0) { 5976002Sroot SCPYN(wtmp.ut_line, line+5); 5986002Sroot SCPYN(wtmp.ut_name, ""); 5996002Sroot time(&wtmp.ut_time); 6006002Sroot lseek(f, (long)0, 2); 6018379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 6026002Sroot close(f); 6036002Sroot } 6046002Sroot } 6056002Sroot chmod(line, 0666); 6066002Sroot chown(line, 0, 0); 6076002Sroot line[strlen("/dev/")] = 'p'; 6086002Sroot chmod(line, 0666); 6096002Sroot chown(line, 0, 0); 6106002Sroot } 611