1*6295Sroot #ifndef lint 2*6295Sroot static char sccsid[] = "@(#)telnetd.c 4.5 82/03/23"; 3*6295Sroot #endif 4*6295Sroot 56002Sroot /* 66002Sroot * Stripped-down telnet server. 76002Sroot */ 86002Sroot #include <stdio.h> 96002Sroot #include <signal.h> 106002Sroot #include <errno.h> 116002Sroot #include <sgtty.h> 126002Sroot #include <wait.h> 136002Sroot #include <sys/types.h> 146002Sroot #include <sys/socket.h> 156002Sroot #include <net/in.h> 166002Sroot #include "telnet.h" 176002Sroot 186002Sroot #define INFINITY 10000000 196002Sroot #define BELL '\07' 206002Sroot 216002Sroot char hisopts[256]; 226002Sroot char myopts[256]; 236002Sroot 246002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 256002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 266002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 276002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 286002Sroot 296002Sroot /* 306002Sroot * I/O data buffers, pointers, and counters. 316002Sroot */ 326002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 336002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 346002Sroot char netibuf[BUFSIZ], *netip = netibuf; 356002Sroot char netobuf[BUFSIZ] = 366002Sroot { IAC, DO, TELOPT_ECHO, '\r', '\n' }, 376002Sroot *nfrontp = netobuf + 5, *nbackp = netobuf; 386002Sroot int pcc, ncc; 396002Sroot 406002Sroot int pty, net; 416002Sroot int inter; 426002Sroot extern int errno; 436002Sroot char line[] = "/dev/ptyp0"; 446002Sroot 45*6295Sroot struct sockaddr_in sin = { AF_INET, IPPORT_TELNET }; 466287Sroot int options = SO_ACCEPTCONN|SO_KEEPALIVE; 476002Sroot 486002Sroot main(argc, argv) 496002Sroot char *argv[]; 506002Sroot { 516002Sroot int s, pid; 526002Sroot union wait status; 536002Sroot 546002Sroot argc--, argv++; 556002Sroot if (argc > 0 && !strcmp(argv[0], "-d")) 566002Sroot options |= SO_DEBUG; 57*6295Sroot #if vax || pdp11 58*6295Sroot sin.sin_port = htons(sin.sin_port); 59*6295Sroot #endif 606002Sroot for (;;) { 616002Sroot errno = 0; 626002Sroot if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) { 636002Sroot perror("socket"); 646002Sroot sleep(5); 656002Sroot continue; 666002Sroot } 676002Sroot if (accept(s, 0) < 0) { 686002Sroot perror("accept"); 696002Sroot close(s); 706002Sroot sleep(1); 716002Sroot continue; 726002Sroot } 736002Sroot if ((pid = fork()) < 0) 746002Sroot printf("Out of processes\n"); 756002Sroot else if (pid == 0) 766002Sroot doit(s); 776002Sroot close(s); 786002Sroot while (wait3(status, WNOHANG, 0) > 0) 796002Sroot continue; 806002Sroot } 816002Sroot /*NOTREACHED*/ 826002Sroot } 836002Sroot 846002Sroot int cleanup(); 856002Sroot 866002Sroot /* 876002Sroot * Get a pty, scan input lines. 886002Sroot */ 896002Sroot doit(f) 906002Sroot { 916002Sroot char *cp = line; 926002Sroot int i, p, cc, t; 936002Sroot struct sgttyb b; 946002Sroot 956002Sroot for (i = 0; i < 16; i++) { 966002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 976002Sroot p = open(cp, 2); 986002Sroot if (p > 0) 996002Sroot goto gotpty; 1006002Sroot } 1016002Sroot dup2(f, 1); 1026002Sroot printf("All network ports in use.\n"); 1036002Sroot exit(1); 1046002Sroot gotpty: 1056002Sroot dup2(f, 0); 1066002Sroot cp[strlen("/dev/")] = 't'; 1076002Sroot t = open("/dev/tty", 2); 1086002Sroot if (t >= 0) { 1096002Sroot ioctl(t, TIOCNOTTY, 0); 1106002Sroot close(t); 1116002Sroot } 1126002Sroot t = open(cp, 2); 1136002Sroot if (t < 0) { 1146002Sroot dup2(f, 2); 1156002Sroot perror(cp); 1166002Sroot exit(1); 1176002Sroot } 1186002Sroot ioctl(t, TIOCGETP, &b); 1196002Sroot b.sg_flags = ECHO|CRMOD|XTABS|ANYP; 1206002Sroot ioctl(t, TIOCSETP, &b); 1216002Sroot if ((i = fork()) < 0) { 1226002Sroot dup2(f, 2); 1236002Sroot perror("fork"); 1246002Sroot exit(1); 1256002Sroot } 1266002Sroot if (i) 1276002Sroot telnet(f, p); 1286002Sroot close(f); 1296002Sroot close(p); 1306002Sroot dup2(t, 0); 1316002Sroot dup2(t, 1); 1326002Sroot dup2(t, 2); 1336002Sroot close(t); 1346002Sroot execl("/bin/login", "telnet-login", 0); 1356002Sroot perror("/bin/login"); 1366002Sroot exit(1); 1376002Sroot } 1386002Sroot 1396002Sroot /* 1406002Sroot * Main loop. Select from pty and network, and 1416002Sroot * hand data to telnet receiver finite state machine. 1426002Sroot */ 1436002Sroot telnet(f, p) 1446002Sroot { 1456002Sroot int on = 1; 1466002Sroot 1476002Sroot net = f, pty = p; 1486002Sroot ioctl(f, FIONBIO, &on); 1496002Sroot ioctl(p, FIONBIO, &on); 1506002Sroot signal(SIGTSTP, SIG_IGN); 1516002Sroot sigset(SIGCHLD, cleanup); 1526002Sroot 1536002Sroot for (;;) { 1546002Sroot int ibits = 0, obits = 0; 1556002Sroot register int c; 1566002Sroot 1576002Sroot /* 1586002Sroot * Never look for input if there's still 1596002Sroot * stuff in the corresponding output buffer 1606002Sroot */ 1616002Sroot if (nfrontp - nbackp) 1626002Sroot obits |= (1 << f); 1636002Sroot else 1646002Sroot ibits |= (1 << p); 1656002Sroot if (pfrontp - pbackp) 1666002Sroot obits |= (1 << p); 1676002Sroot else 1686002Sroot ibits |= (1 << f); 1696002Sroot if (ncc < 0 && pcc < 0) 1706002Sroot break; 1716002Sroot select(32, &ibits, &obits, INFINITY); 1726002Sroot if (ibits == 0 && obits == 0) { 1736002Sroot sleep(5); 1746002Sroot continue; 1756002Sroot } 1766002Sroot 1776002Sroot /* 1786002Sroot * Something to read from the network... 1796002Sroot */ 1806002Sroot if (ibits & (1 << f)) { 1816002Sroot ncc = read(f, netibuf, BUFSIZ); 1826002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 1836002Sroot ncc = 0; 1846002Sroot else { 1856002Sroot if (ncc <= 0) 1866002Sroot break; 1876002Sroot netip = netibuf; 1886002Sroot } 1896002Sroot } 1906002Sroot 1916002Sroot /* 1926002Sroot * Something to read from the pty... 1936002Sroot */ 1946002Sroot if (ibits & (1 << p)) { 1956002Sroot pcc = read(p, ptyibuf, BUFSIZ); 1966002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 1976002Sroot pcc = 0; 1986002Sroot else { 1996002Sroot if (pcc <= 0) 2006002Sroot break; 2016002Sroot ptyip = ptyibuf; 2026002Sroot } 2036002Sroot } 2046002Sroot 2056002Sroot while (pcc > 0) { 2066002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 2076002Sroot break; 2086002Sroot c = *ptyip++ & 0377, pcc--; 2096002Sroot if (c == IAC) 2106002Sroot *nfrontp++ = c; 2116002Sroot *nfrontp++ = c; 2126002Sroot } 2136002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 2146002Sroot netflush(); 2156002Sroot if (ncc > 0) 2166002Sroot telrcv(); 2176002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 2186002Sroot ptyflush(); 2196002Sroot } 2206002Sroot cleanup(); 2216002Sroot } 2226002Sroot 2236002Sroot /* 2246002Sroot * State for recv fsm 2256002Sroot */ 2266002Sroot #define TS_DATA 0 /* base state */ 2276002Sroot #define TS_IAC 1 /* look for double IAC's */ 2286002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 2296002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 2306002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 2316002Sroot #define TS_WILL 5 /* will option negotiation */ 2326002Sroot #define TS_WONT 6 /* wont " */ 2336002Sroot #define TS_DO 7 /* do " */ 2346002Sroot #define TS_DONT 8 /* dont " */ 2356002Sroot 2366002Sroot telrcv() 2376002Sroot { 2386002Sroot register int c; 2396002Sroot static int state = TS_DATA; 2406002Sroot struct sgttyb b; 2416002Sroot 2426002Sroot while (ncc > 0) { 2436002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 2446002Sroot return; 2456002Sroot c = *netip++ & 0377, ncc--; 2466002Sroot switch (state) { 2476002Sroot 2486002Sroot case TS_DATA: 2496002Sroot if (c == IAC) { 2506002Sroot state = TS_IAC; 2516002Sroot break; 2526002Sroot } 2536002Sroot if (inter > 0) 2546002Sroot break; 2556002Sroot *pfrontp++ = c; 2566002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 2576002Sroot state = TS_CR; 2586002Sroot break; 2596002Sroot 2606002Sroot case TS_CR: 2616002Sroot if (c && c != '\n') 2626002Sroot *pfrontp++ = c; 2636002Sroot state = TS_DATA; 2646002Sroot break; 2656002Sroot 2666002Sroot case TS_IAC: 2676002Sroot switch (c) { 2686002Sroot 2696002Sroot /* 2706002Sroot * Send the process on the pty side an 2716002Sroot * interrupt. Do this with a NULL or 2726002Sroot * interrupt char; depending on the tty mode. 2736002Sroot */ 2746002Sroot case BREAK: 2756002Sroot case IP: 2766002Sroot interrupt(); 2776002Sroot break; 2786002Sroot 2796002Sroot /* 2806002Sroot * Are You There? 2816002Sroot */ 2826002Sroot case AYT: 2836002Sroot *pfrontp++ = BELL; 2846002Sroot break; 2856002Sroot 2866002Sroot /* 2876002Sroot * Erase Character and 2886002Sroot * Erase Line 2896002Sroot */ 2906002Sroot case EC: 2916002Sroot case EL: 2926002Sroot ptyflush(); /* half-hearted */ 2936002Sroot ioctl(pty, TIOCGETP, &b); 2946002Sroot *pfrontp++ = (c == EC) ? 2956002Sroot b.sg_erase : b.sg_kill; 2966002Sroot break; 2976002Sroot 2986002Sroot /* 2996002Sroot * Check for urgent data... 3006002Sroot */ 3016002Sroot case DM: 3026002Sroot break; 3036002Sroot 3046002Sroot /* 3056002Sroot * Begin option subnegotiation... 3066002Sroot */ 3076002Sroot case SB: 3086002Sroot state = TS_BEGINNEG; 3096002Sroot continue; 3106002Sroot 3116002Sroot case WILL: 3126002Sroot case WONT: 3136002Sroot case DO: 3146002Sroot case DONT: 3156002Sroot state = TS_WILL + (c - WILL); 3166002Sroot continue; 3176002Sroot 3186002Sroot case IAC: 3196002Sroot *pfrontp++ = c; 3206002Sroot break; 3216002Sroot } 3226002Sroot state = TS_DATA; 3236002Sroot break; 3246002Sroot 3256002Sroot case TS_BEGINNEG: 3266002Sroot if (c == IAC) 3276002Sroot state = TS_ENDNEG; 3286002Sroot break; 3296002Sroot 3306002Sroot case TS_ENDNEG: 3316002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 3326002Sroot break; 3336002Sroot 3346002Sroot case TS_WILL: 3356002Sroot if (!hisopts[c]) 3366002Sroot willoption(c); 3376002Sroot state = TS_DATA; 3386002Sroot continue; 3396002Sroot 3406002Sroot case TS_WONT: 3416002Sroot if (hisopts[c]) 3426002Sroot wontoption(c); 3436002Sroot state = TS_DATA; 3446002Sroot continue; 3456002Sroot 3466002Sroot case TS_DO: 3476002Sroot if (!myopts[c]) 3486002Sroot dooption(c); 3496002Sroot state = TS_DATA; 3506002Sroot continue; 3516002Sroot 3526002Sroot case TS_DONT: 3536002Sroot if (myopts[c]) { 3546002Sroot myopts[c] = 0; 3556002Sroot sprintf(nfrontp, wont, c); 3566002Sroot nfrontp += sizeof(wont) - 2; 3576002Sroot } 3586002Sroot state = TS_DATA; 3596002Sroot continue; 3606002Sroot 3616002Sroot default: 3626002Sroot printf("netser: panic state=%d\n", state); 3636002Sroot exit(1); 3646002Sroot } 3656002Sroot } 3666002Sroot } 3676002Sroot 3686002Sroot willoption(option) 3696002Sroot int option; 3706002Sroot { 3716002Sroot char *fmt; 3726002Sroot 3736002Sroot switch (option) { 3746002Sroot 3756002Sroot case TELOPT_BINARY: 3766002Sroot mode(RAW, 0); 3776002Sroot goto common; 3786002Sroot 3796002Sroot case TELOPT_ECHO: 3806002Sroot mode(0, ECHO|CRMOD); 3816002Sroot /*FALL THRU*/ 3826002Sroot 3836002Sroot case TELOPT_SGA: 3846002Sroot common: 3856002Sroot hisopts[option] = 1; 3866002Sroot fmt = doopt; 3876002Sroot break; 3886002Sroot 3896002Sroot case TELOPT_TM: 3906002Sroot fmt = dont; 3916002Sroot break; 3926002Sroot 3936002Sroot default: 3946002Sroot fmt = dont; 3956002Sroot break; 3966002Sroot } 3976023Ssam sprintf(nfrontp, fmt, option); 3986002Sroot nfrontp += sizeof(dont) - 2; 3996002Sroot } 4006002Sroot 4016002Sroot wontoption(option) 4026002Sroot int option; 4036002Sroot { 4046002Sroot char *fmt; 4056002Sroot 4066002Sroot switch (option) { 4076002Sroot 4086002Sroot case TELOPT_ECHO: 4096002Sroot mode(ECHO|CRMOD, 0); 4106002Sroot goto common; 4116002Sroot 4126002Sroot case TELOPT_BINARY: 4136002Sroot mode(0, RAW); 4146002Sroot /*FALL THRU*/ 4156002Sroot 4166002Sroot case TELOPT_SGA: 4176002Sroot common: 4186002Sroot hisopts[option] = 0; 4196002Sroot fmt = dont; 4206002Sroot break; 4216002Sroot 4226002Sroot default: 4236002Sroot fmt = dont; 4246002Sroot } 4256002Sroot sprintf(nfrontp, fmt, option); 4266002Sroot nfrontp += sizeof(doopt) - 2; 4276002Sroot } 4286002Sroot 4296002Sroot dooption(option) 4306002Sroot int option; 4316002Sroot { 4326002Sroot char *fmt; 4336002Sroot 4346002Sroot switch (option) { 4356002Sroot 4366002Sroot case TELOPT_TM: 4376002Sroot fmt = wont; 4386002Sroot break; 4396002Sroot 4406002Sroot case TELOPT_ECHO: 4416002Sroot mode(ECHO|CRMOD, 0); 4426002Sroot goto common; 4436002Sroot 4446002Sroot case TELOPT_BINARY: 4456002Sroot mode(RAW, 0); 4466002Sroot /*FALL THRU*/ 4476002Sroot 4486002Sroot case TELOPT_SGA: 4496002Sroot common: 4506002Sroot fmt = will; 4516002Sroot break; 4526002Sroot 4536002Sroot default: 4546002Sroot fmt = wont; 4556002Sroot break; 4566002Sroot } 4576002Sroot sprintf(nfrontp, fmt, option); 4586002Sroot nfrontp += sizeof(doopt) - 2; 4596002Sroot } 4606002Sroot 4616002Sroot mode(on, off) 4626002Sroot int on, off; 4636002Sroot { 4646002Sroot struct sgttyb b; 4656002Sroot 4666002Sroot ptyflush(); 4676002Sroot ioctl(pty, TIOCGETP, &b); 4686002Sroot b.sg_flags |= on; 4696002Sroot b.sg_flags &= ~off; 4706002Sroot ioctl(pty, TIOCSETP, &b); 4716002Sroot } 4726002Sroot 4736002Sroot /* 4746002Sroot * Send interrupt to process on other side of pty. 4756002Sroot * If it is in raw mode, just write NULL; 4766002Sroot * otherwise, write intr char. 4776002Sroot */ 4786002Sroot interrupt() 4796002Sroot { 4806002Sroot struct sgttyb b; 4816002Sroot struct tchars tchars; 4826002Sroot 4836002Sroot ptyflush(); /* half-hearted */ 4846002Sroot ioctl(pty, TIOCGETP, &b); 4856002Sroot if (b.sg_flags & RAW) { 4866002Sroot *pfrontp++ = '\0'; 4876002Sroot return; 4886002Sroot } 4896002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 4906002Sroot '\177' : tchars.t_intrc; 4916002Sroot } 4926002Sroot 4936002Sroot ptyflush() 4946002Sroot { 4956002Sroot int n; 4966002Sroot 4976002Sroot if ((n = pfrontp - pbackp) > 0) 4986002Sroot n = write(pty, pbackp, n); 4996002Sroot if (n < 0 && errno == EWOULDBLOCK) 5006002Sroot n = 0; 5016002Sroot pbackp += n; 5026002Sroot if (pbackp == pfrontp) 5036002Sroot pbackp = pfrontp = ptyobuf; 5046002Sroot } 5056002Sroot 5066002Sroot netflush() 5076002Sroot { 5086002Sroot int n; 5096002Sroot 5106002Sroot if ((n = nfrontp - nbackp) > 0) 5116002Sroot n = write(net, nbackp, n); 5126002Sroot if (n < 0 && errno == EWOULDBLOCK) 5136002Sroot n = 0; 5146002Sroot nbackp += n; 5156002Sroot if (nbackp == nfrontp) 5166002Sroot nbackp = nfrontp = netobuf; 5176002Sroot } 5186002Sroot 5196002Sroot cleanup() 5206002Sroot { 5216002Sroot int how = 2; 5226002Sroot 5236002Sroot rmut(); 5246002Sroot vhangup(); 5256002Sroot ioctl(net, SIOCDONE, &how); 5266002Sroot kill(0, SIGKILL); 5276002Sroot exit(1); 5286002Sroot } 5296002Sroot 5306002Sroot #include <utmp.h> 5316002Sroot 5326002Sroot struct utmp wtmp; 5336002Sroot char wtmpf[] = "/usr/adm/wtmp"; 5346002Sroot char utmp[] = "/etc/utmp"; 5356002Sroot #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 5366002Sroot #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 5376002Sroot 5386002Sroot rmut() 5396002Sroot { 5406002Sroot register f; 5416002Sroot int found = 0; 5426002Sroot 5436002Sroot f = open(utmp, 2); 5446002Sroot if (f >= 0) { 5456002Sroot while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) { 5466002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 5476002Sroot continue; 5486002Sroot lseek(f, -(long)sizeof(wtmp), 1); 5496002Sroot SCPYN(wtmp.ut_name, ""); 5506002Sroot time(&wtmp.ut_time); 5516002Sroot write(f, (char *)&wtmp, sizeof(wtmp)); 5526002Sroot found++; 5536002Sroot } 5546002Sroot close(f); 5556002Sroot } 5566002Sroot if (found) { 5576002Sroot f = open(wtmpf, 1); 5586002Sroot if (f >= 0) { 5596002Sroot SCPYN(wtmp.ut_line, line+5); 5606002Sroot SCPYN(wtmp.ut_name, ""); 5616002Sroot time(&wtmp.ut_time); 5626002Sroot lseek(f, (long)0, 2); 5636002Sroot write(f, (char *)&wtmp, sizeof(wtmp)); 5646002Sroot close(f); 5656002Sroot } 5666002Sroot } 5676002Sroot chmod(line, 0666); 5686002Sroot chown(line, 0, 0); 5696002Sroot line[strlen("/dev/")] = 'p'; 5706002Sroot chmod(line, 0666); 5716002Sroot chown(line, 0, 0); 5726002Sroot } 573