16295Sroot #ifndef lint 2*9218Ssam static char sccsid[] = "@(#)telnetd.c 4.10 82/11/14"; 36295Sroot #endif 46295Sroot 56002Sroot /* 66002Sroot * Stripped-down telnet server. 76002Sroot */ 8*9218Ssam #include <sys/types.h> 9*9218Ssam #include <sys/socket.h> 10*9218Ssam 11*9218Ssam #include <netinet/in.h> 12*9218Ssam 136002Sroot #include <stdio.h> 146002Sroot #include <signal.h> 156002Sroot #include <errno.h> 166002Sroot #include <sgtty.h> 176002Sroot #include <wait.h> 188346Ssam #include <netdb.h> 19*9218Ssam 206002Sroot #include "telnet.h" 216002Sroot 226002Sroot #define BELL '\07' 236002Sroot 246002Sroot char hisopts[256]; 256002Sroot char myopts[256]; 266002Sroot 276002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 286002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 296002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 306002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 316002Sroot 326002Sroot /* 336002Sroot * I/O data buffers, pointers, and counters. 346002Sroot */ 356002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 366002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 376002Sroot char netibuf[BUFSIZ], *netip = netibuf; 386388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 396002Sroot int pcc, ncc; 406002Sroot 416002Sroot int pty, net; 426002Sroot int inter; 436002Sroot extern int errno; 446002Sroot char line[] = "/dev/ptyp0"; 456002Sroot 468346Ssam struct sockaddr_in sin = { AF_INET }; 476002Sroot 486002Sroot main(argc, argv) 496002Sroot char *argv[]; 506002Sroot { 516002Sroot int s, pid; 526002Sroot union wait status; 538346Ssam struct servent *sp; 546002Sroot 558346Ssam sp = getservbyname("telnet", "tcp"); 568346Ssam if (sp == 0) { 578346Ssam fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 588346Ssam exit(1); 598346Ssam } 608346Ssam sin.sin_port = sp->s_port; 616002Sroot argc--, argv++; 628346Ssam if (argc > 0) { 638346Ssam sin.sin_port = atoi(*argv); 648346Ssam if (sin.sin_port <= 0) { 658346Ssam fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 668346Ssam exit(1); 678346Ssam } 688346Ssam } 69*9218Ssam sin.sin_port = htons((u_short)sin.sin_port); 708456Ssam #ifndef DEBUG 718456Ssam if (fork()) 728456Ssam exit(0); 738456Ssam for (s = 0; s < 10; s++) 748456Ssam (void) close(s); 758456Ssam (void) open("/", 0); 768456Ssam (void) dup2(0, 1); 778456Ssam (void) dup2(0, 2); 788456Ssam { int tt = open("/dev/tty", 2); 798456Ssam if (tt > 0) { 808456Ssam ioctl(tt, TIOCNOTTY, 0); 818456Ssam close(tt); 828456Ssam } 838456Ssam } 848456Ssam #endif 85*9218Ssam again: 86*9218Ssam s = socket(0, SOCK_STREAM, 0, 0); 87*9218Ssam if (s < 0) { 88*9218Ssam perror("telnetd: socket");; 89*9218Ssam sleep(5); 90*9218Ssam goto again; 91*9218Ssam } 92*9218Ssam while (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) { 93*9218Ssam perror("telnetd: bind"); 94*9218Ssam sleep(5); 95*9218Ssam } 96*9218Ssam listen(s, 10); 976002Sroot for (;;) { 98*9218Ssam int s2; 99*9218Ssam 100*9218Ssam s2 = accept(s, (caddr_t)0, 0); 101*9218Ssam if (s2 < 0) { 1026002Sroot perror("accept"); 1036002Sroot sleep(1); 1046002Sroot continue; 1056002Sroot } 1066002Sroot if ((pid = fork()) < 0) 1076002Sroot printf("Out of processes\n"); 1086002Sroot else if (pid == 0) 109*9218Ssam doit(s2); 110*9218Ssam close(s2); 1116002Sroot while (wait3(status, WNOHANG, 0) > 0) 1126002Sroot continue; 1136002Sroot } 1146002Sroot /*NOTREACHED*/ 1156002Sroot } 1166002Sroot 1176002Sroot int cleanup(); 1186002Sroot 1196002Sroot /* 1206002Sroot * Get a pty, scan input lines. 1216002Sroot */ 1226002Sroot doit(f) 1236002Sroot { 1246002Sroot char *cp = line; 1256002Sroot int i, p, cc, t; 1266002Sroot struct sgttyb b; 1276002Sroot 1286002Sroot for (i = 0; i < 16; i++) { 1296002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 1306002Sroot p = open(cp, 2); 1316002Sroot if (p > 0) 1326002Sroot goto gotpty; 1336002Sroot } 1346002Sroot dup2(f, 1); 1356002Sroot printf("All network ports in use.\n"); 1366002Sroot exit(1); 1376002Sroot gotpty: 1386002Sroot dup2(f, 0); 1396002Sroot cp[strlen("/dev/")] = 't'; 1406002Sroot t = open("/dev/tty", 2); 1416002Sroot if (t >= 0) { 1426002Sroot ioctl(t, TIOCNOTTY, 0); 1436002Sroot close(t); 1446002Sroot } 1456002Sroot t = open(cp, 2); 1466002Sroot if (t < 0) { 1476002Sroot dup2(f, 2); 1486002Sroot perror(cp); 1496002Sroot exit(1); 1506002Sroot } 1516002Sroot ioctl(t, TIOCGETP, &b); 1526388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1536002Sroot ioctl(t, TIOCSETP, &b); 1546388Ssam ioctl(p, TIOCGETP, &b); 1558379Ssam b.sg_flags &= ~ECHO; 1566388Ssam ioctl(p, TIOCSETP, &b); 1576002Sroot if ((i = fork()) < 0) { 1586002Sroot dup2(f, 2); 1596002Sroot perror("fork"); 1606002Sroot exit(1); 1616002Sroot } 1626002Sroot if (i) 1636002Sroot telnet(f, p); 1646002Sroot close(f); 1656002Sroot close(p); 1666002Sroot dup2(t, 0); 1676002Sroot dup2(t, 1); 1686002Sroot dup2(t, 2); 1696002Sroot close(t); 1706002Sroot execl("/bin/login", "telnet-login", 0); 1716002Sroot perror("/bin/login"); 1726002Sroot exit(1); 1736002Sroot } 1746002Sroot 1756002Sroot /* 1766002Sroot * Main loop. Select from pty and network, and 1776002Sroot * hand data to telnet receiver finite state machine. 1786002Sroot */ 1796002Sroot telnet(f, p) 1806002Sroot { 1816002Sroot int on = 1; 1826002Sroot 1836002Sroot net = f, pty = p; 1846002Sroot ioctl(f, FIONBIO, &on); 1856002Sroot ioctl(p, FIONBIO, &on); 1866002Sroot signal(SIGTSTP, SIG_IGN); 1876002Sroot sigset(SIGCHLD, cleanup); 1886002Sroot 1898379Ssam /* 1908379Ssam * Request to do remote echo. 1918379Ssam */ 1928379Ssam dooption(TELOPT_ECHO); 1938379Ssam myopts[TELOPT_ECHO] = 1; 1946002Sroot for (;;) { 1956002Sroot int ibits = 0, obits = 0; 1966002Sroot register int c; 1976002Sroot 1986002Sroot /* 1996002Sroot * Never look for input if there's still 2006002Sroot * stuff in the corresponding output buffer 2016002Sroot */ 2026002Sroot if (nfrontp - nbackp) 2036002Sroot obits |= (1 << f); 2046002Sroot else 2056002Sroot ibits |= (1 << p); 2066002Sroot if (pfrontp - pbackp) 2076002Sroot obits |= (1 << p); 2086002Sroot else 2096002Sroot ibits |= (1 << f); 2106002Sroot if (ncc < 0 && pcc < 0) 2116002Sroot break; 212*9218Ssam select(16, &ibits, &obits, 0, 0); 2136002Sroot if (ibits == 0 && obits == 0) { 2146002Sroot sleep(5); 2156002Sroot continue; 2166002Sroot } 2176002Sroot 2186002Sroot /* 2196002Sroot * Something to read from the network... 2206002Sroot */ 2216002Sroot if (ibits & (1 << f)) { 2226002Sroot ncc = read(f, netibuf, BUFSIZ); 2236002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2246002Sroot ncc = 0; 2256002Sroot else { 2266002Sroot if (ncc <= 0) 2276002Sroot break; 2286002Sroot netip = netibuf; 2296002Sroot } 2306002Sroot } 2316002Sroot 2326002Sroot /* 2336002Sroot * Something to read from the pty... 2346002Sroot */ 2356002Sroot if (ibits & (1 << p)) { 2366002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2376002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2386002Sroot pcc = 0; 2396002Sroot else { 2406002Sroot if (pcc <= 0) 2416002Sroot break; 2426002Sroot ptyip = ptyibuf; 2436002Sroot } 2446002Sroot } 2456002Sroot 2466002Sroot while (pcc > 0) { 2476002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2486002Sroot break; 2496002Sroot c = *ptyip++ & 0377, pcc--; 2506002Sroot if (c == IAC) 2516002Sroot *nfrontp++ = c; 2526002Sroot *nfrontp++ = c; 2536002Sroot } 2546002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2556002Sroot netflush(); 2566002Sroot if (ncc > 0) 2576002Sroot telrcv(); 2586002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2596002Sroot ptyflush(); 2606002Sroot } 2616002Sroot cleanup(); 2626002Sroot } 2636002Sroot 2646002Sroot /* 2656002Sroot * State for recv fsm 2666002Sroot */ 2676002Sroot #define TS_DATA 0 /* base state */ 2686002Sroot #define TS_IAC 1 /* look for double IAC's */ 2696002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2706002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2716002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2726002Sroot #define TS_WILL 5 /* will option negotiation */ 2736002Sroot #define TS_WONT 6 /* wont " */ 2746002Sroot #define TS_DO 7 /* do " */ 2756002Sroot #define TS_DONT 8 /* dont " */ 2766002Sroot 2776002Sroot telrcv() 2786002Sroot { 2796002Sroot register int c; 2806002Sroot static int state = TS_DATA; 2816002Sroot struct sgttyb b; 2826002Sroot 2836002Sroot while (ncc > 0) { 2846002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2856002Sroot return; 2866002Sroot c = *netip++ & 0377, ncc--; 2876002Sroot switch (state) { 2886002Sroot 2896002Sroot case TS_DATA: 2906002Sroot if (c == IAC) { 2916002Sroot state = TS_IAC; 2926002Sroot break; 2936002Sroot } 2946002Sroot if (inter > 0) 2956002Sroot break; 2966002Sroot *pfrontp++ = c; 2976002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2986002Sroot state = TS_CR; 2996002Sroot break; 3006002Sroot 3016002Sroot case TS_CR: 3026002Sroot if (c && c != '\n') 3036002Sroot *pfrontp++ = c; 3046002Sroot state = TS_DATA; 3056002Sroot break; 3066002Sroot 3076002Sroot case TS_IAC: 3086002Sroot switch (c) { 3096002Sroot 3106002Sroot /* 3116002Sroot * Send the process on the pty side an 3126002Sroot * interrupt. Do this with a NULL or 3136002Sroot * interrupt char; depending on the tty mode. 3146002Sroot */ 3156002Sroot case BREAK: 3166002Sroot case IP: 3176002Sroot interrupt(); 3186002Sroot break; 3196002Sroot 3206002Sroot /* 3216002Sroot * Are You There? 3226002Sroot */ 3236002Sroot case AYT: 3246002Sroot *pfrontp++ = BELL; 3256002Sroot break; 3266002Sroot 3276002Sroot /* 3286002Sroot * Erase Character and 3296002Sroot * Erase Line 3306002Sroot */ 3316002Sroot case EC: 3326002Sroot case EL: 3336002Sroot ptyflush(); /* half-hearted */ 3346002Sroot ioctl(pty, TIOCGETP, &b); 3356002Sroot *pfrontp++ = (c == EC) ? 3366002Sroot b.sg_erase : b.sg_kill; 3376002Sroot break; 3386002Sroot 3396002Sroot /* 3406002Sroot * Check for urgent data... 3416002Sroot */ 3426002Sroot case DM: 3436002Sroot break; 3446002Sroot 3456002Sroot /* 3466002Sroot * Begin option subnegotiation... 3476002Sroot */ 3486002Sroot case SB: 3496002Sroot state = TS_BEGINNEG; 3506002Sroot continue; 3516002Sroot 3526002Sroot case WILL: 3536002Sroot case WONT: 3546002Sroot case DO: 3556002Sroot case DONT: 3566002Sroot state = TS_WILL + (c - WILL); 3576002Sroot continue; 3586002Sroot 3596002Sroot case IAC: 3606002Sroot *pfrontp++ = c; 3616002Sroot break; 3626002Sroot } 3636002Sroot state = TS_DATA; 3646002Sroot break; 3656002Sroot 3666002Sroot case TS_BEGINNEG: 3676002Sroot if (c == IAC) 3686002Sroot state = TS_ENDNEG; 3696002Sroot break; 3706002Sroot 3716002Sroot case TS_ENDNEG: 3726002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3736002Sroot break; 3746002Sroot 3756002Sroot case TS_WILL: 3766002Sroot if (!hisopts[c]) 3776002Sroot willoption(c); 3786002Sroot state = TS_DATA; 3796002Sroot continue; 3806002Sroot 3816002Sroot case TS_WONT: 3826002Sroot if (hisopts[c]) 3836002Sroot wontoption(c); 3846002Sroot state = TS_DATA; 3856002Sroot continue; 3866002Sroot 3876002Sroot case TS_DO: 3886002Sroot if (!myopts[c]) 3896002Sroot dooption(c); 3906002Sroot state = TS_DATA; 3916002Sroot continue; 3926002Sroot 3936002Sroot case TS_DONT: 3946002Sroot if (myopts[c]) { 3956002Sroot myopts[c] = 0; 3966002Sroot sprintf(nfrontp, wont, c); 3978379Ssam nfrontp += sizeof (wont) - 2; 3986002Sroot } 3996002Sroot state = TS_DATA; 4006002Sroot continue; 4016002Sroot 4026002Sroot default: 403*9218Ssam printf("telnetd: panic state=%d\n", state); 4046002Sroot exit(1); 4056002Sroot } 4066002Sroot } 4076002Sroot } 4086002Sroot 4096002Sroot willoption(option) 4106002Sroot int option; 4116002Sroot { 4126002Sroot char *fmt; 4136002Sroot 4146002Sroot switch (option) { 4156002Sroot 4166002Sroot case TELOPT_BINARY: 4176002Sroot mode(RAW, 0); 4186002Sroot goto common; 4196002Sroot 4206002Sroot case TELOPT_ECHO: 4216002Sroot mode(0, ECHO|CRMOD); 4226002Sroot /*FALL THRU*/ 4236002Sroot 4246002Sroot case TELOPT_SGA: 4256002Sroot common: 4266002Sroot hisopts[option] = 1; 4276002Sroot fmt = doopt; 4286002Sroot break; 4296002Sroot 4306002Sroot case TELOPT_TM: 4316002Sroot fmt = dont; 4326002Sroot break; 4336002Sroot 4346002Sroot default: 4356002Sroot fmt = dont; 4366002Sroot break; 4376002Sroot } 4386023Ssam sprintf(nfrontp, fmt, option); 4398379Ssam nfrontp += sizeof (dont) - 2; 4406002Sroot } 4416002Sroot 4426002Sroot wontoption(option) 4436002Sroot int option; 4446002Sroot { 4456002Sroot char *fmt; 4466002Sroot 4476002Sroot switch (option) { 4486002Sroot 4496002Sroot case TELOPT_ECHO: 4506002Sroot mode(ECHO|CRMOD, 0); 4516002Sroot goto common; 4526002Sroot 4536002Sroot case TELOPT_BINARY: 4546002Sroot mode(0, RAW); 4556002Sroot /*FALL THRU*/ 4566002Sroot 4576002Sroot case TELOPT_SGA: 4586002Sroot common: 4596002Sroot hisopts[option] = 0; 4606002Sroot fmt = dont; 4616002Sroot break; 4626002Sroot 4636002Sroot default: 4646002Sroot fmt = dont; 4656002Sroot } 4666002Sroot sprintf(nfrontp, fmt, option); 4678379Ssam nfrontp += sizeof (doopt) - 2; 4686002Sroot } 4696002Sroot 4706002Sroot dooption(option) 4716002Sroot int option; 4726002Sroot { 4736002Sroot char *fmt; 4746002Sroot 4756002Sroot switch (option) { 4766002Sroot 4776002Sroot case TELOPT_TM: 4786002Sroot fmt = wont; 4796002Sroot break; 4806002Sroot 4816002Sroot case TELOPT_ECHO: 4826002Sroot mode(ECHO|CRMOD, 0); 4836002Sroot goto common; 4846002Sroot 4856002Sroot case TELOPT_BINARY: 4866002Sroot mode(RAW, 0); 4876002Sroot /*FALL THRU*/ 4886002Sroot 4896002Sroot case TELOPT_SGA: 4906002Sroot common: 4916002Sroot fmt = will; 4926002Sroot break; 4936002Sroot 4946002Sroot default: 4956002Sroot fmt = wont; 4966002Sroot break; 4976002Sroot } 4986002Sroot sprintf(nfrontp, fmt, option); 4998379Ssam nfrontp += sizeof (doopt) - 2; 5006002Sroot } 5016002Sroot 5026002Sroot mode(on, off) 5036002Sroot int on, off; 5046002Sroot { 5056002Sroot struct sgttyb b; 5066002Sroot 5076002Sroot ptyflush(); 5086002Sroot ioctl(pty, TIOCGETP, &b); 5096002Sroot b.sg_flags |= on; 5106002Sroot b.sg_flags &= ~off; 5116002Sroot ioctl(pty, TIOCSETP, &b); 5126002Sroot } 5136002Sroot 5146002Sroot /* 5156002Sroot * Send interrupt to process on other side of pty. 5166002Sroot * If it is in raw mode, just write NULL; 5176002Sroot * otherwise, write intr char. 5186002Sroot */ 5196002Sroot interrupt() 5206002Sroot { 5216002Sroot struct sgttyb b; 5226002Sroot struct tchars tchars; 5236002Sroot 5246002Sroot ptyflush(); /* half-hearted */ 5256002Sroot ioctl(pty, TIOCGETP, &b); 5266002Sroot if (b.sg_flags & RAW) { 5276002Sroot *pfrontp++ = '\0'; 5286002Sroot return; 5296002Sroot } 5306002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5316002Sroot '\177' : tchars.t_intrc; 5326002Sroot } 5336002Sroot 5346002Sroot ptyflush() 5356002Sroot { 5366002Sroot int n; 5376002Sroot 5386002Sroot if ((n = pfrontp - pbackp) > 0) 5396002Sroot n = write(pty, pbackp, n); 5408346Ssam if (n < 0) 5418346Ssam return; 5426002Sroot pbackp += n; 5436002Sroot if (pbackp == pfrontp) 5446002Sroot pbackp = pfrontp = ptyobuf; 5456002Sroot } 5466002Sroot 5476002Sroot netflush() 5486002Sroot { 5496002Sroot int n; 5506002Sroot 5516002Sroot if ((n = nfrontp - nbackp) > 0) 5526002Sroot n = write(net, nbackp, n); 5538346Ssam if (n < 0) { 5548346Ssam if (errno == EWOULDBLOCK) 5558346Ssam return; 5568346Ssam /* should blow this guy away... */ 5578346Ssam return; 5588346Ssam } 5596002Sroot nbackp += n; 5606002Sroot if (nbackp == nfrontp) 5616002Sroot nbackp = nfrontp = netobuf; 5626002Sroot } 5636002Sroot 5646002Sroot cleanup() 5656002Sroot { 5666002Sroot int how = 2; 5676002Sroot 5686002Sroot rmut(); 5696002Sroot vhangup(); 570*9218Ssam #ifndef notdef 5716002Sroot ioctl(net, SIOCDONE, &how); 572*9218Ssam #endif 5736002Sroot kill(0, SIGKILL); 5746002Sroot exit(1); 5756002Sroot } 5766002Sroot 5776002Sroot #include <utmp.h> 5786002Sroot 5796002Sroot struct utmp wtmp; 5806002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5816002Sroot char utmp[] = "/etc/utmp"; 5828379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5838379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5846002Sroot 5856002Sroot rmut() 5866002Sroot { 5876002Sroot register f; 5886002Sroot int found = 0; 5896002Sroot 5906002Sroot f = open(utmp, 2); 5916002Sroot if (f >= 0) { 5928379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5936002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5946002Sroot continue; 5958379Ssam lseek(f, -(long)sizeof (wtmp), 1); 5966002Sroot SCPYN(wtmp.ut_name, ""); 5976002Sroot time(&wtmp.ut_time); 5988379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5996002Sroot found++; 6006002Sroot } 6016002Sroot close(f); 6026002Sroot } 6036002Sroot if (found) { 6046002Sroot f = open(wtmpf, 1); 6056002Sroot if (f >= 0) { 6066002Sroot SCPYN(wtmp.ut_line, line+5); 6076002Sroot SCPYN(wtmp.ut_name, ""); 6086002Sroot time(&wtmp.ut_time); 6096002Sroot lseek(f, (long)0, 2); 6108379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 6116002Sroot close(f); 6126002Sroot } 6136002Sroot } 6146002Sroot chmod(line, 0666); 6156002Sroot chown(line, 0, 0); 6166002Sroot line[strlen("/dev/")] = 'p'; 6176002Sroot chmod(line, 0666); 6186002Sroot chown(line, 0, 0); 6196002Sroot } 620