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