16295Sroot #ifndef lint 2*17156Ssam static char sccsid[] = "@(#)telnetd.c 4.28 (Berkeley) 09/04/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> 219218Ssam 2213798Ssam #define BELL '\07' 2313798Ssam #define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s" 246002Sroot 256002Sroot char hisopts[256]; 266002Sroot char myopts[256]; 276002Sroot 286002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 296002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 306002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 316002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 326002Sroot 336002Sroot /* 346002Sroot * I/O data buffers, pointers, and counters. 356002Sroot */ 366002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 376002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 386002Sroot char netibuf[BUFSIZ], *netip = netibuf; 396388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 406002Sroot int pcc, ncc; 416002Sroot 426002Sroot int pty, net; 436002Sroot int inter; 4413799Ssam extern char **environ; 456002Sroot extern int errno; 466002Sroot char line[] = "/dev/ptyp0"; 476002Sroot 486002Sroot main(argc, argv) 496002Sroot char *argv[]; 506002Sroot { 5116371Skarels struct sockaddr_in from; 52*17156Ssam int on = 1, fromlen; 536002Sroot 5416371Skarels fromlen = sizeof (from); 5516371Skarels if (getpeername(0, &from, &fromlen) < 0) { 5616371Skarels fprintf(stderr, "%s: ", argv[0]); 5716371Skarels perror("getpeername"); 5816371Skarels _exit(1); 598346Ssam } 60*17156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 6116371Skarels fprintf(stderr, "%s: ", argv[0]); 6216371Skarels perror("setsockopt (SO_KEEPALIVE)"); 6310418Ssam } 6416371Skarels doit(0, &from); 656002Sroot } 666002Sroot 6713799Ssam char *envinit[] = { "TERM=network", 0 }; 686002Sroot int cleanup(); 696002Sroot 706002Sroot /* 716002Sroot * Get a pty, scan input lines. 726002Sroot */ 7312683Ssam doit(f, who) 7412683Ssam int f; 7512683Ssam struct sockaddr_in *who; 766002Sroot { 7712683Ssam char *cp = line, *host, *ntoa(); 786002Sroot int i, p, cc, t; 796002Sroot struct sgttyb b; 8012683Ssam struct hostent *hp; 816002Sroot 826002Sroot for (i = 0; i < 16; i++) { 836002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 846002Sroot p = open(cp, 2); 856002Sroot if (p > 0) 866002Sroot goto gotpty; 876002Sroot } 889244Ssam fatal(f, "All network ports in use"); 899244Ssam /*NOTREACHED*/ 906002Sroot gotpty: 916002Sroot dup2(f, 0); 926002Sroot cp[strlen("/dev/")] = 't'; 936002Sroot t = open("/dev/tty", 2); 946002Sroot if (t >= 0) { 956002Sroot ioctl(t, TIOCNOTTY, 0); 966002Sroot close(t); 976002Sroot } 986002Sroot t = open(cp, 2); 999244Ssam if (t < 0) 1009244Ssam fatalperror(f, cp, errno); 1016002Sroot ioctl(t, TIOCGETP, &b); 1026388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1036002Sroot ioctl(t, TIOCSETP, &b); 1046388Ssam ioctl(p, TIOCGETP, &b); 1058379Ssam b.sg_flags &= ~ECHO; 1066388Ssam ioctl(p, TIOCSETP, &b); 10712683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 10812683Ssam who->sin_family); 10912683Ssam if (hp) 11012683Ssam host = hp->h_name; 11112683Ssam else 11212683Ssam host = ntoa(who->sin_addr); 1139244Ssam if ((i = fork()) < 0) 1149244Ssam fatalperror(f, "fork", errno); 1156002Sroot if (i) 1166002Sroot telnet(f, p); 1176002Sroot close(f); 1186002Sroot close(p); 1196002Sroot dup2(t, 0); 1206002Sroot dup2(t, 1); 1216002Sroot dup2(t, 2); 1226002Sroot close(t); 12313799Ssam environ = envinit; 12412713Ssam execl("/bin/login", "login", "-h", host, 0); 1259244Ssam fatalperror(f, "/bin/login", errno); 1269244Ssam /*NOTREACHED*/ 1279244Ssam } 1289244Ssam 1299244Ssam fatal(f, msg) 1309244Ssam int f; 1319244Ssam char *msg; 1329244Ssam { 1339244Ssam char buf[BUFSIZ]; 1349244Ssam 1359244Ssam (void) sprintf(buf, "telnetd: %s.\n", msg); 1369244Ssam (void) write(f, buf, strlen(buf)); 1376002Sroot exit(1); 1386002Sroot } 1396002Sroot 1409244Ssam fatalperror(f, msg, errno) 1419244Ssam int f; 1429244Ssam char *msg; 1439244Ssam int errno; 1449244Ssam { 1459244Ssam char buf[BUFSIZ]; 1469244Ssam extern char *sys_errlist[]; 1479244Ssam 1489244Ssam (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 1499244Ssam fatal(f, buf); 1509244Ssam } 1519244Ssam 1526002Sroot /* 1536002Sroot * Main loop. Select from pty and network, and 1546002Sroot * hand data to telnet receiver finite state machine. 1556002Sroot */ 1566002Sroot telnet(f, p) 1576002Sroot { 1586002Sroot int on = 1; 15912713Ssam char hostname[32]; 1606002Sroot 1616002Sroot net = f, pty = p; 1626002Sroot ioctl(f, FIONBIO, &on); 1636002Sroot ioctl(p, FIONBIO, &on); 1646002Sroot signal(SIGTSTP, SIG_IGN); 16513028Ssam signal(SIGCHLD, cleanup); 1666002Sroot 1678379Ssam /* 1688379Ssam * Request to do remote echo. 1698379Ssam */ 1708379Ssam dooption(TELOPT_ECHO); 1718379Ssam myopts[TELOPT_ECHO] = 1; 17212713Ssam /* 17312713Ssam * Show banner that getty never gave. 17412713Ssam */ 17512713Ssam gethostname(hostname, sizeof (hostname)); 17612713Ssam sprintf(nfrontp, BANNER, hostname, ""); 17712713Ssam nfrontp += strlen(nfrontp); 1786002Sroot for (;;) { 1796002Sroot int ibits = 0, obits = 0; 1806002Sroot register int c; 1816002Sroot 1826002Sroot /* 1836002Sroot * Never look for input if there's still 1846002Sroot * stuff in the corresponding output buffer 1856002Sroot */ 1866002Sroot if (nfrontp - nbackp) 1876002Sroot obits |= (1 << f); 1886002Sroot else 1896002Sroot ibits |= (1 << p); 1906002Sroot if (pfrontp - pbackp) 1916002Sroot obits |= (1 << p); 1926002Sroot else 1936002Sroot ibits |= (1 << f); 1946002Sroot if (ncc < 0 && pcc < 0) 1956002Sroot break; 1969218Ssam select(16, &ibits, &obits, 0, 0); 1976002Sroot if (ibits == 0 && obits == 0) { 1986002Sroot sleep(5); 1996002Sroot continue; 2006002Sroot } 2016002Sroot 2026002Sroot /* 2036002Sroot * Something to read from the network... 2046002Sroot */ 2056002Sroot if (ibits & (1 << f)) { 2066002Sroot ncc = read(f, netibuf, BUFSIZ); 2076002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2086002Sroot ncc = 0; 2096002Sroot else { 2106002Sroot if (ncc <= 0) 2116002Sroot break; 2126002Sroot netip = netibuf; 2136002Sroot } 2146002Sroot } 2156002Sroot 2166002Sroot /* 2176002Sroot * Something to read from the pty... 2186002Sroot */ 2196002Sroot if (ibits & (1 << p)) { 2206002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2216002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2226002Sroot pcc = 0; 2236002Sroot else { 2246002Sroot if (pcc <= 0) 2256002Sroot break; 2266002Sroot ptyip = ptyibuf; 2276002Sroot } 2286002Sroot } 2296002Sroot 2306002Sroot while (pcc > 0) { 2316002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2326002Sroot break; 2336002Sroot c = *ptyip++ & 0377, pcc--; 2346002Sroot if (c == IAC) 2356002Sroot *nfrontp++ = c; 2366002Sroot *nfrontp++ = c; 2376002Sroot } 2386002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2396002Sroot netflush(); 2406002Sroot if (ncc > 0) 2416002Sroot telrcv(); 2426002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2436002Sroot ptyflush(); 2446002Sroot } 2456002Sroot cleanup(); 2466002Sroot } 2476002Sroot 2486002Sroot /* 2496002Sroot * State for recv fsm 2506002Sroot */ 2516002Sroot #define TS_DATA 0 /* base state */ 2526002Sroot #define TS_IAC 1 /* look for double IAC's */ 2536002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2546002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2556002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2566002Sroot #define TS_WILL 5 /* will option negotiation */ 2576002Sroot #define TS_WONT 6 /* wont " */ 2586002Sroot #define TS_DO 7 /* do " */ 2596002Sroot #define TS_DONT 8 /* dont " */ 2606002Sroot 2616002Sroot telrcv() 2626002Sroot { 2636002Sroot register int c; 2646002Sroot static int state = TS_DATA; 2656002Sroot struct sgttyb b; 2666002Sroot 2676002Sroot while (ncc > 0) { 2686002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2696002Sroot return; 2706002Sroot c = *netip++ & 0377, ncc--; 2716002Sroot switch (state) { 2726002Sroot 2736002Sroot case TS_DATA: 2746002Sroot if (c == IAC) { 2756002Sroot state = TS_IAC; 2766002Sroot break; 2776002Sroot } 2786002Sroot if (inter > 0) 2796002Sroot break; 2806002Sroot *pfrontp++ = c; 2816002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2826002Sroot state = TS_CR; 2836002Sroot break; 2846002Sroot 2856002Sroot case TS_CR: 2866002Sroot if (c && c != '\n') 2876002Sroot *pfrontp++ = c; 2886002Sroot state = TS_DATA; 2896002Sroot break; 2906002Sroot 2916002Sroot case TS_IAC: 2926002Sroot switch (c) { 2936002Sroot 2946002Sroot /* 2956002Sroot * Send the process on the pty side an 2966002Sroot * interrupt. Do this with a NULL or 2976002Sroot * interrupt char; depending on the tty mode. 2986002Sroot */ 2996002Sroot case BREAK: 3006002Sroot case IP: 3016002Sroot interrupt(); 3026002Sroot break; 3036002Sroot 3046002Sroot /* 3056002Sroot * Are You There? 3066002Sroot */ 3076002Sroot case AYT: 3086002Sroot *pfrontp++ = BELL; 3096002Sroot break; 3106002Sroot 3116002Sroot /* 3126002Sroot * Erase Character and 3136002Sroot * Erase Line 3146002Sroot */ 3156002Sroot case EC: 3166002Sroot case EL: 3176002Sroot ptyflush(); /* half-hearted */ 3186002Sroot ioctl(pty, TIOCGETP, &b); 3196002Sroot *pfrontp++ = (c == EC) ? 3206002Sroot b.sg_erase : b.sg_kill; 3216002Sroot break; 3226002Sroot 3236002Sroot /* 3246002Sroot * Check for urgent data... 3256002Sroot */ 3266002Sroot case DM: 3276002Sroot break; 3286002Sroot 3296002Sroot /* 3306002Sroot * Begin option subnegotiation... 3316002Sroot */ 3326002Sroot case SB: 3336002Sroot state = TS_BEGINNEG; 3346002Sroot continue; 3356002Sroot 3366002Sroot case WILL: 3376002Sroot case WONT: 3386002Sroot case DO: 3396002Sroot case DONT: 3406002Sroot state = TS_WILL + (c - WILL); 3416002Sroot continue; 3426002Sroot 3436002Sroot case IAC: 3446002Sroot *pfrontp++ = c; 3456002Sroot break; 3466002Sroot } 3476002Sroot state = TS_DATA; 3486002Sroot break; 3496002Sroot 3506002Sroot case TS_BEGINNEG: 3516002Sroot if (c == IAC) 3526002Sroot state = TS_ENDNEG; 3536002Sroot break; 3546002Sroot 3556002Sroot case TS_ENDNEG: 3566002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3576002Sroot break; 3586002Sroot 3596002Sroot case TS_WILL: 3606002Sroot if (!hisopts[c]) 3616002Sroot willoption(c); 3626002Sroot state = TS_DATA; 3636002Sroot continue; 3646002Sroot 3656002Sroot case TS_WONT: 3666002Sroot if (hisopts[c]) 3676002Sroot wontoption(c); 3686002Sroot state = TS_DATA; 3696002Sroot continue; 3706002Sroot 3716002Sroot case TS_DO: 3726002Sroot if (!myopts[c]) 3736002Sroot dooption(c); 3746002Sroot state = TS_DATA; 3756002Sroot continue; 3766002Sroot 3776002Sroot case TS_DONT: 3786002Sroot if (myopts[c]) { 3796002Sroot myopts[c] = 0; 3806002Sroot sprintf(nfrontp, wont, c); 3818379Ssam nfrontp += sizeof (wont) - 2; 3826002Sroot } 3836002Sroot state = TS_DATA; 3846002Sroot continue; 3856002Sroot 3866002Sroot default: 3879218Ssam printf("telnetd: panic state=%d\n", state); 3886002Sroot exit(1); 3896002Sroot } 3906002Sroot } 3916002Sroot } 3926002Sroot 3936002Sroot willoption(option) 3946002Sroot int option; 3956002Sroot { 3966002Sroot char *fmt; 3976002Sroot 3986002Sroot switch (option) { 3996002Sroot 4006002Sroot case TELOPT_BINARY: 4016002Sroot mode(RAW, 0); 4026002Sroot goto common; 4036002Sroot 4046002Sroot case TELOPT_ECHO: 4056002Sroot mode(0, ECHO|CRMOD); 4066002Sroot /*FALL THRU*/ 4076002Sroot 4086002Sroot case TELOPT_SGA: 4096002Sroot common: 4106002Sroot hisopts[option] = 1; 4116002Sroot fmt = doopt; 4126002Sroot break; 4136002Sroot 4146002Sroot case TELOPT_TM: 4156002Sroot fmt = dont; 4166002Sroot break; 4176002Sroot 4186002Sroot default: 4196002Sroot fmt = dont; 4206002Sroot break; 4216002Sroot } 4226023Ssam sprintf(nfrontp, fmt, option); 4238379Ssam nfrontp += sizeof (dont) - 2; 4246002Sroot } 4256002Sroot 4266002Sroot wontoption(option) 4276002Sroot int option; 4286002Sroot { 4296002Sroot char *fmt; 4306002Sroot 4316002Sroot switch (option) { 4326002Sroot 4336002Sroot case TELOPT_ECHO: 4346002Sroot mode(ECHO|CRMOD, 0); 4356002Sroot goto common; 4366002Sroot 4376002Sroot case TELOPT_BINARY: 4386002Sroot mode(0, RAW); 4396002Sroot /*FALL THRU*/ 4406002Sroot 4416002Sroot case TELOPT_SGA: 4426002Sroot common: 4436002Sroot hisopts[option] = 0; 4446002Sroot fmt = dont; 4456002Sroot break; 4466002Sroot 4476002Sroot default: 4486002Sroot fmt = dont; 4496002Sroot } 4506002Sroot sprintf(nfrontp, fmt, option); 4518379Ssam nfrontp += sizeof (doopt) - 2; 4526002Sroot } 4536002Sroot 4546002Sroot dooption(option) 4556002Sroot int option; 4566002Sroot { 4576002Sroot char *fmt; 4586002Sroot 4596002Sroot switch (option) { 4606002Sroot 4616002Sroot case TELOPT_TM: 4626002Sroot fmt = wont; 4636002Sroot break; 4646002Sroot 4656002Sroot case TELOPT_ECHO: 4666002Sroot mode(ECHO|CRMOD, 0); 4676002Sroot goto common; 4686002Sroot 4696002Sroot case TELOPT_BINARY: 4706002Sroot mode(RAW, 0); 4716002Sroot /*FALL THRU*/ 4726002Sroot 4736002Sroot case TELOPT_SGA: 4746002Sroot common: 4756002Sroot fmt = will; 4766002Sroot break; 4776002Sroot 4786002Sroot default: 4796002Sroot fmt = wont; 4806002Sroot break; 4816002Sroot } 4826002Sroot sprintf(nfrontp, fmt, option); 4838379Ssam nfrontp += sizeof (doopt) - 2; 4846002Sroot } 4856002Sroot 4866002Sroot mode(on, off) 4876002Sroot int on, off; 4886002Sroot { 4896002Sroot struct sgttyb b; 4906002Sroot 4916002Sroot ptyflush(); 4926002Sroot ioctl(pty, TIOCGETP, &b); 4936002Sroot b.sg_flags |= on; 4946002Sroot b.sg_flags &= ~off; 4956002Sroot ioctl(pty, TIOCSETP, &b); 4966002Sroot } 4976002Sroot 4986002Sroot /* 4996002Sroot * Send interrupt to process on other side of pty. 5006002Sroot * If it is in raw mode, just write NULL; 5016002Sroot * otherwise, write intr char. 5026002Sroot */ 5036002Sroot interrupt() 5046002Sroot { 5056002Sroot struct sgttyb b; 5066002Sroot struct tchars tchars; 5076002Sroot 5086002Sroot ptyflush(); /* half-hearted */ 5096002Sroot ioctl(pty, TIOCGETP, &b); 5106002Sroot if (b.sg_flags & RAW) { 5116002Sroot *pfrontp++ = '\0'; 5126002Sroot return; 5136002Sroot } 5146002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5156002Sroot '\177' : tchars.t_intrc; 5166002Sroot } 5176002Sroot 5186002Sroot ptyflush() 5196002Sroot { 5206002Sroot int n; 5216002Sroot 5226002Sroot if ((n = pfrontp - pbackp) > 0) 5236002Sroot n = write(pty, pbackp, n); 5248346Ssam if (n < 0) 5258346Ssam return; 5266002Sroot pbackp += n; 5276002Sroot if (pbackp == pfrontp) 5286002Sroot pbackp = pfrontp = ptyobuf; 5296002Sroot } 5306002Sroot 5316002Sroot netflush() 5326002Sroot { 5336002Sroot int n; 5346002Sroot 5356002Sroot if ((n = nfrontp - nbackp) > 0) 5366002Sroot n = write(net, nbackp, n); 5378346Ssam if (n < 0) { 5388346Ssam if (errno == EWOULDBLOCK) 5398346Ssam return; 5408346Ssam /* should blow this guy away... */ 5418346Ssam return; 5428346Ssam } 5436002Sroot nbackp += n; 5446002Sroot if (nbackp == nfrontp) 5456002Sroot nbackp = nfrontp = netobuf; 5466002Sroot } 5476002Sroot 5486002Sroot cleanup() 5496002Sroot { 5506002Sroot 5516002Sroot rmut(); 55210008Ssam vhangup(); /* XXX */ 55310191Ssam shutdown(net, 2); 5546002Sroot kill(0, SIGKILL); 5556002Sroot exit(1); 5566002Sroot } 5576002Sroot 5586002Sroot #include <utmp.h> 5596002Sroot 5606002Sroot struct utmp wtmp; 5616002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5626002Sroot char utmp[] = "/etc/utmp"; 5638379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5648379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5656002Sroot 5666002Sroot rmut() 5676002Sroot { 5686002Sroot register f; 5696002Sroot int found = 0; 5706002Sroot 5716002Sroot f = open(utmp, 2); 5726002Sroot if (f >= 0) { 5738379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5746002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5756002Sroot continue; 5768379Ssam lseek(f, -(long)sizeof (wtmp), 1); 5776002Sroot SCPYN(wtmp.ut_name, ""); 57812683Ssam SCPYN(wtmp.ut_host, ""); 5796002Sroot time(&wtmp.ut_time); 5808379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5816002Sroot found++; 5826002Sroot } 5836002Sroot close(f); 5846002Sroot } 5856002Sroot if (found) { 5866002Sroot f = open(wtmpf, 1); 5876002Sroot if (f >= 0) { 5886002Sroot SCPYN(wtmp.ut_line, line+5); 5896002Sroot SCPYN(wtmp.ut_name, ""); 59012683Ssam SCPYN(wtmp.ut_host, ""); 5916002Sroot time(&wtmp.ut_time); 5926002Sroot lseek(f, (long)0, 2); 5938379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5946002Sroot close(f); 5956002Sroot } 5966002Sroot } 5976002Sroot chmod(line, 0666); 5986002Sroot chown(line, 0, 0); 5996002Sroot line[strlen("/dev/")] = 'p'; 6006002Sroot chmod(line, 0666); 6016002Sroot chown(line, 0, 0); 6026002Sroot } 60312683Ssam 60412683Ssam /* 60512683Ssam * Convert network-format internet address 60612683Ssam * to base 256 d.d.d.d representation. 60712683Ssam */ 60812683Ssam char * 60912683Ssam ntoa(in) 61012683Ssam struct in_addr in; 61112683Ssam { 61212683Ssam static char b[18]; 61312683Ssam register char *p; 61412683Ssam 61512683Ssam p = (char *)∈ 61612683Ssam #define UC(b) (((int)b)&0xff) 61712683Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 61812683Ssam return (b); 61912683Ssam } 620