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