16295Sroot #ifndef lint 2*10008Ssam static char sccsid[] = "@(#)telnetd.c 4.14 82/12/29"; 36295Sroot #endif 46295Sroot 56002Sroot /* 66002Sroot * Stripped-down telnet server. 76002Sroot */ 89218Ssam #include <sys/types.h> 99218Ssam #include <sys/socket.h> 109218Ssam 119218Ssam #include <netinet/in.h> 129218Ssam 136002Sroot #include <stdio.h> 146002Sroot #include <signal.h> 156002Sroot #include <errno.h> 166002Sroot #include <sgtty.h> 176002Sroot #include <wait.h> 188346Ssam #include <netdb.h> 199218Ssam 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 } 689969Ssam sin.sin_port = htons((u_short)sin.sin_port); 698346Ssam } 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 859218Ssam again: 869288Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 879218Ssam if (s < 0) { 889218Ssam perror("telnetd: socket");; 899218Ssam sleep(5); 909218Ssam goto again; 919218Ssam } 929218Ssam while (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) { 939218Ssam perror("telnetd: bind"); 949218Ssam sleep(5); 959218Ssam } 969218Ssam listen(s, 10); 976002Sroot for (;;) { 989218Ssam int s2; 999218Ssam 1009244Ssam s2 = accept(s, (caddr_t)0, 0, 0); 1019218Ssam if (s2 < 0) { 1029244Ssam perror("telnetd: accept"); 1036002Sroot sleep(1); 1046002Sroot continue; 1056002Sroot } 1066002Sroot if ((pid = fork()) < 0) 1076002Sroot printf("Out of processes\n"); 1086002Sroot else if (pid == 0) 1099218Ssam doit(s2); 1109218Ssam 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 } 1349244Ssam fatal(f, "All network ports in use"); 1359244Ssam /*NOTREACHED*/ 1366002Sroot gotpty: 1376002Sroot dup2(f, 0); 1386002Sroot cp[strlen("/dev/")] = 't'; 1396002Sroot t = open("/dev/tty", 2); 1406002Sroot if (t >= 0) { 1416002Sroot ioctl(t, TIOCNOTTY, 0); 1426002Sroot close(t); 1436002Sroot } 1446002Sroot t = open(cp, 2); 1459244Ssam if (t < 0) 1469244Ssam fatalperror(f, cp, errno); 1476002Sroot ioctl(t, TIOCGETP, &b); 1486388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1496002Sroot ioctl(t, TIOCSETP, &b); 1506388Ssam ioctl(p, TIOCGETP, &b); 1518379Ssam b.sg_flags &= ~ECHO; 1526388Ssam ioctl(p, TIOCSETP, &b); 1539244Ssam if ((i = fork()) < 0) 1549244Ssam fatalperror(f, "fork", errno); 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); 1649244Ssam fatalperror(f, "/bin/login", errno); 1659244Ssam /*NOTREACHED*/ 1669244Ssam } 1679244Ssam 1689244Ssam fatal(f, msg) 1699244Ssam int f; 1709244Ssam char *msg; 1719244Ssam { 1729244Ssam char buf[BUFSIZ]; 1739244Ssam 1749244Ssam (void) sprintf(buf, "telnetd: %s.\n", msg); 1759244Ssam (void) write(f, buf, strlen(buf)); 1766002Sroot exit(1); 1776002Sroot } 1786002Sroot 1799244Ssam fatalperror(f, msg, errno) 1809244Ssam int f; 1819244Ssam char *msg; 1829244Ssam int errno; 1839244Ssam { 1849244Ssam char buf[BUFSIZ]; 1859244Ssam extern char *sys_errlist[]; 1869244Ssam 1879244Ssam (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 1889244Ssam fatal(f, buf); 1899244Ssam } 1909244Ssam 1916002Sroot /* 1926002Sroot * Main loop. Select from pty and network, and 1936002Sroot * hand data to telnet receiver finite state machine. 1946002Sroot */ 1956002Sroot telnet(f, p) 1966002Sroot { 1976002Sroot int on = 1; 1986002Sroot 1996002Sroot net = f, pty = p; 2006002Sroot ioctl(f, FIONBIO, &on); 2016002Sroot ioctl(p, FIONBIO, &on); 2026002Sroot signal(SIGTSTP, SIG_IGN); 2036002Sroot sigset(SIGCHLD, cleanup); 2046002Sroot 2058379Ssam /* 2068379Ssam * Request to do remote echo. 2078379Ssam */ 2088379Ssam dooption(TELOPT_ECHO); 2098379Ssam myopts[TELOPT_ECHO] = 1; 2106002Sroot for (;;) { 2116002Sroot int ibits = 0, obits = 0; 2126002Sroot register int c; 2136002Sroot 2146002Sroot /* 2156002Sroot * Never look for input if there's still 2166002Sroot * stuff in the corresponding output buffer 2176002Sroot */ 2186002Sroot if (nfrontp - nbackp) 2196002Sroot obits |= (1 << f); 2206002Sroot else 2216002Sroot ibits |= (1 << p); 2226002Sroot if (pfrontp - pbackp) 2236002Sroot obits |= (1 << p); 2246002Sroot else 2256002Sroot ibits |= (1 << f); 2266002Sroot if (ncc < 0 && pcc < 0) 2276002Sroot break; 2289218Ssam select(16, &ibits, &obits, 0, 0); 2296002Sroot if (ibits == 0 && obits == 0) { 2306002Sroot sleep(5); 2316002Sroot continue; 2326002Sroot } 2336002Sroot 2346002Sroot /* 2356002Sroot * Something to read from the network... 2366002Sroot */ 2376002Sroot if (ibits & (1 << f)) { 2386002Sroot ncc = read(f, netibuf, BUFSIZ); 2396002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2406002Sroot ncc = 0; 2416002Sroot else { 2426002Sroot if (ncc <= 0) 2436002Sroot break; 2446002Sroot netip = netibuf; 2456002Sroot } 2466002Sroot } 2476002Sroot 2486002Sroot /* 2496002Sroot * Something to read from the pty... 2506002Sroot */ 2516002Sroot if (ibits & (1 << p)) { 2526002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2536002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2546002Sroot pcc = 0; 2556002Sroot else { 2566002Sroot if (pcc <= 0) 2576002Sroot break; 2586002Sroot ptyip = ptyibuf; 2596002Sroot } 2606002Sroot } 2616002Sroot 2626002Sroot while (pcc > 0) { 2636002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2646002Sroot break; 2656002Sroot c = *ptyip++ & 0377, pcc--; 2666002Sroot if (c == IAC) 2676002Sroot *nfrontp++ = c; 2686002Sroot *nfrontp++ = c; 2696002Sroot } 2706002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2716002Sroot netflush(); 2726002Sroot if (ncc > 0) 2736002Sroot telrcv(); 2746002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2756002Sroot ptyflush(); 2766002Sroot } 2776002Sroot cleanup(); 2786002Sroot } 2796002Sroot 2806002Sroot /* 2816002Sroot * State for recv fsm 2826002Sroot */ 2836002Sroot #define TS_DATA 0 /* base state */ 2846002Sroot #define TS_IAC 1 /* look for double IAC's */ 2856002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2866002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2876002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2886002Sroot #define TS_WILL 5 /* will option negotiation */ 2896002Sroot #define TS_WONT 6 /* wont " */ 2906002Sroot #define TS_DO 7 /* do " */ 2916002Sroot #define TS_DONT 8 /* dont " */ 2926002Sroot 2936002Sroot telrcv() 2946002Sroot { 2956002Sroot register int c; 2966002Sroot static int state = TS_DATA; 2976002Sroot struct sgttyb b; 2986002Sroot 2996002Sroot while (ncc > 0) { 3006002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 3016002Sroot return; 3026002Sroot c = *netip++ & 0377, ncc--; 3036002Sroot switch (state) { 3046002Sroot 3056002Sroot case TS_DATA: 3066002Sroot if (c == IAC) { 3076002Sroot state = TS_IAC; 3086002Sroot break; 3096002Sroot } 3106002Sroot if (inter > 0) 3116002Sroot break; 3126002Sroot *pfrontp++ = c; 3136002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 3146002Sroot state = TS_CR; 3156002Sroot break; 3166002Sroot 3176002Sroot case TS_CR: 3186002Sroot if (c && c != '\n') 3196002Sroot *pfrontp++ = c; 3206002Sroot state = TS_DATA; 3216002Sroot break; 3226002Sroot 3236002Sroot case TS_IAC: 3246002Sroot switch (c) { 3256002Sroot 3266002Sroot /* 3276002Sroot * Send the process on the pty side an 3286002Sroot * interrupt. Do this with a NULL or 3296002Sroot * interrupt char; depending on the tty mode. 3306002Sroot */ 3316002Sroot case BREAK: 3326002Sroot case IP: 3336002Sroot interrupt(); 3346002Sroot break; 3356002Sroot 3366002Sroot /* 3376002Sroot * Are You There? 3386002Sroot */ 3396002Sroot case AYT: 3406002Sroot *pfrontp++ = BELL; 3416002Sroot break; 3426002Sroot 3436002Sroot /* 3446002Sroot * Erase Character and 3456002Sroot * Erase Line 3466002Sroot */ 3476002Sroot case EC: 3486002Sroot case EL: 3496002Sroot ptyflush(); /* half-hearted */ 3506002Sroot ioctl(pty, TIOCGETP, &b); 3516002Sroot *pfrontp++ = (c == EC) ? 3526002Sroot b.sg_erase : b.sg_kill; 3536002Sroot break; 3546002Sroot 3556002Sroot /* 3566002Sroot * Check for urgent data... 3576002Sroot */ 3586002Sroot case DM: 3596002Sroot break; 3606002Sroot 3616002Sroot /* 3626002Sroot * Begin option subnegotiation... 3636002Sroot */ 3646002Sroot case SB: 3656002Sroot state = TS_BEGINNEG; 3666002Sroot continue; 3676002Sroot 3686002Sroot case WILL: 3696002Sroot case WONT: 3706002Sroot case DO: 3716002Sroot case DONT: 3726002Sroot state = TS_WILL + (c - WILL); 3736002Sroot continue; 3746002Sroot 3756002Sroot case IAC: 3766002Sroot *pfrontp++ = c; 3776002Sroot break; 3786002Sroot } 3796002Sroot state = TS_DATA; 3806002Sroot break; 3816002Sroot 3826002Sroot case TS_BEGINNEG: 3836002Sroot if (c == IAC) 3846002Sroot state = TS_ENDNEG; 3856002Sroot break; 3866002Sroot 3876002Sroot case TS_ENDNEG: 3886002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3896002Sroot break; 3906002Sroot 3916002Sroot case TS_WILL: 3926002Sroot if (!hisopts[c]) 3936002Sroot willoption(c); 3946002Sroot state = TS_DATA; 3956002Sroot continue; 3966002Sroot 3976002Sroot case TS_WONT: 3986002Sroot if (hisopts[c]) 3996002Sroot wontoption(c); 4006002Sroot state = TS_DATA; 4016002Sroot continue; 4026002Sroot 4036002Sroot case TS_DO: 4046002Sroot if (!myopts[c]) 4056002Sroot dooption(c); 4066002Sroot state = TS_DATA; 4076002Sroot continue; 4086002Sroot 4096002Sroot case TS_DONT: 4106002Sroot if (myopts[c]) { 4116002Sroot myopts[c] = 0; 4126002Sroot sprintf(nfrontp, wont, c); 4138379Ssam nfrontp += sizeof (wont) - 2; 4146002Sroot } 4156002Sroot state = TS_DATA; 4166002Sroot continue; 4176002Sroot 4186002Sroot default: 4199218Ssam printf("telnetd: panic state=%d\n", state); 4206002Sroot exit(1); 4216002Sroot } 4226002Sroot } 4236002Sroot } 4246002Sroot 4256002Sroot willoption(option) 4266002Sroot int option; 4276002Sroot { 4286002Sroot char *fmt; 4296002Sroot 4306002Sroot switch (option) { 4316002Sroot 4326002Sroot case TELOPT_BINARY: 4336002Sroot mode(RAW, 0); 4346002Sroot goto common; 4356002Sroot 4366002Sroot case TELOPT_ECHO: 4376002Sroot mode(0, ECHO|CRMOD); 4386002Sroot /*FALL THRU*/ 4396002Sroot 4406002Sroot case TELOPT_SGA: 4416002Sroot common: 4426002Sroot hisopts[option] = 1; 4436002Sroot fmt = doopt; 4446002Sroot break; 4456002Sroot 4466002Sroot case TELOPT_TM: 4476002Sroot fmt = dont; 4486002Sroot break; 4496002Sroot 4506002Sroot default: 4516002Sroot fmt = dont; 4526002Sroot break; 4536002Sroot } 4546023Ssam sprintf(nfrontp, fmt, option); 4558379Ssam nfrontp += sizeof (dont) - 2; 4566002Sroot } 4576002Sroot 4586002Sroot wontoption(option) 4596002Sroot int option; 4606002Sroot { 4616002Sroot char *fmt; 4626002Sroot 4636002Sroot switch (option) { 4646002Sroot 4656002Sroot case TELOPT_ECHO: 4666002Sroot mode(ECHO|CRMOD, 0); 4676002Sroot goto common; 4686002Sroot 4696002Sroot case TELOPT_BINARY: 4706002Sroot mode(0, RAW); 4716002Sroot /*FALL THRU*/ 4726002Sroot 4736002Sroot case TELOPT_SGA: 4746002Sroot common: 4756002Sroot hisopts[option] = 0; 4766002Sroot fmt = dont; 4776002Sroot break; 4786002Sroot 4796002Sroot default: 4806002Sroot fmt = dont; 4816002Sroot } 4826002Sroot sprintf(nfrontp, fmt, option); 4838379Ssam nfrontp += sizeof (doopt) - 2; 4846002Sroot } 4856002Sroot 4866002Sroot dooption(option) 4876002Sroot int option; 4886002Sroot { 4896002Sroot char *fmt; 4906002Sroot 4916002Sroot switch (option) { 4926002Sroot 4936002Sroot case TELOPT_TM: 4946002Sroot fmt = wont; 4956002Sroot break; 4966002Sroot 4976002Sroot case TELOPT_ECHO: 4986002Sroot mode(ECHO|CRMOD, 0); 4996002Sroot goto common; 5006002Sroot 5016002Sroot case TELOPT_BINARY: 5026002Sroot mode(RAW, 0); 5036002Sroot /*FALL THRU*/ 5046002Sroot 5056002Sroot case TELOPT_SGA: 5066002Sroot common: 5076002Sroot fmt = will; 5086002Sroot break; 5096002Sroot 5106002Sroot default: 5116002Sroot fmt = wont; 5126002Sroot break; 5136002Sroot } 5146002Sroot sprintf(nfrontp, fmt, option); 5158379Ssam nfrontp += sizeof (doopt) - 2; 5166002Sroot } 5176002Sroot 5186002Sroot mode(on, off) 5196002Sroot int on, off; 5206002Sroot { 5216002Sroot struct sgttyb b; 5226002Sroot 5236002Sroot ptyflush(); 5246002Sroot ioctl(pty, TIOCGETP, &b); 5256002Sroot b.sg_flags |= on; 5266002Sroot b.sg_flags &= ~off; 5276002Sroot ioctl(pty, TIOCSETP, &b); 5286002Sroot } 5296002Sroot 5306002Sroot /* 5316002Sroot * Send interrupt to process on other side of pty. 5326002Sroot * If it is in raw mode, just write NULL; 5336002Sroot * otherwise, write intr char. 5346002Sroot */ 5356002Sroot interrupt() 5366002Sroot { 5376002Sroot struct sgttyb b; 5386002Sroot struct tchars tchars; 5396002Sroot 5406002Sroot ptyflush(); /* half-hearted */ 5416002Sroot ioctl(pty, TIOCGETP, &b); 5426002Sroot if (b.sg_flags & RAW) { 5436002Sroot *pfrontp++ = '\0'; 5446002Sroot return; 5456002Sroot } 5466002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5476002Sroot '\177' : tchars.t_intrc; 5486002Sroot } 5496002Sroot 5506002Sroot ptyflush() 5516002Sroot { 5526002Sroot int n; 5536002Sroot 5546002Sroot if ((n = pfrontp - pbackp) > 0) 5556002Sroot n = write(pty, pbackp, n); 5568346Ssam if (n < 0) 5578346Ssam return; 5586002Sroot pbackp += n; 5596002Sroot if (pbackp == pfrontp) 5606002Sroot pbackp = pfrontp = ptyobuf; 5616002Sroot } 5626002Sroot 5636002Sroot netflush() 5646002Sroot { 5656002Sroot int n; 5666002Sroot 5676002Sroot if ((n = nfrontp - nbackp) > 0) 5686002Sroot n = write(net, nbackp, n); 5698346Ssam if (n < 0) { 5708346Ssam if (errno == EWOULDBLOCK) 5718346Ssam return; 5728346Ssam /* should blow this guy away... */ 5738346Ssam return; 5748346Ssam } 5756002Sroot nbackp += n; 5766002Sroot if (nbackp == nfrontp) 5776002Sroot nbackp = nfrontp = netobuf; 5786002Sroot } 5796002Sroot 5806002Sroot cleanup() 5816002Sroot { 5826002Sroot int how = 2; 5836002Sroot 5846002Sroot rmut(); 585*10008Ssam vhangup(); /* XXX */ 5866002Sroot ioctl(net, SIOCDONE, &how); 5876002Sroot kill(0, SIGKILL); 5886002Sroot exit(1); 5896002Sroot } 5906002Sroot 5916002Sroot #include <utmp.h> 5926002Sroot 5936002Sroot struct utmp wtmp; 5946002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5956002Sroot char utmp[] = "/etc/utmp"; 5968379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5978379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5986002Sroot 5996002Sroot rmut() 6006002Sroot { 6016002Sroot register f; 6026002Sroot int found = 0; 6036002Sroot 6046002Sroot f = open(utmp, 2); 6056002Sroot if (f >= 0) { 6068379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 6076002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 6086002Sroot continue; 6098379Ssam lseek(f, -(long)sizeof (wtmp), 1); 6106002Sroot SCPYN(wtmp.ut_name, ""); 6116002Sroot time(&wtmp.ut_time); 6128379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 6136002Sroot found++; 6146002Sroot } 6156002Sroot close(f); 6166002Sroot } 6176002Sroot if (found) { 6186002Sroot f = open(wtmpf, 1); 6196002Sroot if (f >= 0) { 6206002Sroot SCPYN(wtmp.ut_line, line+5); 6216002Sroot SCPYN(wtmp.ut_name, ""); 6226002Sroot time(&wtmp.ut_time); 6236002Sroot lseek(f, (long)0, 2); 6248379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 6256002Sroot close(f); 6266002Sroot } 6276002Sroot } 6286002Sroot chmod(line, 0666); 6296002Sroot chown(line, 0, 0); 6306002Sroot line[strlen("/dev/")] = 'p'; 6316002Sroot chmod(line, 0666); 6326002Sroot chown(line, 0, 0); 6336002Sroot } 634