121182Sdist /* 221182Sdist * Copyright (c) 1983 Regents of the University of California. 321182Sdist * All rights reserved. The Berkeley software License Agreement 421182Sdist * specifies the terms and conditions for redistribution. 521182Sdist */ 621182Sdist 76295Sroot #ifndef lint 821182Sdist char copyright[] = 921182Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1021182Sdist All rights reserved.\n"; 1121182Sdist #endif not lint 126295Sroot 1321182Sdist #ifndef lint 14*27185Sminshall static char sccsid[] = "@(#)telnetd.c 5.9 (Berkeley) 04/19/86"; 1521182Sdist #endif not lint 1621182Sdist 176002Sroot /* 186002Sroot * Stripped-down telnet server. 196002Sroot */ 209218Ssam #include <sys/types.h> 219218Ssam #include <sys/socket.h> 2213608Ssam #include <sys/wait.h> 2317583Ssam #include <sys/file.h> 2420188Skarels #include <sys/stat.h> 25*27185Sminshall #include <sys/time.h> 269218Ssam 279218Ssam #include <netinet/in.h> 289218Ssam 2912216Ssam #include <arpa/telnet.h> 3012216Ssam 316002Sroot #include <stdio.h> 326002Sroot #include <signal.h> 336002Sroot #include <errno.h> 346002Sroot #include <sgtty.h> 358346Ssam #include <netdb.h> 3617187Sralph #include <syslog.h> 379218Ssam 3813798Ssam #define BELL '\07' 3923567Sbloom #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s" 406002Sroot 416002Sroot char hisopts[256]; 426002Sroot char myopts[256]; 436002Sroot 446002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 456002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 466002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 476002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 486002Sroot 496002Sroot /* 506002Sroot * I/O data buffers, pointers, and counters. 516002Sroot */ 526002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 536002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 546002Sroot char netibuf[BUFSIZ], *netip = netibuf; 556388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 56*27185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 576002Sroot int pcc, ncc; 586002Sroot 596002Sroot int pty, net; 606002Sroot int inter; 6113799Ssam extern char **environ; 626002Sroot extern int errno; 6320188Skarels char *line; 64*27185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 65*27185Sminshall /* 66*27185Sminshall * The following are some clocks used to decide how to interpret 67*27185Sminshall * the relationship between various variables. 68*27185Sminshall */ 696002Sroot 70*27185Sminshall struct { 71*27185Sminshall int 72*27185Sminshall system, /* what the current time is */ 73*27185Sminshall echotoggle, /* last time user entered echo character */ 74*27185Sminshall modenegotiated, /* last time operating mode negotiated */ 75*27185Sminshall didnetreceive, /* last time we read data from network */ 76*27185Sminshall gotDM; /* when did we last see a data mark */ 77*27185Sminshall } clocks; 78*27185Sminshall 79*27185Sminshall #define settimer(x) clocks.x = clocks.system++ 80*27185Sminshall 816002Sroot main(argc, argv) 826002Sroot char *argv[]; 836002Sroot { 8416371Skarels struct sockaddr_in from; 8517156Ssam int on = 1, fromlen; 866002Sroot 87*27185Sminshall #if defined(DEBUG) 88*27185Sminshall { 89*27185Sminshall int s, ns, foo; 90*27185Sminshall struct servent *sp; 91*27185Sminshall static struct sockaddr_in sin = { AF_INET }; 92*27185Sminshall 93*27185Sminshall sp = getservbyname("telnet", "tcp"); 94*27185Sminshall if (sp == 0) { 95*27185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 96*27185Sminshall exit(1); 97*27185Sminshall } 98*27185Sminshall sin.sin_port = sp->s_port; 99*27185Sminshall argc--, argv++; 100*27185Sminshall if (argc > 0) { 101*27185Sminshall sin.sin_port = atoi(*argv); 102*27185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 103*27185Sminshall } 104*27185Sminshall 105*27185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 106*27185Sminshall if (s < 0) { 107*27185Sminshall perror("telnetd: socket");; 108*27185Sminshall exit(1); 109*27185Sminshall } 110*27185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 111*27185Sminshall perror("bind"); 112*27185Sminshall exit(1); 113*27185Sminshall } 114*27185Sminshall if (listen(s, 1) < 0) { 115*27185Sminshall perror("listen"); 116*27185Sminshall exit(1); 117*27185Sminshall } 118*27185Sminshall foo = sizeof sin; 119*27185Sminshall ns = accept(s, &sin, &foo); 120*27185Sminshall if (ns < 0) { 121*27185Sminshall perror("accept"); 122*27185Sminshall exit(1); 123*27185Sminshall } 124*27185Sminshall dup2(ns, 0); 125*27185Sminshall close(s); 126*27185Sminshall } 127*27185Sminshall #endif /* defined(DEBUG) */ 12824855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 12916371Skarels fromlen = sizeof (from); 13016371Skarels if (getpeername(0, &from, &fromlen) < 0) { 13116371Skarels fprintf(stderr, "%s: ", argv[0]); 13216371Skarels perror("getpeername"); 13316371Skarels _exit(1); 1348346Ssam } 13517156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 13617187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 13710418Ssam } 13816371Skarels doit(0, &from); 1396002Sroot } 1406002Sroot 14113799Ssam char *envinit[] = { "TERM=network", 0 }; 1426002Sroot int cleanup(); 1436002Sroot 1446002Sroot /* 1456002Sroot * Get a pty, scan input lines. 1466002Sroot */ 14712683Ssam doit(f, who) 14812683Ssam int f; 14912683Ssam struct sockaddr_in *who; 1506002Sroot { 15120188Skarels char *host, *inet_ntoa(); 15217583Ssam int i, p, t; 1536002Sroot struct sgttyb b; 15412683Ssam struct hostent *hp; 15520188Skarels char c; 1566002Sroot 15720188Skarels for (c = 'p'; c <= 's'; c++) { 15820188Skarels struct stat stb; 15920188Skarels 16020188Skarels line = "/dev/ptyXX"; 16120188Skarels line[strlen("/dev/pty")] = c; 16220188Skarels line[strlen("/dev/ptyp")] = '0'; 16320188Skarels if (stat(line, &stb) < 0) 16420188Skarels break; 16517583Ssam for (i = 0; i < 16; i++) { 16620188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 16720188Skarels p = open(line, 2); 16817583Ssam if (p > 0) 16917583Ssam goto gotpty; 17017583Ssam } 1716002Sroot } 1729244Ssam fatal(f, "All network ports in use"); 1739244Ssam /*NOTREACHED*/ 1746002Sroot gotpty: 1756002Sroot dup2(f, 0); 17620188Skarels line[strlen("/dev/")] = 't'; 17717583Ssam t = open("/dev/tty", O_RDWR); 1786002Sroot if (t >= 0) { 1796002Sroot ioctl(t, TIOCNOTTY, 0); 1806002Sroot close(t); 1816002Sroot } 18220188Skarels t = open(line, O_RDWR); 1839244Ssam if (t < 0) 18420188Skarels fatalperror(f, line, errno); 1856002Sroot ioctl(t, TIOCGETP, &b); 1866388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 1876002Sroot ioctl(t, TIOCSETP, &b); 1886388Ssam ioctl(p, TIOCGETP, &b); 1898379Ssam b.sg_flags &= ~ECHO; 1906388Ssam ioctl(p, TIOCSETP, &b); 19112683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 19212683Ssam who->sin_family); 19312683Ssam if (hp) 19412683Ssam host = hp->h_name; 19512683Ssam else 19617444Sralph host = inet_ntoa(who->sin_addr); 1979244Ssam if ((i = fork()) < 0) 1989244Ssam fatalperror(f, "fork", errno); 1996002Sroot if (i) 2006002Sroot telnet(f, p); 2016002Sroot close(f); 2026002Sroot close(p); 2036002Sroot dup2(t, 0); 2046002Sroot dup2(t, 1); 2056002Sroot dup2(t, 2); 2066002Sroot close(t); 20713799Ssam environ = envinit; 20812713Ssam execl("/bin/login", "login", "-h", host, 0); 2099244Ssam fatalperror(f, "/bin/login", errno); 2109244Ssam /*NOTREACHED*/ 2119244Ssam } 2129244Ssam 2139244Ssam fatal(f, msg) 2149244Ssam int f; 2159244Ssam char *msg; 2169244Ssam { 2179244Ssam char buf[BUFSIZ]; 2189244Ssam 21917583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 2209244Ssam (void) write(f, buf, strlen(buf)); 2216002Sroot exit(1); 2226002Sroot } 2236002Sroot 2249244Ssam fatalperror(f, msg, errno) 2259244Ssam int f; 2269244Ssam char *msg; 2279244Ssam int errno; 2289244Ssam { 2299244Ssam char buf[BUFSIZ]; 2309244Ssam extern char *sys_errlist[]; 2319244Ssam 23217583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 2339244Ssam fatal(f, buf); 2349244Ssam } 2359244Ssam 236*27185Sminshall 2376002Sroot /* 238*27185Sminshall * Check a descriptor to see if out of band data exists on it. 239*27185Sminshall */ 240*27185Sminshall 241*27185Sminshall 242*27185Sminshall stilloob(s) 243*27185Sminshall int s; /* socket number */ 244*27185Sminshall { 245*27185Sminshall static struct timeval timeout = { 0 }; 246*27185Sminshall fd_set excepts; 247*27185Sminshall int value; 248*27185Sminshall 249*27185Sminshall do { 250*27185Sminshall FD_ZERO(&excepts); 251*27185Sminshall FD_SET(s, &excepts); 252*27185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 253*27185Sminshall } while ((value == -1) && (errno = EINTR)); 254*27185Sminshall 255*27185Sminshall if (value < 0) { 256*27185Sminshall fatalperror(pty, "select", errno); 257*27185Sminshall } 258*27185Sminshall if (FD_ISSET(s, &excepts)) { 259*27185Sminshall return 1; 260*27185Sminshall } else { 261*27185Sminshall return 0; 262*27185Sminshall } 263*27185Sminshall } 264*27185Sminshall 265*27185Sminshall /* 2666002Sroot * Main loop. Select from pty and network, and 2676002Sroot * hand data to telnet receiver finite state machine. 2686002Sroot */ 2696002Sroot telnet(f, p) 2706002Sroot { 2716002Sroot int on = 1; 27212713Ssam char hostname[32]; 2736002Sroot 2746002Sroot net = f, pty = p; 2756002Sroot ioctl(f, FIONBIO, &on); 2766002Sroot ioctl(p, FIONBIO, &on); 2776002Sroot signal(SIGTSTP, SIG_IGN); 27813028Ssam signal(SIGCHLD, cleanup); 27926083Slepreau setpgrp(0, 0); 2806002Sroot 2818379Ssam /* 282*27185Sminshall * Request to do remote echo and to suppress go ahead. 2838379Ssam */ 2848379Ssam dooption(TELOPT_ECHO); 285*27185Sminshall dooption(TELOPT_SGA); 28612713Ssam /* 28712713Ssam * Show banner that getty never gave. 28812713Ssam */ 28912713Ssam gethostname(hostname, sizeof (hostname)); 29012713Ssam sprintf(nfrontp, BANNER, hostname, ""); 29112713Ssam nfrontp += strlen(nfrontp); 2926002Sroot for (;;) { 293*27185Sminshall fd_set ibits, obits, xbits; 2946002Sroot register int c; 2956002Sroot 296*27185Sminshall if (ncc < 0 && pcc < 0) 297*27185Sminshall break; 298*27185Sminshall 299*27185Sminshall FD_ZERO(&ibits); 300*27185Sminshall FD_ZERO(&obits); 301*27185Sminshall FD_ZERO(&xbits); 3026002Sroot /* 3036002Sroot * Never look for input if there's still 3046002Sroot * stuff in the corresponding output buffer 3056002Sroot */ 306*27185Sminshall if (nfrontp - nbackp || pcc > 0) { 307*27185Sminshall FD_SET(f, &obits); 308*27185Sminshall } else { 309*27185Sminshall FD_SET(p, &ibits); 310*27185Sminshall } 311*27185Sminshall if (pfrontp - pbackp || ncc > 0) { 312*27185Sminshall FD_SET(p, &obits); 313*27185Sminshall } else { 314*27185Sminshall FD_SET(f, &ibits); 315*27185Sminshall } 316*27185Sminshall if (!SYNCHing) { 317*27185Sminshall FD_SET(f, &xbits); 318*27185Sminshall } 319*27185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 320*27185Sminshall (struct timeval *)0)) < 1) { 321*27185Sminshall if (c == -1) { 322*27185Sminshall if (errno == EINTR) { 323*27185Sminshall continue; 324*27185Sminshall } 325*27185Sminshall } 3266002Sroot sleep(5); 3276002Sroot continue; 3286002Sroot } 3296002Sroot 3306002Sroot /* 331*27185Sminshall * Any urgent data? 332*27185Sminshall */ 333*27185Sminshall if (FD_ISSET(net, &xbits)) { 334*27185Sminshall SYNCHing = 1; 335*27185Sminshall } 336*27185Sminshall 337*27185Sminshall /* 3386002Sroot * Something to read from the network... 3396002Sroot */ 340*27185Sminshall if (FD_ISSET(net, &ibits)) { 341*27185Sminshall #if !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) 342*27185Sminshall /* 343*27185Sminshall * In 4.2 (and some early 4.3) systems, the 344*27185Sminshall * OOB indication and data handling in the kernel 345*27185Sminshall * is such that if two separate TCP Urgent requests 346*27185Sminshall * come in, one byte of TCP data will be overlaid. 347*27185Sminshall * This is fatal for Telnet, but we try to live 348*27185Sminshall * with it. 349*27185Sminshall * 350*27185Sminshall * In addition, in 4.2 (and...), a special protocol 351*27185Sminshall * is needed to pick up the TCP Urgent data in 352*27185Sminshall * the correct sequence. 353*27185Sminshall * 354*27185Sminshall * What we do is: if we think we are in urgent 355*27185Sminshall * mode, we look to see if we are "at the mark". 356*27185Sminshall * If we are, we do an OOB receive. If we run 357*27185Sminshall * this twice, we will do the OOB receive twice, 358*27185Sminshall * but the second will fail, since the second 359*27185Sminshall * time we were "at the mark", but there wasn't 360*27185Sminshall * any data there (the kernel doesn't reset 361*27185Sminshall * "at the mark" until we do a normal read). 362*27185Sminshall * Once we've read the OOB data, we go ahead 363*27185Sminshall * and do normal reads. 364*27185Sminshall * 365*27185Sminshall * There is also another problem, which is that 366*27185Sminshall * since the OOB byte we read doesn't put us 367*27185Sminshall * out of OOB state, and since that byte is most 368*27185Sminshall * likely the TELNET DM (data mark), we would 369*27185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 370*27185Sminshall * So, clocks to the rescue. If we've "just" 371*27185Sminshall * received a DM, then we test for the 372*27185Sminshall * presence of OOB data when the receive OOB 373*27185Sminshall * fails (and AFTER we did the normal mode read 374*27185Sminshall * to clear "at the mark"). 375*27185Sminshall */ 376*27185Sminshall if (SYNCHing) { 377*27185Sminshall int atmark; 378*27185Sminshall 379*27185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 380*27185Sminshall if (atmark) { 381*27185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 382*27185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 383*27185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 384*27185Sminshall if (clocks.didnetreceive < clocks.gotDM) { 385*27185Sminshall SYNCHing = stilloob(net); 386*27185Sminshall } 387*27185Sminshall } 388*27185Sminshall } else { 389*27185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 3906002Sroot } 391*27185Sminshall } else { 392*27185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 393*27185Sminshall } 394*27185Sminshall settimer(didnetreceive); 395*27185Sminshall #else /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ 396*27185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 397*27185Sminshall #endif /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ 398*27185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 399*27185Sminshall ncc = 0; 400*27185Sminshall else { 401*27185Sminshall if (ncc <= 0) { 402*27185Sminshall break; 403*27185Sminshall } 404*27185Sminshall netip = netibuf; 405*27185Sminshall } 4066002Sroot } 4076002Sroot 4086002Sroot /* 4096002Sroot * Something to read from the pty... 4106002Sroot */ 411*27185Sminshall if (FD_ISSET(p, &ibits)) { 4126002Sroot pcc = read(p, ptyibuf, BUFSIZ); 4136002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 4146002Sroot pcc = 0; 4156002Sroot else { 4166002Sroot if (pcc <= 0) 4176002Sroot break; 4186002Sroot ptyip = ptyibuf; 4196002Sroot } 4206002Sroot } 4216002Sroot 4226002Sroot while (pcc > 0) { 4236002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 4246002Sroot break; 4256002Sroot c = *ptyip++ & 0377, pcc--; 4266002Sroot if (c == IAC) 4276002Sroot *nfrontp++ = c; 4286002Sroot *nfrontp++ = c; 42927020Sminshall if (c == '\r') { 43027020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 43127020Sminshall *nfrontp++ = *ptyip++ & 0377; 43227020Sminshall pcc--; 43327020Sminshall } else 43427020Sminshall *nfrontp++ = '\0'; 43527020Sminshall } 4366002Sroot } 437*27185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 4386002Sroot netflush(); 4396002Sroot if (ncc > 0) 4406002Sroot telrcv(); 441*27185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 4426002Sroot ptyflush(); 4436002Sroot } 4446002Sroot cleanup(); 4456002Sroot } 4466002Sroot 4476002Sroot /* 4486002Sroot * State for recv fsm 4496002Sroot */ 4506002Sroot #define TS_DATA 0 /* base state */ 4516002Sroot #define TS_IAC 1 /* look for double IAC's */ 4526002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 4536002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 4546002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 4556002Sroot #define TS_WILL 5 /* will option negotiation */ 4566002Sroot #define TS_WONT 6 /* wont " */ 4576002Sroot #define TS_DO 7 /* do " */ 4586002Sroot #define TS_DONT 8 /* dont " */ 4596002Sroot 4606002Sroot telrcv() 4616002Sroot { 4626002Sroot register int c; 4636002Sroot static int state = TS_DATA; 4646002Sroot 4656002Sroot while (ncc > 0) { 4666002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 4676002Sroot return; 4686002Sroot c = *netip++ & 0377, ncc--; 4696002Sroot switch (state) { 4706002Sroot 47126090Sminshall case TS_CR: 47226090Sminshall state = TS_DATA; 47326499Sminshall if ((c == 0) || (c == '\n')) { 47426090Sminshall break; 47526499Sminshall } 47626090Sminshall /* FALL THROUGH */ 47726090Sminshall 4786002Sroot case TS_DATA: 4796002Sroot if (c == IAC) { 4806002Sroot state = TS_IAC; 4816002Sroot break; 4826002Sroot } 4836002Sroot if (inter > 0) 4846002Sroot break; 48527020Sminshall /* 48627020Sminshall * We map \r\n ==> \n, since \r\n says 48727020Sminshall * that we want to be in column 1 of the next 48827020Sminshall * printable line, and \n is the standard 48927020Sminshall * unix way of saying that (\r is only good 49027020Sminshall * if CRMOD is set, which it normally is). 49127020Sminshall */ 49226499Sminshall if (!myopts[TELOPT_BINARY] && c == '\r') { 49327020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 49427020Sminshall netip++; ncc--; 49527020Sminshall c = '\n'; 49627020Sminshall } else { 49727020Sminshall state = TS_CR; 49827020Sminshall } 49926499Sminshall } 50026499Sminshall *pfrontp++ = c; 5016002Sroot break; 5026002Sroot 5036002Sroot case TS_IAC: 5046002Sroot switch (c) { 5056002Sroot 5066002Sroot /* 5076002Sroot * Send the process on the pty side an 5086002Sroot * interrupt. Do this with a NULL or 5096002Sroot * interrupt char; depending on the tty mode. 5106002Sroot */ 5116002Sroot case BREAK: 5126002Sroot case IP: 5136002Sroot interrupt(); 5146002Sroot break; 5156002Sroot 5166002Sroot /* 5176002Sroot * Are You There? 5186002Sroot */ 5196002Sroot case AYT: 52017583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 52117583Ssam nfrontp += 9; 5226002Sroot break; 5236002Sroot 5246002Sroot /* 525*27185Sminshall * Abort Output 526*27185Sminshall */ 527*27185Sminshall case AO: { 528*27185Sminshall struct ltchars tmpltc; 529*27185Sminshall 530*27185Sminshall ptyflush(); /* half-hearted */ 531*27185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 532*27185Sminshall if (tmpltc.t_flushc != '\377') { 533*27185Sminshall *pfrontp++ = tmpltc.t_flushc; 534*27185Sminshall } 535*27185Sminshall *nfrontp++ = IAC; 536*27185Sminshall *nfrontp++ = DM; 537*27185Sminshall neturg = nfrontp; 538*27185Sminshall break; 539*27185Sminshall } 540*27185Sminshall 541*27185Sminshall /* 5426002Sroot * Erase Character and 5436002Sroot * Erase Line 5446002Sroot */ 5456002Sroot case EC: 546*27185Sminshall case EL: { 547*27185Sminshall struct sgttyb b; 548*27185Sminshall char ch; 5496002Sroot 550*27185Sminshall ptyflush(); /* half-hearted */ 551*27185Sminshall ioctl(pty, TIOCGETP, &b); 552*27185Sminshall ch = (c == EC) ? 553*27185Sminshall b.sg_erase : b.sg_kill; 554*27185Sminshall if (ch != '\377') { 555*27185Sminshall *pfrontp++ = ch; 556*27185Sminshall } 557*27185Sminshall break; 558*27185Sminshall } 559*27185Sminshall 5606002Sroot /* 5616002Sroot * Check for urgent data... 5626002Sroot */ 5636002Sroot case DM: 564*27185Sminshall SYNCHing = stilloob(net); 565*27185Sminshall settimer(gotDM); 5666002Sroot break; 5676002Sroot 568*27185Sminshall 5696002Sroot /* 5706002Sroot * Begin option subnegotiation... 5716002Sroot */ 5726002Sroot case SB: 5736002Sroot state = TS_BEGINNEG; 5746002Sroot continue; 5756002Sroot 5766002Sroot case WILL: 5776002Sroot case WONT: 5786002Sroot case DO: 5796002Sroot case DONT: 5806002Sroot state = TS_WILL + (c - WILL); 5816002Sroot continue; 5826002Sroot 5836002Sroot case IAC: 5846002Sroot *pfrontp++ = c; 5856002Sroot break; 5866002Sroot } 5876002Sroot state = TS_DATA; 5886002Sroot break; 5896002Sroot 5906002Sroot case TS_BEGINNEG: 5916002Sroot if (c == IAC) 5926002Sroot state = TS_ENDNEG; 5936002Sroot break; 5946002Sroot 5956002Sroot case TS_ENDNEG: 5966002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 5976002Sroot break; 5986002Sroot 5996002Sroot case TS_WILL: 6006002Sroot if (!hisopts[c]) 6016002Sroot willoption(c); 6026002Sroot state = TS_DATA; 6036002Sroot continue; 6046002Sroot 6056002Sroot case TS_WONT: 6066002Sroot if (hisopts[c]) 6076002Sroot wontoption(c); 6086002Sroot state = TS_DATA; 6096002Sroot continue; 6106002Sroot 6116002Sroot case TS_DO: 6126002Sroot if (!myopts[c]) 6136002Sroot dooption(c); 6146002Sroot state = TS_DATA; 6156002Sroot continue; 6166002Sroot 6176002Sroot case TS_DONT: 6186002Sroot if (myopts[c]) { 6196002Sroot myopts[c] = 0; 6206002Sroot sprintf(nfrontp, wont, c); 6218379Ssam nfrontp += sizeof (wont) - 2; 6226002Sroot } 6236002Sroot state = TS_DATA; 6246002Sroot continue; 6256002Sroot 6266002Sroot default: 6279218Ssam printf("telnetd: panic state=%d\n", state); 6286002Sroot exit(1); 6296002Sroot } 6306002Sroot } 6316002Sroot } 6326002Sroot 6336002Sroot willoption(option) 6346002Sroot int option; 6356002Sroot { 6366002Sroot char *fmt; 6376002Sroot 6386002Sroot switch (option) { 6396002Sroot 6406002Sroot case TELOPT_BINARY: 6416002Sroot mode(RAW, 0); 6426002Sroot goto common; 6436002Sroot 6446002Sroot case TELOPT_ECHO: 6456002Sroot mode(0, ECHO|CRMOD); 6466002Sroot /*FALL THRU*/ 6476002Sroot 6486002Sroot case TELOPT_SGA: 6496002Sroot common: 6506002Sroot hisopts[option] = 1; 6516002Sroot fmt = doopt; 6526002Sroot break; 6536002Sroot 6546002Sroot case TELOPT_TM: 6556002Sroot fmt = dont; 6566002Sroot break; 6576002Sroot 6586002Sroot default: 6596002Sroot fmt = dont; 6606002Sroot break; 6616002Sroot } 6626023Ssam sprintf(nfrontp, fmt, option); 6638379Ssam nfrontp += sizeof (dont) - 2; 6646002Sroot } 6656002Sroot 6666002Sroot wontoption(option) 6676002Sroot int option; 6686002Sroot { 6696002Sroot char *fmt; 6706002Sroot 6716002Sroot switch (option) { 6726002Sroot 6736002Sroot case TELOPT_ECHO: 6746002Sroot mode(ECHO|CRMOD, 0); 6756002Sroot goto common; 6766002Sroot 6776002Sroot case TELOPT_BINARY: 6786002Sroot mode(0, RAW); 6796002Sroot /*FALL THRU*/ 6806002Sroot 6816002Sroot case TELOPT_SGA: 6826002Sroot common: 6836002Sroot hisopts[option] = 0; 6846002Sroot fmt = dont; 6856002Sroot break; 6866002Sroot 6876002Sroot default: 6886002Sroot fmt = dont; 6896002Sroot } 6906002Sroot sprintf(nfrontp, fmt, option); 6918379Ssam nfrontp += sizeof (doopt) - 2; 6926002Sroot } 6936002Sroot 6946002Sroot dooption(option) 6956002Sroot int option; 6966002Sroot { 6976002Sroot char *fmt; 6986002Sroot 6996002Sroot switch (option) { 7006002Sroot 7016002Sroot case TELOPT_TM: 7026002Sroot fmt = wont; 7036002Sroot break; 7046002Sroot 7056002Sroot case TELOPT_ECHO: 7066002Sroot mode(ECHO|CRMOD, 0); 7076002Sroot goto common; 7086002Sroot 7096002Sroot case TELOPT_BINARY: 7106002Sroot mode(RAW, 0); 7116002Sroot /*FALL THRU*/ 7126002Sroot 7136002Sroot case TELOPT_SGA: 7146002Sroot common: 7156002Sroot fmt = will; 7166002Sroot break; 7176002Sroot 7186002Sroot default: 7196002Sroot fmt = wont; 7206002Sroot break; 7216002Sroot } 7226002Sroot sprintf(nfrontp, fmt, option); 7238379Ssam nfrontp += sizeof (doopt) - 2; 7246002Sroot } 7256002Sroot 7266002Sroot mode(on, off) 7276002Sroot int on, off; 7286002Sroot { 7296002Sroot struct sgttyb b; 7306002Sroot 7316002Sroot ptyflush(); 7326002Sroot ioctl(pty, TIOCGETP, &b); 7336002Sroot b.sg_flags |= on; 7346002Sroot b.sg_flags &= ~off; 7356002Sroot ioctl(pty, TIOCSETP, &b); 7366002Sroot } 7376002Sroot 7386002Sroot /* 7396002Sroot * Send interrupt to process on other side of pty. 7406002Sroot * If it is in raw mode, just write NULL; 7416002Sroot * otherwise, write intr char. 7426002Sroot */ 7436002Sroot interrupt() 7446002Sroot { 7456002Sroot struct sgttyb b; 7466002Sroot struct tchars tchars; 7476002Sroot 7486002Sroot ptyflush(); /* half-hearted */ 7496002Sroot ioctl(pty, TIOCGETP, &b); 7506002Sroot if (b.sg_flags & RAW) { 7516002Sroot *pfrontp++ = '\0'; 7526002Sroot return; 7536002Sroot } 7546002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 7556002Sroot '\177' : tchars.t_intrc; 7566002Sroot } 7576002Sroot 7586002Sroot ptyflush() 7596002Sroot { 7606002Sroot int n; 7616002Sroot 7626002Sroot if ((n = pfrontp - pbackp) > 0) 7636002Sroot n = write(pty, pbackp, n); 7648346Ssam if (n < 0) 7658346Ssam return; 7666002Sroot pbackp += n; 7676002Sroot if (pbackp == pfrontp) 7686002Sroot pbackp = pfrontp = ptyobuf; 7696002Sroot } 7706002Sroot 771*27185Sminshall #if 0 7726002Sroot netflush() 7736002Sroot { 7746002Sroot int n; 7756002Sroot 7766002Sroot if ((n = nfrontp - nbackp) > 0) 7776002Sroot n = write(net, nbackp, n); 7788346Ssam if (n < 0) { 7798346Ssam if (errno == EWOULDBLOCK) 7808346Ssam return; 7818346Ssam /* should blow this guy away... */ 7828346Ssam return; 7838346Ssam } 7846002Sroot nbackp += n; 7856002Sroot if (nbackp == nfrontp) 7866002Sroot nbackp = nfrontp = netobuf; 7876002Sroot } 788*27185Sminshall #else /* 0 */ 7896002Sroot 790*27185Sminshall 791*27185Sminshall /* 792*27185Sminshall * netflush 793*27185Sminshall * Send as much data as possible to the network, 794*27185Sminshall * handling requests for urgent data. 795*27185Sminshall */ 796*27185Sminshall 797*27185Sminshall 798*27185Sminshall netflush() 799*27185Sminshall { 800*27185Sminshall int n; 801*27185Sminshall 802*27185Sminshall if ((n = nfrontp - nbackp) > 0) { 803*27185Sminshall if (!neturg) { 804*27185Sminshall n = write(net, nbackp, n); /* normal write */ 805*27185Sminshall } else { 806*27185Sminshall n = neturg - nbackp; 807*27185Sminshall /* 808*27185Sminshall * In 4.2 (and 4.3) systems, there is some question about 809*27185Sminshall * what byte in a sendOOB operation is the "OOB" data. 810*27185Sminshall * To make ourselves compatible, we only send ONE byte 811*27185Sminshall * out of band, the one WE THINK should be OOB (though 812*27185Sminshall * we really have more the TCP philosophy of urgent data 813*27185Sminshall * rather than the Unix philosophy of OOB data). 814*27185Sminshall */ 815*27185Sminshall if (n > 1) { 816*27185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 817*27185Sminshall } else { 818*27185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 819*27185Sminshall } 820*27185Sminshall } 821*27185Sminshall } 822*27185Sminshall if (n < 0) { 823*27185Sminshall if (errno == EWOULDBLOCK) 824*27185Sminshall return; 825*27185Sminshall /* should blow this guy away... */ 826*27185Sminshall return; 827*27185Sminshall } 828*27185Sminshall nbackp += n; 829*27185Sminshall if (nbackp >= neturg) { 830*27185Sminshall neturg = 0; 831*27185Sminshall } 832*27185Sminshall if (nbackp == nfrontp) { 833*27185Sminshall nbackp = nfrontp = netobuf; 834*27185Sminshall } 835*27185Sminshall } 836*27185Sminshall #endif /* 0 */ 837*27185Sminshall 8386002Sroot cleanup() 8396002Sroot { 8406002Sroot 8416002Sroot rmut(); 84210008Ssam vhangup(); /* XXX */ 84310191Ssam shutdown(net, 2); 8446002Sroot exit(1); 8456002Sroot } 8466002Sroot 8476002Sroot #include <utmp.h> 8486002Sroot 8496002Sroot struct utmp wtmp; 8506002Sroot char wtmpf[] = "/usr/adm/wtmp"; 85123567Sbloom char utmpf[] = "/etc/utmp"; 85223567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 85323567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 8546002Sroot 8556002Sroot rmut() 8566002Sroot { 8576002Sroot register f; 8586002Sroot int found = 0; 85923567Sbloom struct utmp *u, *utmp; 86023567Sbloom int nutmp; 86123567Sbloom struct stat statbf; 8626002Sroot 86323567Sbloom f = open(utmpf, O_RDWR); 8646002Sroot if (f >= 0) { 86523567Sbloom fstat(f, &statbf); 86623567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 86723567Sbloom if (!utmp) 86823567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 86923567Sbloom if (statbf.st_size && utmp) { 87023567Sbloom nutmp = read(f, utmp, statbf.st_size); 87123567Sbloom nutmp /= sizeof(struct utmp); 87223567Sbloom 87323567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 87423567Sbloom if (SCMPN(u->ut_line, line+5) || 87523567Sbloom u->ut_name[0]==0) 87623567Sbloom continue; 87723567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 87823567Sbloom SCPYN(u->ut_name, ""); 87923567Sbloom SCPYN(u->ut_host, ""); 88023567Sbloom time(&u->ut_time); 88123567Sbloom write(f, (char *)u, sizeof(wtmp)); 88223567Sbloom found++; 88323567Sbloom } 8846002Sroot } 8856002Sroot close(f); 8866002Sroot } 8876002Sroot if (found) { 88817583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 8896002Sroot if (f >= 0) { 8906002Sroot SCPYN(wtmp.ut_line, line+5); 8916002Sroot SCPYN(wtmp.ut_name, ""); 89212683Ssam SCPYN(wtmp.ut_host, ""); 8936002Sroot time(&wtmp.ut_time); 89423567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 8956002Sroot close(f); 8966002Sroot } 8976002Sroot } 8986002Sroot chmod(line, 0666); 8996002Sroot chown(line, 0, 0); 9006002Sroot line[strlen("/dev/")] = 'p'; 9016002Sroot chmod(line, 0666); 9026002Sroot chown(line, 0, 0); 9036002Sroot } 904