1*6002Sroot /* telnetd.c 4.1 82/02/28 */ 2*6002Sroot 3*6002Sroot /* 4*6002Sroot * Stripped-down telnet server. 5*6002Sroot */ 6*6002Sroot #include <stdio.h> 7*6002Sroot #include <signal.h> 8*6002Sroot #include <errno.h> 9*6002Sroot #include <sgtty.h> 10*6002Sroot #include <wait.h> 11*6002Sroot #include <sys/types.h> 12*6002Sroot #include <sys/socket.h> 13*6002Sroot #include <net/in.h> 14*6002Sroot #include "telnet.h" 15*6002Sroot 16*6002Sroot #define INFINITY 10000000 17*6002Sroot #define BELL '\07' 18*6002Sroot #define swab(x) ((((x) >> 8) | ((x) << 8)) & 0xffff) 19*6002Sroot 20*6002Sroot char hisopts[256]; 21*6002Sroot char myopts[256]; 22*6002Sroot 23*6002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 24*6002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 25*6002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 26*6002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 27*6002Sroot 28*6002Sroot /* 29*6002Sroot * I/O data buffers, pointers, and counters. 30*6002Sroot */ 31*6002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 32*6002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 33*6002Sroot char netibuf[BUFSIZ], *netip = netibuf; 34*6002Sroot char netobuf[BUFSIZ] = 35*6002Sroot { IAC, DO, TELOPT_ECHO, '\r', '\n' }, 36*6002Sroot *nfrontp = netobuf + 5, *nbackp = netobuf; 37*6002Sroot int pcc, ncc; 38*6002Sroot 39*6002Sroot int pty, net; 40*6002Sroot int inter; 41*6002Sroot extern int errno; 42*6002Sroot char line[] = "/dev/ptyp0"; 43*6002Sroot 44*6002Sroot struct sockaddr_in sin = { AF_INET, swab(IPPORT_TELNET) }; 45*6002Sroot int options = SO_ACCEPTCONN; 46*6002Sroot 47*6002Sroot main(argc, argv) 48*6002Sroot char *argv[]; 49*6002Sroot { 50*6002Sroot int s, pid; 51*6002Sroot union wait status; 52*6002Sroot 53*6002Sroot argc--, argv++; 54*6002Sroot if (argc > 0 && !strcmp(argv[0], "-d")) 55*6002Sroot options |= SO_DEBUG; 56*6002Sroot for (;;) { 57*6002Sroot errno = 0; 58*6002Sroot if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) { 59*6002Sroot perror("socket"); 60*6002Sroot sleep(5); 61*6002Sroot continue; 62*6002Sroot } 63*6002Sroot if (accept(s, 0) < 0) { 64*6002Sroot perror("accept"); 65*6002Sroot close(s); 66*6002Sroot sleep(1); 67*6002Sroot continue; 68*6002Sroot } 69*6002Sroot if ((pid = fork()) < 0) 70*6002Sroot printf("Out of processes\n"); 71*6002Sroot else if (pid == 0) 72*6002Sroot doit(s); 73*6002Sroot close(s); 74*6002Sroot while (wait3(status, WNOHANG, 0) > 0) 75*6002Sroot continue; 76*6002Sroot } 77*6002Sroot /*NOTREACHED*/ 78*6002Sroot } 79*6002Sroot 80*6002Sroot int cleanup(); 81*6002Sroot 82*6002Sroot /* 83*6002Sroot * Get a pty, scan input lines. 84*6002Sroot */ 85*6002Sroot doit(f) 86*6002Sroot { 87*6002Sroot char *cp = line; 88*6002Sroot int i, p, cc, t; 89*6002Sroot struct sgttyb b; 90*6002Sroot 91*6002Sroot for (i = 0; i < 16; i++) { 92*6002Sroot cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 93*6002Sroot p = open(cp, 2); 94*6002Sroot if (p > 0) 95*6002Sroot goto gotpty; 96*6002Sroot } 97*6002Sroot dup2(f, 1); 98*6002Sroot printf("All network ports in use.\n"); 99*6002Sroot exit(1); 100*6002Sroot gotpty: 101*6002Sroot dup2(f, 0); 102*6002Sroot cp[strlen("/dev/")] = 't'; 103*6002Sroot t = open("/dev/tty", 2); 104*6002Sroot if (t >= 0) { 105*6002Sroot ioctl(t, TIOCNOTTY, 0); 106*6002Sroot close(t); 107*6002Sroot } 108*6002Sroot t = open(cp, 2); 109*6002Sroot if (t < 0) { 110*6002Sroot dup2(f, 2); 111*6002Sroot perror(cp); 112*6002Sroot exit(1); 113*6002Sroot } 114*6002Sroot ioctl(t, TIOCGETP, &b); 115*6002Sroot b.sg_flags = ECHO|CRMOD|XTABS|ANYP; 116*6002Sroot ioctl(t, TIOCSETP, &b); 117*6002Sroot if ((i = fork()) < 0) { 118*6002Sroot dup2(f, 2); 119*6002Sroot perror("fork"); 120*6002Sroot exit(1); 121*6002Sroot } 122*6002Sroot if (i) 123*6002Sroot telnet(f, p); 124*6002Sroot close(f); 125*6002Sroot close(p); 126*6002Sroot dup2(t, 0); 127*6002Sroot dup2(t, 1); 128*6002Sroot dup2(t, 2); 129*6002Sroot close(t); 130*6002Sroot execl("/bin/login", "telnet-login", 0); 131*6002Sroot perror("/bin/login"); 132*6002Sroot exit(1); 133*6002Sroot } 134*6002Sroot 135*6002Sroot /* 136*6002Sroot * Main loop. Select from pty and network, and 137*6002Sroot * hand data to telnet receiver finite state machine. 138*6002Sroot */ 139*6002Sroot telnet(f, p) 140*6002Sroot { 141*6002Sroot int on = 1; 142*6002Sroot 143*6002Sroot net = f, pty = p; 144*6002Sroot ioctl(f, FIONBIO, &on); 145*6002Sroot ioctl(p, FIONBIO, &on); 146*6002Sroot signal(SIGTSTP, SIG_IGN); 147*6002Sroot sigset(SIGCHLD, cleanup); 148*6002Sroot 149*6002Sroot for (;;) { 150*6002Sroot int ibits = 0, obits = 0; 151*6002Sroot register int c; 152*6002Sroot 153*6002Sroot /* 154*6002Sroot * Never look for input if there's still 155*6002Sroot * stuff in the corresponding output buffer 156*6002Sroot */ 157*6002Sroot if (nfrontp - nbackp) 158*6002Sroot obits |= (1 << f); 159*6002Sroot else 160*6002Sroot ibits |= (1 << p); 161*6002Sroot if (pfrontp - pbackp) 162*6002Sroot obits |= (1 << p); 163*6002Sroot else 164*6002Sroot ibits |= (1 << f); 165*6002Sroot if (ncc < 0 && pcc < 0) 166*6002Sroot break; 167*6002Sroot select(32, &ibits, &obits, INFINITY); 168*6002Sroot if (ibits == 0 && obits == 0) { 169*6002Sroot sleep(5); 170*6002Sroot continue; 171*6002Sroot } 172*6002Sroot 173*6002Sroot /* 174*6002Sroot * Something to read from the network... 175*6002Sroot */ 176*6002Sroot if (ibits & (1 << f)) { 177*6002Sroot ncc = read(f, netibuf, BUFSIZ); 178*6002Sroot if (ncc < 0 && errno == EWOULDBLOCK) 179*6002Sroot ncc = 0; 180*6002Sroot else { 181*6002Sroot if (ncc <= 0) 182*6002Sroot break; 183*6002Sroot netip = netibuf; 184*6002Sroot } 185*6002Sroot } 186*6002Sroot 187*6002Sroot /* 188*6002Sroot * Something to read from the pty... 189*6002Sroot */ 190*6002Sroot if (ibits & (1 << p)) { 191*6002Sroot pcc = read(p, ptyibuf, BUFSIZ); 192*6002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 193*6002Sroot pcc = 0; 194*6002Sroot else { 195*6002Sroot if (pcc <= 0) 196*6002Sroot break; 197*6002Sroot ptyip = ptyibuf; 198*6002Sroot } 199*6002Sroot } 200*6002Sroot 201*6002Sroot while (pcc > 0) { 202*6002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 203*6002Sroot break; 204*6002Sroot c = *ptyip++ & 0377, pcc--; 205*6002Sroot if (c == IAC) 206*6002Sroot *nfrontp++ = c; 207*6002Sroot *nfrontp++ = c; 208*6002Sroot } 209*6002Sroot if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 210*6002Sroot netflush(); 211*6002Sroot if (ncc > 0) 212*6002Sroot telrcv(); 213*6002Sroot if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 214*6002Sroot ptyflush(); 215*6002Sroot } 216*6002Sroot cleanup(); 217*6002Sroot } 218*6002Sroot 219*6002Sroot /* 220*6002Sroot * State for recv fsm 221*6002Sroot */ 222*6002Sroot #define TS_DATA 0 /* base state */ 223*6002Sroot #define TS_IAC 1 /* look for double IAC's */ 224*6002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 225*6002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 226*6002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 227*6002Sroot #define TS_WILL 5 /* will option negotiation */ 228*6002Sroot #define TS_WONT 6 /* wont " */ 229*6002Sroot #define TS_DO 7 /* do " */ 230*6002Sroot #define TS_DONT 8 /* dont " */ 231*6002Sroot 232*6002Sroot telrcv() 233*6002Sroot { 234*6002Sroot register int c; 235*6002Sroot static int state = TS_DATA; 236*6002Sroot struct sgttyb b; 237*6002Sroot 238*6002Sroot while (ncc > 0) { 239*6002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 240*6002Sroot return; 241*6002Sroot c = *netip++ & 0377, ncc--; 242*6002Sroot switch (state) { 243*6002Sroot 244*6002Sroot case TS_DATA: 245*6002Sroot if (c == IAC) { 246*6002Sroot state = TS_IAC; 247*6002Sroot break; 248*6002Sroot } 249*6002Sroot if (inter > 0) 250*6002Sroot break; 251*6002Sroot *pfrontp++ = c; 252*6002Sroot if (!myopts[TELOPT_BINARY] && c == '\r') 253*6002Sroot state = TS_CR; 254*6002Sroot break; 255*6002Sroot 256*6002Sroot case TS_CR: 257*6002Sroot if (c && c != '\n') 258*6002Sroot *pfrontp++ = c; 259*6002Sroot state = TS_DATA; 260*6002Sroot break; 261*6002Sroot 262*6002Sroot case TS_IAC: 263*6002Sroot switch (c) { 264*6002Sroot 265*6002Sroot /* 266*6002Sroot * Send the process on the pty side an 267*6002Sroot * interrupt. Do this with a NULL or 268*6002Sroot * interrupt char; depending on the tty mode. 269*6002Sroot */ 270*6002Sroot case BREAK: 271*6002Sroot case IP: 272*6002Sroot interrupt(); 273*6002Sroot break; 274*6002Sroot 275*6002Sroot /* 276*6002Sroot * Are You There? 277*6002Sroot */ 278*6002Sroot case AYT: 279*6002Sroot *pfrontp++ = BELL; 280*6002Sroot break; 281*6002Sroot 282*6002Sroot /* 283*6002Sroot * Erase Character and 284*6002Sroot * Erase Line 285*6002Sroot */ 286*6002Sroot case EC: 287*6002Sroot case EL: 288*6002Sroot ptyflush(); /* half-hearted */ 289*6002Sroot ioctl(pty, TIOCGETP, &b); 290*6002Sroot *pfrontp++ = (c == EC) ? 291*6002Sroot b.sg_erase : b.sg_kill; 292*6002Sroot break; 293*6002Sroot 294*6002Sroot /* 295*6002Sroot * Check for urgent data... 296*6002Sroot */ 297*6002Sroot case DM: 298*6002Sroot break; 299*6002Sroot 300*6002Sroot /* 301*6002Sroot * Begin option subnegotiation... 302*6002Sroot */ 303*6002Sroot case SB: 304*6002Sroot state = TS_BEGINNEG; 305*6002Sroot continue; 306*6002Sroot 307*6002Sroot case WILL: 308*6002Sroot case WONT: 309*6002Sroot case DO: 310*6002Sroot case DONT: 311*6002Sroot state = TS_WILL + (c - WILL); 312*6002Sroot continue; 313*6002Sroot 314*6002Sroot case IAC: 315*6002Sroot *pfrontp++ = c; 316*6002Sroot break; 317*6002Sroot } 318*6002Sroot state = TS_DATA; 319*6002Sroot break; 320*6002Sroot 321*6002Sroot case TS_BEGINNEG: 322*6002Sroot if (c == IAC) 323*6002Sroot state = TS_ENDNEG; 324*6002Sroot break; 325*6002Sroot 326*6002Sroot case TS_ENDNEG: 327*6002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 328*6002Sroot break; 329*6002Sroot 330*6002Sroot case TS_WILL: 331*6002Sroot if (!hisopts[c]) 332*6002Sroot willoption(c); 333*6002Sroot state = TS_DATA; 334*6002Sroot continue; 335*6002Sroot 336*6002Sroot case TS_WONT: 337*6002Sroot if (hisopts[c]) 338*6002Sroot wontoption(c); 339*6002Sroot state = TS_DATA; 340*6002Sroot continue; 341*6002Sroot 342*6002Sroot case TS_DO: 343*6002Sroot if (!myopts[c]) 344*6002Sroot dooption(c); 345*6002Sroot state = TS_DATA; 346*6002Sroot continue; 347*6002Sroot 348*6002Sroot case TS_DONT: 349*6002Sroot if (myopts[c]) { 350*6002Sroot myopts[c] = 0; 351*6002Sroot sprintf(nfrontp, wont, c); 352*6002Sroot nfrontp += sizeof(wont) - 2; 353*6002Sroot } 354*6002Sroot state = TS_DATA; 355*6002Sroot continue; 356*6002Sroot 357*6002Sroot default: 358*6002Sroot printf("netser: panic state=%d\n", state); 359*6002Sroot exit(1); 360*6002Sroot } 361*6002Sroot } 362*6002Sroot } 363*6002Sroot 364*6002Sroot willoption(option) 365*6002Sroot int option; 366*6002Sroot { 367*6002Sroot char *fmt; 368*6002Sroot 369*6002Sroot switch (option) { 370*6002Sroot 371*6002Sroot case TELOPT_BINARY: 372*6002Sroot mode(RAW, 0); 373*6002Sroot goto common; 374*6002Sroot 375*6002Sroot case TELOPT_ECHO: 376*6002Sroot mode(0, ECHO|CRMOD); 377*6002Sroot /*FALL THRU*/ 378*6002Sroot 379*6002Sroot case TELOPT_SGA: 380*6002Sroot common: 381*6002Sroot hisopts[option] = 1; 382*6002Sroot fmt = doopt; 383*6002Sroot break; 384*6002Sroot 385*6002Sroot case TELOPT_TM: 386*6002Sroot fmt = dont; 387*6002Sroot break; 388*6002Sroot 389*6002Sroot default: 390*6002Sroot fmt = dont; 391*6002Sroot break; 392*6002Sroot } 393*6002Sroot sprintf(nfrontp, dont, option); 394*6002Sroot nfrontp += sizeof(dont) - 2; 395*6002Sroot } 396*6002Sroot 397*6002Sroot wontoption(option) 398*6002Sroot int option; 399*6002Sroot { 400*6002Sroot char *fmt; 401*6002Sroot 402*6002Sroot switch (option) { 403*6002Sroot 404*6002Sroot case TELOPT_ECHO: 405*6002Sroot mode(ECHO|CRMOD, 0); 406*6002Sroot goto common; 407*6002Sroot 408*6002Sroot case TELOPT_BINARY: 409*6002Sroot mode(0, RAW); 410*6002Sroot /*FALL THRU*/ 411*6002Sroot 412*6002Sroot case TELOPT_SGA: 413*6002Sroot common: 414*6002Sroot hisopts[option] = 0; 415*6002Sroot fmt = dont; 416*6002Sroot break; 417*6002Sroot 418*6002Sroot default: 419*6002Sroot fmt = dont; 420*6002Sroot } 421*6002Sroot sprintf(nfrontp, fmt, option); 422*6002Sroot nfrontp += sizeof(doopt) - 2; 423*6002Sroot } 424*6002Sroot 425*6002Sroot dooption(option) 426*6002Sroot int option; 427*6002Sroot { 428*6002Sroot char *fmt; 429*6002Sroot 430*6002Sroot switch (option) { 431*6002Sroot 432*6002Sroot case TELOPT_TM: 433*6002Sroot fmt = wont; 434*6002Sroot break; 435*6002Sroot 436*6002Sroot case TELOPT_ECHO: 437*6002Sroot mode(ECHO|CRMOD, 0); 438*6002Sroot goto common; 439*6002Sroot 440*6002Sroot case TELOPT_BINARY: 441*6002Sroot mode(RAW, 0); 442*6002Sroot /*FALL THRU*/ 443*6002Sroot 444*6002Sroot case TELOPT_SGA: 445*6002Sroot common: 446*6002Sroot fmt = will; 447*6002Sroot break; 448*6002Sroot 449*6002Sroot default: 450*6002Sroot fmt = wont; 451*6002Sroot break; 452*6002Sroot } 453*6002Sroot sprintf(nfrontp, fmt, option); 454*6002Sroot nfrontp += sizeof(doopt) - 2; 455*6002Sroot } 456*6002Sroot 457*6002Sroot mode(on, off) 458*6002Sroot int on, off; 459*6002Sroot { 460*6002Sroot struct sgttyb b; 461*6002Sroot 462*6002Sroot ptyflush(); 463*6002Sroot ioctl(pty, TIOCGETP, &b); 464*6002Sroot b.sg_flags |= on; 465*6002Sroot b.sg_flags &= ~off; 466*6002Sroot ioctl(pty, TIOCSETP, &b); 467*6002Sroot } 468*6002Sroot 469*6002Sroot /* 470*6002Sroot * Send interrupt to process on other side of pty. 471*6002Sroot * If it is in raw mode, just write NULL; 472*6002Sroot * otherwise, write intr char. 473*6002Sroot */ 474*6002Sroot interrupt() 475*6002Sroot { 476*6002Sroot struct sgttyb b; 477*6002Sroot struct tchars tchars; 478*6002Sroot 479*6002Sroot ptyflush(); /* half-hearted */ 480*6002Sroot ioctl(pty, TIOCGETP, &b); 481*6002Sroot if (b.sg_flags & RAW) { 482*6002Sroot *pfrontp++ = '\0'; 483*6002Sroot return; 484*6002Sroot } 485*6002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 486*6002Sroot '\177' : tchars.t_intrc; 487*6002Sroot } 488*6002Sroot 489*6002Sroot ptyflush() 490*6002Sroot { 491*6002Sroot int n; 492*6002Sroot 493*6002Sroot if ((n = pfrontp - pbackp) > 0) 494*6002Sroot n = write(pty, pbackp, n); 495*6002Sroot if (n < 0 && errno == EWOULDBLOCK) 496*6002Sroot n = 0; 497*6002Sroot pbackp += n; 498*6002Sroot if (pbackp == pfrontp) 499*6002Sroot pbackp = pfrontp = ptyobuf; 500*6002Sroot } 501*6002Sroot 502*6002Sroot netflush() 503*6002Sroot { 504*6002Sroot int n; 505*6002Sroot 506*6002Sroot if ((n = nfrontp - nbackp) > 0) 507*6002Sroot n = write(net, nbackp, n); 508*6002Sroot if (n < 0 && errno == EWOULDBLOCK) 509*6002Sroot n = 0; 510*6002Sroot nbackp += n; 511*6002Sroot if (nbackp == nfrontp) 512*6002Sroot nbackp = nfrontp = netobuf; 513*6002Sroot } 514*6002Sroot 515*6002Sroot cleanup() 516*6002Sroot { 517*6002Sroot int how = 2; 518*6002Sroot 519*6002Sroot rmut(); 520*6002Sroot vhangup(); 521*6002Sroot ioctl(net, SIOCDONE, &how); 522*6002Sroot kill(0, SIGKILL); 523*6002Sroot exit(1); 524*6002Sroot } 525*6002Sroot 526*6002Sroot #include <utmp.h> 527*6002Sroot 528*6002Sroot struct utmp wtmp; 529*6002Sroot char wtmpf[] = "/usr/adm/wtmp"; 530*6002Sroot char utmp[] = "/etc/utmp"; 531*6002Sroot #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 532*6002Sroot #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 533*6002Sroot 534*6002Sroot rmut() 535*6002Sroot { 536*6002Sroot register f; 537*6002Sroot int found = 0; 538*6002Sroot 539*6002Sroot f = open(utmp, 2); 540*6002Sroot if (f >= 0) { 541*6002Sroot while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) { 542*6002Sroot if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 543*6002Sroot continue; 544*6002Sroot lseek(f, -(long)sizeof(wtmp), 1); 545*6002Sroot SCPYN(wtmp.ut_name, ""); 546*6002Sroot time(&wtmp.ut_time); 547*6002Sroot write(f, (char *)&wtmp, sizeof(wtmp)); 548*6002Sroot found++; 549*6002Sroot } 550*6002Sroot close(f); 551*6002Sroot } 552*6002Sroot if (found) { 553*6002Sroot f = open(wtmpf, 1); 554*6002Sroot if (f >= 0) { 555*6002Sroot SCPYN(wtmp.ut_line, line+5); 556*6002Sroot SCPYN(wtmp.ut_name, ""); 557*6002Sroot time(&wtmp.ut_time); 558*6002Sroot lseek(f, (long)0, 2); 559*6002Sroot write(f, (char *)&wtmp, sizeof(wtmp)); 560*6002Sroot close(f); 561*6002Sroot } 562*6002Sroot } 563*6002Sroot chmod(line, 0666); 564*6002Sroot chown(line, 0, 0); 565*6002Sroot line[strlen("/dev/")] = 'p'; 566*6002Sroot chmod(line, 0666); 567*6002Sroot chown(line, 0, 0); 568*6002Sroot } 569