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