16295Sroot #ifndef lint 2*17583Ssam static char sccsid[] = "@(#)telnetd.c 4.31 (Berkeley) 12/23/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> 11*17583Ssam #include <sys/file.h> 129218Ssam 139218Ssam #include <netinet/in.h> 149218Ssam 1512216Ssam #include <arpa/telnet.h> 1612216Ssam 176002Sroot #include <stdio.h> 186002Sroot #include <signal.h> 196002Sroot #include <errno.h> 206002Sroot #include <sgtty.h> 218346Ssam #include <netdb.h> 2217187Sralph #include <syslog.h> 239218Ssam 2413798Ssam #define BELL '\07' 2513798Ssam #define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s" 266002Sroot 276002Sroot char hisopts[256]; 286002Sroot char myopts[256]; 296002Sroot 306002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 316002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 326002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 336002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 346002Sroot 356002Sroot /* 366002Sroot * I/O data buffers, pointers, and counters. 376002Sroot */ 386002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 396002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 406002Sroot char netibuf[BUFSIZ], *netip = netibuf; 416388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 426002Sroot int pcc, ncc; 436002Sroot 446002Sroot int pty, net; 456002Sroot int inter; 4613799Ssam extern char **environ; 476002Sroot extern int errno; 486002Sroot char line[] = "/dev/ptyp0"; 496002Sroot 506002Sroot main(argc, argv) 516002Sroot char *argv[]; 526002Sroot { 5316371Skarels struct sockaddr_in from; 5417156Ssam int on = 1, fromlen; 556002Sroot 5616371Skarels fromlen = sizeof (from); 5716371Skarels if (getpeername(0, &from, &fromlen) < 0) { 5816371Skarels fprintf(stderr, "%s: ", argv[0]); 5916371Skarels perror("getpeername"); 6016371Skarels _exit(1); 618346Ssam } 6217156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 6317187Sralph openlog(argv[0], LOG_PID, 0); 6417187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 6510418Ssam } 6616371Skarels doit(0, &from); 676002Sroot } 686002Sroot 6913799Ssam char *envinit[] = { "TERM=network", 0 }; 706002Sroot int cleanup(); 716002Sroot 726002Sroot /* 736002Sroot * Get a pty, scan input lines. 746002Sroot */ 7512683Ssam doit(f, who) 7612683Ssam int f; 7712683Ssam struct sockaddr_in *who; 786002Sroot { 7917444Sralph char *cp = line, *host, *inet_ntoa(); 80*17583Ssam int i, p, t; 816002Sroot struct sgttyb b; 8212683Ssam struct hostent *hp; 83*17583Ssam char *pqrs; 846002Sroot 85*17583Ssam t = strlen("/dev/ptyp"); 86*17583Ssam for (pqrs = "pqrs"; *pqrs; pqrs++) { 87*17583Ssam cp[t] = *pqrs; 88*17583Ssam for (i = 0; i < 16; i++) { 89*17583Ssam cp[t] = "0123456789abcdef"[i]; 90*17583Ssam p = open(cp, O_RDWR); 91*17583Ssam if (p > 0) 92*17583Ssam goto gotpty; 93*17583Ssam } 946002Sroot } 959244Ssam fatal(f, "All network ports in use"); 969244Ssam /*NOTREACHED*/ 976002Sroot gotpty: 986002Sroot dup2(f, 0); 996002Sroot cp[strlen("/dev/")] = 't'; 100*17583Ssam t = open("/dev/tty", O_RDWR); 1016002Sroot if (t >= 0) { 1026002Sroot ioctl(t, TIOCNOTTY, 0); 1036002Sroot close(t); 1046002Sroot } 105*17583Ssam t = open(cp, O_RDWR); 1069244Ssam if (t < 0) 1079244Ssam fatalperror(f, cp, errno); 1086002Sroot ioctl(t, TIOCGETP, &b); 1096388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1106002Sroot ioctl(t, TIOCSETP, &b); 1116388Ssam ioctl(p, TIOCGETP, &b); 1128379Ssam b.sg_flags &= ~ECHO; 1136388Ssam ioctl(p, TIOCSETP, &b); 11412683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 11512683Ssam who->sin_family); 11612683Ssam if (hp) 11712683Ssam host = hp->h_name; 11812683Ssam else 11917444Sralph host = inet_ntoa(who->sin_addr); 1209244Ssam if ((i = fork()) < 0) 1219244Ssam fatalperror(f, "fork", errno); 1226002Sroot if (i) 1236002Sroot telnet(f, p); 1246002Sroot close(f); 1256002Sroot close(p); 1266002Sroot dup2(t, 0); 1276002Sroot dup2(t, 1); 1286002Sroot dup2(t, 2); 1296002Sroot close(t); 13013799Ssam environ = envinit; 13112713Ssam execl("/bin/login", "login", "-h", host, 0); 1329244Ssam fatalperror(f, "/bin/login", errno); 1339244Ssam /*NOTREACHED*/ 1349244Ssam } 1359244Ssam 1369244Ssam fatal(f, msg) 1379244Ssam int f; 1389244Ssam char *msg; 1399244Ssam { 1409244Ssam char buf[BUFSIZ]; 1419244Ssam 142*17583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 1439244Ssam (void) write(f, buf, strlen(buf)); 1446002Sroot exit(1); 1456002Sroot } 1466002Sroot 1479244Ssam fatalperror(f, msg, errno) 1489244Ssam int f; 1499244Ssam char *msg; 1509244Ssam int errno; 1519244Ssam { 1529244Ssam char buf[BUFSIZ]; 1539244Ssam extern char *sys_errlist[]; 1549244Ssam 155*17583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 1569244Ssam fatal(f, buf); 1579244Ssam } 1589244Ssam 1596002Sroot /* 1606002Sroot * Main loop. Select from pty and network, and 1616002Sroot * hand data to telnet receiver finite state machine. 1626002Sroot */ 1636002Sroot telnet(f, p) 1646002Sroot { 1656002Sroot int on = 1; 16612713Ssam char hostname[32]; 1676002Sroot 1686002Sroot net = f, pty = p; 1696002Sroot ioctl(f, FIONBIO, &on); 1706002Sroot ioctl(p, FIONBIO, &on); 1716002Sroot signal(SIGTSTP, SIG_IGN); 17213028Ssam signal(SIGCHLD, cleanup); 1736002Sroot 1748379Ssam /* 1758379Ssam * Request to do remote echo. 1768379Ssam */ 1778379Ssam dooption(TELOPT_ECHO); 1788379Ssam myopts[TELOPT_ECHO] = 1; 17912713Ssam /* 18012713Ssam * Show banner that getty never gave. 18112713Ssam */ 18212713Ssam gethostname(hostname, sizeof (hostname)); 18312713Ssam sprintf(nfrontp, BANNER, hostname, ""); 18412713Ssam nfrontp += strlen(nfrontp); 1856002Sroot for (;;) { 1866002Sroot int ibits = 0, obits = 0; 1876002Sroot register int c; 1886002Sroot 1896002Sroot /* 1906002Sroot * Never look for input if there's still 1916002Sroot * stuff in the corresponding output buffer 1926002Sroot */ 193*17583Ssam if (nfrontp - nbackp || pcc > 0) 1946002Sroot obits |= (1 << f); 1956002Sroot else 1966002Sroot ibits |= (1 << p); 197*17583Ssam if (pfrontp - pbackp || ncc > 0) 1986002Sroot obits |= (1 << p); 1996002Sroot else 2006002Sroot ibits |= (1 << f); 2016002Sroot if (ncc < 0 && pcc < 0) 2026002Sroot break; 2039218Ssam select(16, &ibits, &obits, 0, 0); 2046002Sroot if (ibits == 0 && obits == 0) { 2056002Sroot sleep(5); 2066002Sroot continue; 2076002Sroot } 2086002Sroot 2096002Sroot /* 2106002Sroot * Something to read from the network... 2116002Sroot */ 2126002Sroot if (ibits & (1 << f)) { 2136002Sroot ncc = read(f, netibuf, BUFSIZ); 2146002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 2156002Sroot ncc = 0; 2166002Sroot else { 2176002Sroot if (ncc <= 0) 2186002Sroot break; 2196002Sroot netip = netibuf; 2206002Sroot } 2216002Sroot } 2226002Sroot 2236002Sroot /* 2246002Sroot * Something to read from the pty... 2256002Sroot */ 2266002Sroot if (ibits & (1 << p)) { 2276002Sroot pcc = read(p, ptyibuf, BUFSIZ); 2286002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 2296002Sroot pcc = 0; 2306002Sroot else { 2316002Sroot if (pcc <= 0) 2326002Sroot break; 2336002Sroot ptyip = ptyibuf; 2346002Sroot } 2356002Sroot } 2366002Sroot 2376002Sroot while (pcc > 0) { 2386002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2396002Sroot break; 2406002Sroot c = *ptyip++ & 0377, pcc--; 2416002Sroot if (c == IAC) 2426002Sroot *nfrontp++ = c; 2436002Sroot *nfrontp++ = c; 2446002Sroot } 2456002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2466002Sroot netflush(); 2476002Sroot if (ncc > 0) 2486002Sroot telrcv(); 2496002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2506002Sroot ptyflush(); 2516002Sroot } 2526002Sroot cleanup(); 2536002Sroot } 2546002Sroot 2556002Sroot /* 2566002Sroot * State for recv fsm 2576002Sroot */ 2586002Sroot #define TS_DATA 0 /* base state */ 2596002Sroot #define TS_IAC 1 /* look for double IAC's */ 2606002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2616002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2626002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2636002Sroot #define TS_WILL 5 /* will option negotiation */ 2646002Sroot #define TS_WONT 6 /* wont " */ 2656002Sroot #define TS_DO 7 /* do " */ 2666002Sroot #define TS_DONT 8 /* dont " */ 2676002Sroot 2686002Sroot telrcv() 2696002Sroot { 2706002Sroot register int c; 2716002Sroot static int state = TS_DATA; 2726002Sroot struct sgttyb b; 2736002Sroot 2746002Sroot while (ncc > 0) { 2756002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2766002Sroot return; 2776002Sroot c = *netip++ & 0377, ncc--; 2786002Sroot switch (state) { 2796002Sroot 2806002Sroot case TS_DATA: 2816002Sroot if (c == IAC) { 2826002Sroot state = TS_IAC; 2836002Sroot break; 2846002Sroot } 2856002Sroot if (inter > 0) 2866002Sroot break; 2876002Sroot *pfrontp++ = c; 2886002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2896002Sroot state = TS_CR; 2906002Sroot break; 2916002Sroot 2926002Sroot case TS_CR: 2936002Sroot if (c && c != '\n') 2946002Sroot *pfrontp++ = c; 2956002Sroot state = TS_DATA; 2966002Sroot break; 2976002Sroot 2986002Sroot case TS_IAC: 2996002Sroot switch (c) { 3006002Sroot 3016002Sroot /* 3026002Sroot * Send the process on the pty side an 3036002Sroot * interrupt. Do this with a NULL or 3046002Sroot * interrupt char; depending on the tty mode. 3056002Sroot */ 3066002Sroot case BREAK: 3076002Sroot case IP: 3086002Sroot interrupt(); 3096002Sroot break; 3106002Sroot 3116002Sroot /* 3126002Sroot * Are You There? 3136002Sroot */ 3146002Sroot case AYT: 315*17583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 316*17583Ssam nfrontp += 9; 3176002Sroot break; 3186002Sroot 3196002Sroot /* 3206002Sroot * Erase Character and 3216002Sroot * Erase Line 3226002Sroot */ 3236002Sroot case EC: 3246002Sroot case EL: 3256002Sroot ptyflush(); /* half-hearted */ 3266002Sroot ioctl(pty, TIOCGETP, &b); 3276002Sroot *pfrontp++ = (c == EC) ? 3286002Sroot b.sg_erase : b.sg_kill; 3296002Sroot break; 3306002Sroot 3316002Sroot /* 3326002Sroot * Check for urgent data... 3336002Sroot */ 3346002Sroot case DM: 3356002Sroot break; 3366002Sroot 3376002Sroot /* 3386002Sroot * Begin option subnegotiation... 3396002Sroot */ 3406002Sroot case SB: 3416002Sroot state = TS_BEGINNEG; 3426002Sroot continue; 3436002Sroot 3446002Sroot case WILL: 3456002Sroot case WONT: 3466002Sroot case DO: 3476002Sroot case DONT: 3486002Sroot state = TS_WILL + (c - WILL); 3496002Sroot continue; 3506002Sroot 3516002Sroot case IAC: 3526002Sroot *pfrontp++ = c; 3536002Sroot break; 3546002Sroot } 3556002Sroot state = TS_DATA; 3566002Sroot break; 3576002Sroot 3586002Sroot case TS_BEGINNEG: 3596002Sroot if (c == IAC) 3606002Sroot state = TS_ENDNEG; 3616002Sroot break; 3626002Sroot 3636002Sroot case TS_ENDNEG: 3646002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3656002Sroot break; 3666002Sroot 3676002Sroot case TS_WILL: 3686002Sroot if (!hisopts[c]) 3696002Sroot willoption(c); 3706002Sroot state = TS_DATA; 3716002Sroot continue; 3726002Sroot 3736002Sroot case TS_WONT: 3746002Sroot if (hisopts[c]) 3756002Sroot wontoption(c); 3766002Sroot state = TS_DATA; 3776002Sroot continue; 3786002Sroot 3796002Sroot case TS_DO: 3806002Sroot if (!myopts[c]) 3816002Sroot dooption(c); 3826002Sroot state = TS_DATA; 3836002Sroot continue; 3846002Sroot 3856002Sroot case TS_DONT: 3866002Sroot if (myopts[c]) { 3876002Sroot myopts[c] = 0; 3886002Sroot sprintf(nfrontp, wont, c); 3898379Ssam nfrontp += sizeof (wont) - 2; 3906002Sroot } 3916002Sroot state = TS_DATA; 3926002Sroot continue; 3936002Sroot 3946002Sroot default: 3959218Ssam printf("telnetd: panic state=%d\n", state); 3966002Sroot exit(1); 3976002Sroot } 3986002Sroot } 3996002Sroot } 4006002Sroot 4016002Sroot willoption(option) 4026002Sroot int option; 4036002Sroot { 4046002Sroot char *fmt; 4056002Sroot 4066002Sroot switch (option) { 4076002Sroot 4086002Sroot case TELOPT_BINARY: 4096002Sroot mode(RAW, 0); 4106002Sroot goto common; 4116002Sroot 4126002Sroot case TELOPT_ECHO: 4136002Sroot mode(0, ECHO|CRMOD); 4146002Sroot /*FALL THRU*/ 4156002Sroot 4166002Sroot case TELOPT_SGA: 4176002Sroot common: 4186002Sroot hisopts[option] = 1; 4196002Sroot fmt = doopt; 4206002Sroot break; 4216002Sroot 4226002Sroot case TELOPT_TM: 4236002Sroot fmt = dont; 4246002Sroot break; 4256002Sroot 4266002Sroot default: 4276002Sroot fmt = dont; 4286002Sroot break; 4296002Sroot } 4306023Ssam sprintf(nfrontp, fmt, option); 4318379Ssam nfrontp += sizeof (dont) - 2; 4326002Sroot } 4336002Sroot 4346002Sroot wontoption(option) 4356002Sroot int option; 4366002Sroot { 4376002Sroot char *fmt; 4386002Sroot 4396002Sroot switch (option) { 4406002Sroot 4416002Sroot case TELOPT_ECHO: 4426002Sroot mode(ECHO|CRMOD, 0); 4436002Sroot goto common; 4446002Sroot 4456002Sroot case TELOPT_BINARY: 4466002Sroot mode(0, RAW); 4476002Sroot /*FALL THRU*/ 4486002Sroot 4496002Sroot case TELOPT_SGA: 4506002Sroot common: 4516002Sroot hisopts[option] = 0; 4526002Sroot fmt = dont; 4536002Sroot break; 4546002Sroot 4556002Sroot default: 4566002Sroot fmt = dont; 4576002Sroot } 4586002Sroot sprintf(nfrontp, fmt, option); 4598379Ssam nfrontp += sizeof (doopt) - 2; 4606002Sroot } 4616002Sroot 4626002Sroot dooption(option) 4636002Sroot int option; 4646002Sroot { 4656002Sroot char *fmt; 4666002Sroot 4676002Sroot switch (option) { 4686002Sroot 4696002Sroot case TELOPT_TM: 4706002Sroot fmt = wont; 4716002Sroot break; 4726002Sroot 4736002Sroot case TELOPT_ECHO: 4746002Sroot mode(ECHO|CRMOD, 0); 4756002Sroot goto common; 4766002Sroot 4776002Sroot case TELOPT_BINARY: 4786002Sroot mode(RAW, 0); 4796002Sroot /*FALL THRU*/ 4806002Sroot 4816002Sroot case TELOPT_SGA: 4826002Sroot common: 4836002Sroot fmt = will; 4846002Sroot break; 4856002Sroot 4866002Sroot default: 4876002Sroot fmt = wont; 4886002Sroot break; 4896002Sroot } 4906002Sroot sprintf(nfrontp, fmt, option); 4918379Ssam nfrontp += sizeof (doopt) - 2; 4926002Sroot } 4936002Sroot 4946002Sroot mode(on, off) 4956002Sroot int on, off; 4966002Sroot { 4976002Sroot struct sgttyb b; 4986002Sroot 4996002Sroot ptyflush(); 5006002Sroot ioctl(pty, TIOCGETP, &b); 5016002Sroot b.sg_flags |= on; 5026002Sroot b.sg_flags &= ~off; 5036002Sroot ioctl(pty, TIOCSETP, &b); 5046002Sroot } 5056002Sroot 5066002Sroot /* 5076002Sroot * Send interrupt to process on other side of pty. 5086002Sroot * If it is in raw mode, just write NULL; 5096002Sroot * otherwise, write intr char. 5106002Sroot */ 5116002Sroot interrupt() 5126002Sroot { 5136002Sroot struct sgttyb b; 5146002Sroot struct tchars tchars; 5156002Sroot 5166002Sroot ptyflush(); /* half-hearted */ 5176002Sroot ioctl(pty, TIOCGETP, &b); 5186002Sroot if (b.sg_flags & RAW) { 5196002Sroot *pfrontp++ = '\0'; 5206002Sroot return; 5216002Sroot } 5226002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 5236002Sroot '\177' : tchars.t_intrc; 5246002Sroot } 5256002Sroot 5266002Sroot ptyflush() 5276002Sroot { 5286002Sroot int n; 5296002Sroot 5306002Sroot if ((n = pfrontp - pbackp) > 0) 5316002Sroot n = write(pty, pbackp, n); 5328346Ssam if (n < 0) 5338346Ssam return; 5346002Sroot pbackp += n; 5356002Sroot if (pbackp == pfrontp) 5366002Sroot pbackp = pfrontp = ptyobuf; 5376002Sroot } 5386002Sroot 5396002Sroot netflush() 5406002Sroot { 5416002Sroot int n; 5426002Sroot 5436002Sroot if ((n = nfrontp - nbackp) > 0) 5446002Sroot n = write(net, nbackp, n); 5458346Ssam if (n < 0) { 5468346Ssam if (errno == EWOULDBLOCK) 5478346Ssam return; 5488346Ssam /* should blow this guy away... */ 5498346Ssam return; 5508346Ssam } 5516002Sroot nbackp += n; 5526002Sroot if (nbackp == nfrontp) 5536002Sroot nbackp = nfrontp = netobuf; 5546002Sroot } 5556002Sroot 5566002Sroot cleanup() 5576002Sroot { 5586002Sroot 5596002Sroot rmut(); 56010008Ssam vhangup(); /* XXX */ 56110191Ssam shutdown(net, 2); 5626002Sroot kill(0, SIGKILL); 5636002Sroot exit(1); 5646002Sroot } 5656002Sroot 5666002Sroot #include <utmp.h> 5676002Sroot 5686002Sroot struct utmp wtmp; 5696002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5706002Sroot char utmp[] = "/etc/utmp"; 5718379Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 5728379Ssam #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 5736002Sroot 5746002Sroot rmut() 5756002Sroot { 5766002Sroot register f; 5776002Sroot int found = 0; 5786002Sroot 579*17583Ssam f = open(utmp, O_RDWR); 5806002Sroot if (f >= 0) { 5818379Ssam while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 5826002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5836002Sroot continue; 584*17583Ssam lseek(f, -(long)sizeof (wtmp), L_INCR); 5856002Sroot SCPYN(wtmp.ut_name, ""); 58612683Ssam SCPYN(wtmp.ut_host, ""); 5876002Sroot time(&wtmp.ut_time); 5888379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 5896002Sroot found++; 5906002Sroot } 5916002Sroot close(f); 5926002Sroot } 5936002Sroot if (found) { 594*17583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 5956002Sroot if (f >= 0) { 5966002Sroot SCPYN(wtmp.ut_line, line+5); 5976002Sroot SCPYN(wtmp.ut_name, ""); 59812683Ssam SCPYN(wtmp.ut_host, ""); 5996002Sroot time(&wtmp.ut_time); 6008379Ssam write(f, (char *)&wtmp, sizeof (wtmp)); 6016002Sroot close(f); 6026002Sroot } 6036002Sroot } 6046002Sroot chmod(line, 0666); 6056002Sroot chown(line, 0, 0); 6066002Sroot line[strlen("/dev/")] = 'p'; 6076002Sroot chmod(line, 0666); 6086002Sroot chown(line, 0, 0); 6096002Sroot } 610