16295Sroot #ifndef lint 2*17444Sralph static char sccsid[] = "@(#)telnetd.c 4.30 (Berkeley) 11/29/84"; 36295Sroot #endif 46295Sroot 56002Sroot /* 66002Sroot * Stripped-down telnet server. 76002Sroot */ 89218Ssam #include <sys/types.h> 99218Ssam #include <sys/socket.h> 1013608Ssam #include <sys/wait.h> 119218Ssam 129218Ssam #include <netinet/in.h> 139218Ssam 1412216Ssam #include <arpa/telnet.h> 1512216Ssam 166002Sroot #include <stdio.h> 176002Sroot #include <signal.h> 186002Sroot #include <errno.h> 196002Sroot #include <sgtty.h> 208346Ssam #include <netdb.h> 2117187Sralph #include <syslog.h> 229218Ssam 2313798Ssam #define BELL '\07' 2413798Ssam #define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s" 256002Sroot 266002Sroot char hisopts[256]; 276002Sroot char myopts[256]; 286002Sroot 296002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 306002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 316002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 326002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 336002Sroot 346002Sroot /* 356002Sroot * I/O data buffers, pointers, and counters. 366002Sroot */ 376002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 386002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 396002Sroot char netibuf[BUFSIZ], *netip = netibuf; 406388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 416002Sroot int pcc, ncc; 426002Sroot 436002Sroot int pty, net; 446002Sroot int inter; 4513799Ssam extern char **environ; 466002Sroot extern int errno; 476002Sroot char line[] = "/dev/ptyp0"; 486002Sroot 496002Sroot main(argc, argv) 506002Sroot char *argv[]; 516002Sroot { 5216371Skarels struct sockaddr_in from; 5317156Ssam int on = 1, fromlen; 546002Sroot 5516371Skarels fromlen = sizeof (from); 5616371Skarels if (getpeername(0, &from, &fromlen) < 0) { 5716371Skarels fprintf(stderr, "%s: ", argv[0]); 5816371Skarels perror("getpeername"); 5916371Skarels _exit(1); 608346Ssam } 6117156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 6217187Sralph openlog(argv[0], LOG_PID, 0); 6317187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 6410418Ssam } 6516371Skarels doit(0, &from); 666002Sroot } 676002Sroot 6813799Ssam char *envinit[] = { "TERM=network", 0 }; 696002Sroot int cleanup(); 706002Sroot 716002Sroot /* 726002Sroot * Get a pty, scan input lines. 736002Sroot */ 7412683Ssam doit(f, who) 7512683Ssam int f; 7612683Ssam struct sockaddr_in *who; 776002Sroot { 78*17444Sralph char *cp = line, *host, *inet_ntoa(); 796002Sroot int i, p, cc, t; 806002Sroot struct sgttyb b; 8112683Ssam struct hostent *hp; 826002Sroot 836002Sroot for (i = 0; i < 16; i++) { 846002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 856002Sroot p = open(cp, 2); 866002Sroot if (p > 0) 876002Sroot goto gotpty; 886002Sroot } 899244Ssam fatal(f, "All network ports in use"); 909244Ssam /*NOTREACHED*/ 916002Sroot gotpty: 926002Sroot dup2(f, 0); 936002Sroot cp[strlen("/dev/")] = 't'; 946002Sroot t = open("/dev/tty", 2); 956002Sroot if (t >= 0) { 966002Sroot ioctl(t, TIOCNOTTY, 0); 976002Sroot close(t); 986002Sroot } 996002Sroot t = open(cp, 2); 1009244Ssam if (t < 0) 1019244Ssam fatalperror(f, cp, errno); 1026002Sroot ioctl(t, TIOCGETP, &b); 1036388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1046002Sroot ioctl(t, TIOCSETP, &b); 1056388Ssam ioctl(p, TIOCGETP, &b); 1068379Ssam b.sg_flags &= ~ECHO; 1076388Ssam ioctl(p, TIOCSETP, &b); 10812683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 10912683Ssam who->sin_family); 11012683Ssam if (hp) 11112683Ssam host = hp->h_name; 11212683Ssam else 113*17444Sralph host = inet_ntoa(who->sin_addr); 1149244Ssam if ((i = fork()) < 0) 1159244Ssam fatalperror(f, "fork", errno); 1166002Sroot if (i) 1176002Sroot telnet(f, p); 1186002Sroot close(f); 1196002Sroot close(p); 1206002Sroot dup2(t, 0); 1216002Sroot dup2(t, 1); 1226002Sroot dup2(t, 2); 1236002Sroot close(t); 12413799Ssam environ = envinit; 12512713Ssam execl("/bin/login", "login", "-h", host, 0); 1269244Ssam fatalperror(f, "/bin/login", errno); 1279244Ssam /*NOTREACHED*/ 1289244Ssam } 1299244Ssam 1309244Ssam fatal(f, msg) 1319244Ssam int f; 1329244Ssam char *msg; 1339244Ssam { 1349244Ssam char buf[BUFSIZ]; 1359244Ssam 1369244Ssam (void) sprintf(buf, "telnetd: %s.\n", msg); 1379244Ssam (void) write(f, buf, strlen(buf)); 1386002Sroot exit(1); 1396002Sroot } 1406002Sroot 1419244Ssam fatalperror(f, msg, errno) 1429244Ssam int f; 1439244Ssam char *msg; 1449244Ssam int errno; 1459244Ssam { 1469244Ssam char buf[BUFSIZ]; 1479244Ssam extern char *sys_errlist[]; 1489244Ssam 1499244Ssam (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 1509244Ssam fatal(f, buf); 1519244Ssam } 1529244Ssam 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; 16012713Ssam char hostname[32]; 1616002Sroot 1626002Sroot net = f, pty = p; 1636002Sroot ioctl(f, FIONBIO, &on); 1646002Sroot ioctl(p, FIONBIO, &on); 1656002Sroot signal(SIGTSTP, SIG_IGN); 16613028Ssam signal(SIGCHLD, cleanup); 1676002Sroot 1688379Ssam /* 1698379Ssam * Request to do remote echo. 1708379Ssam */ 1718379Ssam dooption(TELOPT_ECHO); 1728379Ssam myopts[TELOPT_ECHO] = 1; 17312713Ssam /* 17412713Ssam * Show banner that getty never gave. 17512713Ssam */ 17612713Ssam gethostname(hostname, sizeof (hostname)); 17712713Ssam sprintf(nfrontp, BANNER, hostname, ""); 17812713Ssam nfrontp += strlen(nfrontp); 1796002Sroot for (;;) { 1806002Sroot int ibits = 0, obits = 0; 1816002Sroot register int c; 1826002Sroot 1836002Sroot /* 1846002Sroot * Never look for input if there's still 1856002Sroot * stuff in the corresponding output buffer 1866002Sroot */ 1876002Sroot if (nfrontp - nbackp) 1886002Sroot obits |= (1 << f); 1896002Sroot else 1906002Sroot ibits |= (1 << p); 1916002Sroot if (pfrontp - pbackp) 1926002Sroot obits |= (1 << p); 1936002Sroot else 1946002Sroot ibits |= (1 << f); 1956002Sroot if (ncc < 0 && pcc < 0) 1966002Sroot break; 1979218Ssam select(16, &ibits, &obits, 0, 0); 1986002Sroot if (ibits == 0 && obits == 0) { 1996002Sroot sleep(5); 2006002Sroot continue; 2016002Sroot } 2026002Sroot 2036002Sroot /* 2046002Sroot * Something to read from the network... 2056002Sroot */ 2066002Sroot if (ibits & (1 << f)) { 2076002Sroot ncc = read(f, netibuf, BUFSIZ); 2086002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2096002Sroot ncc = 0; 2106002Sroot else { 2116002Sroot if (ncc <= 0) 2126002Sroot break; 2136002Sroot netip = netibuf; 2146002Sroot } 2156002Sroot } 2166002Sroot 2176002Sroot /* 2186002Sroot * Something to read from the pty... 2196002Sroot */ 2206002Sroot if (ibits & (1 << p)) { 2216002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2226002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2236002Sroot pcc = 0; 2246002Sroot else { 2256002Sroot if (pcc <= 0) 2266002Sroot break; 2276002Sroot ptyip = ptyibuf; 2286002Sroot } 2296002Sroot } 2306002Sroot 2316002Sroot while (pcc > 0) { 2326002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2336002Sroot break; 2346002Sroot c = *ptyip++ & 0377, pcc--; 2356002Sroot if (c == IAC) 2366002Sroot *nfrontp++ = c; 2376002Sroot *nfrontp++ = c; 2386002Sroot } 2396002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2406002Sroot netflush(); 2416002Sroot if (ncc > 0) 2426002Sroot telrcv(); 2436002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2446002Sroot ptyflush(); 2456002Sroot } 2466002Sroot cleanup(); 2476002Sroot } 2486002Sroot 2496002Sroot /* 2506002Sroot * State for recv fsm 2516002Sroot */ 2526002Sroot #define TS_DATA 0 /* base state */ 2536002Sroot #define TS_IAC 1 /* look for double IAC's */ 2546002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2556002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2566002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2576002Sroot #define TS_WILL 5 /* will option negotiation */ 2586002Sroot #define TS_WONT 6 /* wont " */ 2596002Sroot #define TS_DO 7 /* do " */ 2606002Sroot #define TS_DONT 8 /* dont " */ 2616002Sroot 2626002Sroot telrcv() 2636002Sroot { 2646002Sroot register int c; 2656002Sroot static int state = TS_DATA; 2666002Sroot struct sgttyb b; 2676002Sroot 2686002Sroot while (ncc > 0) { 2696002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2706002Sroot return; 2716002Sroot c = *netip++ & 0377, ncc--; 2726002Sroot switch (state) { 2736002Sroot 2746002Sroot case TS_DATA: 2756002Sroot if (c == IAC) { 2766002Sroot state = TS_IAC; 2776002Sroot break; 2786002Sroot } 2796002Sroot if (inter > 0) 2806002Sroot break; 2816002Sroot *pfrontp++ = c; 2826002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2836002Sroot state = TS_CR; 2846002Sroot break; 2856002Sroot 2866002Sroot case TS_CR: 2876002Sroot if (c && c != '\n') 2886002Sroot *pfrontp++ = c; 2896002Sroot state = TS_DATA; 2906002Sroot break; 2916002Sroot 2926002Sroot case TS_IAC: 2936002Sroot switch (c) { 2946002Sroot 2956002Sroot /* 2966002Sroot * Send the process on the pty side an 2976002Sroot * interrupt. Do this with a NULL or 2986002Sroot * interrupt char; depending on the tty mode. 2996002Sroot */ 3006002Sroot case BREAK: 3016002Sroot case IP: 3026002Sroot interrupt(); 3036002Sroot break; 3046002Sroot 3056002Sroot /* 3066002Sroot * Are You There? 3076002Sroot */ 3086002Sroot case AYT: 3096002Sroot *pfrontp++ = BELL; 3106002Sroot break; 3116002Sroot 3126002Sroot /* 3136002Sroot * Erase Character and 3146002Sroot * Erase Line 3156002Sroot */ 3166002Sroot case EC: 3176002Sroot case EL: 3186002Sroot ptyflush(); /* half-hearted */ 3196002Sroot ioctl(pty, TIOCGETP, &b); 3206002Sroot *pfrontp++ = (c == EC) ? 3216002Sroot b.sg_erase : b.sg_kill; 3226002Sroot break; 3236002Sroot 3246002Sroot /* 3256002Sroot * Check for urgent data... 3266002Sroot */ 3276002Sroot case DM: 3286002Sroot break; 3296002Sroot 3306002Sroot /* 3316002Sroot * Begin option subnegotiation... 3326002Sroot */ 3336002Sroot case SB: 3346002Sroot state = TS_BEGINNEG; 3356002Sroot continue; 3366002Sroot 3376002Sroot case WILL: 3386002Sroot case WONT: 3396002Sroot case DO: 3406002Sroot case DONT: 3416002Sroot state = TS_WILL + (c - WILL); 3426002Sroot continue; 3436002Sroot 3446002Sroot case IAC: 3456002Sroot *pfrontp++ = c; 3466002Sroot break; 3476002Sroot } 3486002Sroot state = TS_DATA; 3496002Sroot break; 3506002Sroot 3516002Sroot case TS_BEGINNEG: 3526002Sroot if (c == IAC) 3536002Sroot state = TS_ENDNEG; 3546002Sroot break; 3556002Sroot 3566002Sroot case TS_ENDNEG: 3576002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3586002Sroot break; 3596002Sroot 3606002Sroot case TS_WILL: 3616002Sroot if (!hisopts[c]) 3626002Sroot willoption(c); 3636002Sroot state = TS_DATA; 3646002Sroot continue; 3656002Sroot 3666002Sroot case TS_WONT: 3676002Sroot if (hisopts[c]) 3686002Sroot wontoption(c); 3696002Sroot state = TS_DATA; 3706002Sroot continue; 3716002Sroot 3726002Sroot case TS_DO: 3736002Sroot if (!myopts[c]) 3746002Sroot dooption(c); 3756002Sroot state = TS_DATA; 3766002Sroot continue; 3776002Sroot 3786002Sroot case TS_DONT: 3796002Sroot if (myopts[c]) { 3806002Sroot myopts[c] = 0; 3816002Sroot sprintf(nfrontp, wont, c); 3828379Ssam nfrontp += sizeof (wont) - 2; 3836002Sroot } 3846002Sroot state = TS_DATA; 3856002Sroot continue; 3866002Sroot 3876002Sroot default: 3889218Ssam printf("telnetd: panic state=%d\n", state); 3896002Sroot exit(1); 3906002Sroot } 3916002Sroot } 3926002Sroot } 3936002Sroot 3946002Sroot willoption(option) 3956002Sroot int option; 3966002Sroot { 3976002Sroot char *fmt; 3986002Sroot 3996002Sroot switch (option) { 4006002Sroot 4016002Sroot case TELOPT_BINARY: 4026002Sroot mode(RAW, 0); 4036002Sroot goto common; 4046002Sroot 4056002Sroot case TELOPT_ECHO: 4066002Sroot mode(0, ECHO|CRMOD); 4076002Sroot /*FALL THRU*/ 4086002Sroot 4096002Sroot case TELOPT_SGA: 4106002Sroot common: 4116002Sroot hisopts[option] = 1; 4126002Sroot fmt = doopt; 4136002Sroot break; 4146002Sroot 4156002Sroot case TELOPT_TM: 4166002Sroot fmt = dont; 4176002Sroot break; 4186002Sroot 4196002Sroot default: 4206002Sroot fmt = dont; 4216002Sroot break; 4226002Sroot } 4236023Ssam sprintf(nfrontp, fmt, option); 4248379Ssam nfrontp += sizeof (dont) - 2; 4256002Sroot } 4266002Sroot 4276002Sroot wontoption(option) 4286002Sroot int option; 4296002Sroot { 4306002Sroot char *fmt; 4316002Sroot 4326002Sroot switch (option) { 4336002Sroot 4346002Sroot case TELOPT_ECHO: 4356002Sroot mode(ECHO|CRMOD, 0); 4366002Sroot goto common; 4376002Sroot 4386002Sroot case TELOPT_BINARY: 4396002Sroot mode(0, RAW); 4406002Sroot /*FALL THRU*/ 4416002Sroot 4426002Sroot case TELOPT_SGA: 4436002Sroot common: 4446002Sroot hisopts[option] = 0; 4456002Sroot fmt = dont; 4466002Sroot break; 4476002Sroot 4486002Sroot default: 4496002Sroot fmt = dont; 4506002Sroot } 4516002Sroot sprintf(nfrontp, fmt, option); 4528379Ssam nfrontp += sizeof (doopt) - 2; 4536002Sroot } 4546002Sroot 4556002Sroot dooption(option) 4566002Sroot int option; 4576002Sroot { 4586002Sroot char *fmt; 4596002Sroot 4606002Sroot switch (option) { 4616002Sroot 4626002Sroot case TELOPT_TM: 4636002Sroot fmt = wont; 4646002Sroot break; 4656002Sroot 4666002Sroot case TELOPT_ECHO: 4676002Sroot mode(ECHO|CRMOD, 0); 4686002Sroot goto common; 4696002Sroot 4706002Sroot case TELOPT_BINARY: 4716002Sroot mode(RAW, 0); 4726002Sroot /*FALL THRU*/ 4736002Sroot 4746002Sroot case TELOPT_SGA: 4756002Sroot common: 4766002Sroot fmt = will; 4776002Sroot break; 4786002Sroot 4796002Sroot default: 4806002Sroot fmt = wont; 4816002Sroot break; 4826002Sroot } 4836002Sroot sprintf(nfrontp, fmt, option); 4848379Ssam nfrontp += sizeof (doopt) - 2; 4856002Sroot } 4866002Sroot 4876002Sroot mode(on, off) 4886002Sroot int on, off; 4896002Sroot { 4906002Sroot struct sgttyb b; 4916002Sroot 4926002Sroot ptyflush(); 4936002Sroot ioctl(pty, TIOCGETP, &b); 4946002Sroot b.sg_flags |= on; 4956002Sroot b.sg_flags &= ~off; 4966002Sroot ioctl(pty, TIOCSETP, &b); 4976002Sroot } 4986002Sroot 4996002Sroot /* 5006002Sroot * Send interrupt to process on other side of pty. 5016002Sroot * If it is in raw mode, just write NULL; 5026002Sroot * otherwise, write intr char. 5036002Sroot */ 5046002Sroot interrupt() 5056002Sroot { 5066002Sroot struct sgttyb b; 5076002Sroot struct tchars tchars; 5086002Sroot 5096002Sroot ptyflush(); /* half-hearted */ 5106002Sroot ioctl(pty, TIOCGETP, &b); 5116002Sroot if (b.sg_flags & RAW) { 5126002Sroot *pfrontp++ = '\0'; 5136002Sroot return; 5146002Sroot } 5156002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5166002Sroot '\177' : tchars.t_intrc; 5176002Sroot } 5186002Sroot 5196002Sroot ptyflush() 5206002Sroot { 5216002Sroot int n; 5226002Sroot 5236002Sroot if ((n = pfrontp - pbackp) > 0) 5246002Sroot n = write(pty, pbackp, n); 5258346Ssam if (n < 0) 5268346Ssam return; 5276002Sroot pbackp += n; 5286002Sroot if (pbackp == pfrontp) 5296002Sroot pbackp = pfrontp = ptyobuf; 5306002Sroot } 5316002Sroot 5326002Sroot netflush() 5336002Sroot { 5346002Sroot int n; 5356002Sroot 5366002Sroot if ((n = nfrontp - nbackp) > 0) 5376002Sroot n = write(net, nbackp, n); 5388346Ssam if (n < 0) { 5398346Ssam if (errno == EWOULDBLOCK) 5408346Ssam return; 5418346Ssam /* should blow this guy away... */ 5428346Ssam return; 5438346Ssam } 5446002Sroot nbackp += n; 5456002Sroot if (nbackp == nfrontp) 5466002Sroot nbackp = nfrontp = netobuf; 5476002Sroot } 5486002Sroot 5496002Sroot cleanup() 5506002Sroot { 5516002Sroot 5526002Sroot rmut(); 55310008Ssam vhangup(); /* XXX */ 55410191Ssam shutdown(net, 2); 5556002Sroot kill(0, SIGKILL); 5566002Sroot exit(1); 5576002Sroot } 5586002Sroot 5596002Sroot #include <utmp.h> 5606002Sroot 5616002Sroot struct utmp wtmp; 5626002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5636002Sroot char utmp[] = "/etc/utmp"; 5648379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5658379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5666002Sroot 5676002Sroot rmut() 5686002Sroot { 5696002Sroot register f; 5706002Sroot int found = 0; 5716002Sroot 5726002Sroot f = open(utmp, 2); 5736002Sroot if (f >= 0) { 5748379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5756002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5766002Sroot continue; 5778379Ssam lseek(f, -(long)sizeof (wtmp), 1); 5786002Sroot SCPYN(wtmp.ut_name, ""); 57912683Ssam SCPYN(wtmp.ut_host, ""); 5806002Sroot time(&wtmp.ut_time); 5818379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5826002Sroot found++; 5836002Sroot } 5846002Sroot close(f); 5856002Sroot } 5866002Sroot if (found) { 5876002Sroot f = open(wtmpf, 1); 5886002Sroot if (f >= 0) { 5896002Sroot SCPYN(wtmp.ut_line, line+5); 5906002Sroot SCPYN(wtmp.ut_name, ""); 59112683Ssam SCPYN(wtmp.ut_host, ""); 5926002Sroot time(&wtmp.ut_time); 5936002Sroot lseek(f, (long)0, 2); 5948379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5956002Sroot close(f); 5966002Sroot } 5976002Sroot } 5986002Sroot chmod(line, 0666); 5996002Sroot chown(line, 0, 0); 6006002Sroot line[strlen("/dev/")] = 'p'; 6016002Sroot chmod(line, 0666); 6026002Sroot chown(line, 0, 0); 6036002Sroot } 604