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*27229Sminshall static char sccsid[] = "@(#)telnetd.c 5.12 (Berkeley) 04/20/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> 2527185Sminshall #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; 5627185Sminshall 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; 6427185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 6527185Sminshall /* 6627185Sminshall * The following are some clocks used to decide how to interpret 6727185Sminshall * the relationship between various variables. 6827185Sminshall */ 696002Sroot 7027185Sminshall struct { 7127185Sminshall int 7227185Sminshall system, /* what the current time is */ 7327185Sminshall echotoggle, /* last time user entered echo character */ 7427185Sminshall modenegotiated, /* last time operating mode negotiated */ 7527185Sminshall didnetreceive, /* last time we read data from network */ 7627185Sminshall gotDM; /* when did we last see a data mark */ 7727185Sminshall } clocks; 7827185Sminshall 7927185Sminshall #define settimer(x) clocks.x = clocks.system++ 8027185Sminshall 816002Sroot main(argc, argv) 826002Sroot char *argv[]; 836002Sroot { 8416371Skarels struct sockaddr_in from; 8517156Ssam int on = 1, fromlen; 866002Sroot 8727185Sminshall #if defined(DEBUG) 8827185Sminshall { 8927185Sminshall int s, ns, foo; 9027185Sminshall struct servent *sp; 9127185Sminshall static struct sockaddr_in sin = { AF_INET }; 9227185Sminshall 9327185Sminshall sp = getservbyname("telnet", "tcp"); 9427185Sminshall if (sp == 0) { 9527185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 9627185Sminshall exit(1); 9727185Sminshall } 9827185Sminshall sin.sin_port = sp->s_port; 9927185Sminshall argc--, argv++; 10027185Sminshall if (argc > 0) { 10127185Sminshall sin.sin_port = atoi(*argv); 10227185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 10327185Sminshall } 10427185Sminshall 10527185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 10627185Sminshall if (s < 0) { 10727185Sminshall perror("telnetd: socket");; 10827185Sminshall exit(1); 10927185Sminshall } 11027185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 11127185Sminshall perror("bind"); 11227185Sminshall exit(1); 11327185Sminshall } 11427185Sminshall if (listen(s, 1) < 0) { 11527185Sminshall perror("listen"); 11627185Sminshall exit(1); 11727185Sminshall } 11827185Sminshall foo = sizeof sin; 11927185Sminshall ns = accept(s, &sin, &foo); 12027185Sminshall if (ns < 0) { 12127185Sminshall perror("accept"); 12227185Sminshall exit(1); 12327185Sminshall } 12427185Sminshall dup2(ns, 0); 12527185Sminshall close(s); 12627185Sminshall } 12727185Sminshall #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 23627185Sminshall 2376002Sroot /* 23827185Sminshall * Check a descriptor to see if out of band data exists on it. 23927185Sminshall */ 24027185Sminshall 24127185Sminshall 24227185Sminshall stilloob(s) 24327185Sminshall int s; /* socket number */ 24427185Sminshall { 24527185Sminshall static struct timeval timeout = { 0 }; 24627185Sminshall fd_set excepts; 24727185Sminshall int value; 24827185Sminshall 24927185Sminshall do { 25027185Sminshall FD_ZERO(&excepts); 25127185Sminshall FD_SET(s, &excepts); 25227185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 25327185Sminshall } while ((value == -1) && (errno = EINTR)); 25427185Sminshall 25527185Sminshall if (value < 0) { 25627185Sminshall fatalperror(pty, "select", errno); 25727185Sminshall } 25827185Sminshall if (FD_ISSET(s, &excepts)) { 25927185Sminshall return 1; 26027185Sminshall } else { 26127185Sminshall return 0; 26227185Sminshall } 26327185Sminshall } 26427185Sminshall 26527185Sminshall /* 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); 277*27229Sminshall #if defined(xxxSO_OOBINLINE) 278*27229Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, on, sizeof on); 279*27229Sminshall #endif /* defined(xxxSO_OOBINLINE) */ 2806002Sroot signal(SIGTSTP, SIG_IGN); 28113028Ssam signal(SIGCHLD, cleanup); 28226083Slepreau setpgrp(0, 0); 2836002Sroot 2848379Ssam /* 28527185Sminshall * Request to do remote echo and to suppress go ahead. 2868379Ssam */ 2878379Ssam dooption(TELOPT_ECHO); 28827185Sminshall dooption(TELOPT_SGA); 28912713Ssam /* 29012713Ssam * Show banner that getty never gave. 29112713Ssam */ 29212713Ssam gethostname(hostname, sizeof (hostname)); 29312713Ssam sprintf(nfrontp, BANNER, hostname, ""); 29412713Ssam nfrontp += strlen(nfrontp); 2956002Sroot for (;;) { 29627185Sminshall fd_set ibits, obits, xbits; 2976002Sroot register int c; 2986002Sroot 29927185Sminshall if (ncc < 0 && pcc < 0) 30027185Sminshall break; 30127185Sminshall 30227185Sminshall FD_ZERO(&ibits); 30327185Sminshall FD_ZERO(&obits); 30427185Sminshall FD_ZERO(&xbits); 3056002Sroot /* 3066002Sroot * Never look for input if there's still 3076002Sroot * stuff in the corresponding output buffer 3086002Sroot */ 30927185Sminshall if (nfrontp - nbackp || pcc > 0) { 31027185Sminshall FD_SET(f, &obits); 31127185Sminshall } else { 31227185Sminshall FD_SET(p, &ibits); 31327185Sminshall } 31427185Sminshall if (pfrontp - pbackp || ncc > 0) { 31527185Sminshall FD_SET(p, &obits); 31627185Sminshall } else { 31727185Sminshall FD_SET(f, &ibits); 31827185Sminshall } 31927185Sminshall if (!SYNCHing) { 32027185Sminshall FD_SET(f, &xbits); 32127185Sminshall } 32227185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 32327185Sminshall (struct timeval *)0)) < 1) { 32427185Sminshall if (c == -1) { 32527185Sminshall if (errno == EINTR) { 32627185Sminshall continue; 32727185Sminshall } 32827185Sminshall } 3296002Sroot sleep(5); 3306002Sroot continue; 3316002Sroot } 3326002Sroot 3336002Sroot /* 33427185Sminshall * Any urgent data? 33527185Sminshall */ 33627185Sminshall if (FD_ISSET(net, &xbits)) { 33727185Sminshall SYNCHing = 1; 33827185Sminshall } 33927185Sminshall 34027185Sminshall /* 3416002Sroot * Something to read from the network... 3426002Sroot */ 34327185Sminshall if (FD_ISSET(net, &ibits)) { 344*27229Sminshall #if !defined(xxxSO_OOBINLINE) 34527185Sminshall /* 34627185Sminshall * In 4.2 (and some early 4.3) systems, the 34727185Sminshall * OOB indication and data handling in the kernel 34827185Sminshall * is such that if two separate TCP Urgent requests 34927185Sminshall * come in, one byte of TCP data will be overlaid. 35027185Sminshall * This is fatal for Telnet, but we try to live 35127185Sminshall * with it. 35227185Sminshall * 35327185Sminshall * In addition, in 4.2 (and...), a special protocol 35427185Sminshall * is needed to pick up the TCP Urgent data in 35527185Sminshall * the correct sequence. 35627185Sminshall * 35727185Sminshall * What we do is: if we think we are in urgent 35827185Sminshall * mode, we look to see if we are "at the mark". 35927185Sminshall * If we are, we do an OOB receive. If we run 36027185Sminshall * this twice, we will do the OOB receive twice, 36127185Sminshall * but the second will fail, since the second 36227185Sminshall * time we were "at the mark", but there wasn't 36327185Sminshall * any data there (the kernel doesn't reset 36427185Sminshall * "at the mark" until we do a normal read). 36527185Sminshall * Once we've read the OOB data, we go ahead 36627185Sminshall * and do normal reads. 36727185Sminshall * 36827185Sminshall * There is also another problem, which is that 36927185Sminshall * since the OOB byte we read doesn't put us 37027185Sminshall * out of OOB state, and since that byte is most 37127185Sminshall * likely the TELNET DM (data mark), we would 37227185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 37327185Sminshall * So, clocks to the rescue. If we've "just" 37427185Sminshall * received a DM, then we test for the 37527185Sminshall * presence of OOB data when the receive OOB 37627185Sminshall * fails (and AFTER we did the normal mode read 37727185Sminshall * to clear "at the mark"). 37827185Sminshall */ 37927185Sminshall if (SYNCHing) { 38027185Sminshall int atmark; 38127185Sminshall 38227185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 38327185Sminshall if (atmark) { 38427185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 38527185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 38627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 38727185Sminshall if (clocks.didnetreceive < clocks.gotDM) { 38827185Sminshall SYNCHing = stilloob(net); 38927185Sminshall } 39027185Sminshall } 39127185Sminshall } else { 39227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 3936002Sroot } 39427185Sminshall } else { 39527185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 39627185Sminshall } 39727185Sminshall settimer(didnetreceive); 398*27229Sminshall #else /* !defined(xxxSO_OOBINLINE)) */ 39927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 400*27229Sminshall #endif /* !defined(xxxSO_OOBINLINE)) */ 40127185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 40227185Sminshall ncc = 0; 40327185Sminshall else { 40427185Sminshall if (ncc <= 0) { 40527185Sminshall break; 40627185Sminshall } 40727185Sminshall netip = netibuf; 40827185Sminshall } 4096002Sroot } 4106002Sroot 4116002Sroot /* 4126002Sroot * Something to read from the pty... 4136002Sroot */ 41427185Sminshall if (FD_ISSET(p, &ibits)) { 4156002Sroot pcc = read(p, ptyibuf, BUFSIZ); 4166002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 4176002Sroot pcc = 0; 4186002Sroot else { 4196002Sroot if (pcc <= 0) 4206002Sroot break; 4216002Sroot ptyip = ptyibuf; 4226002Sroot } 4236002Sroot } 4246002Sroot 4256002Sroot while (pcc > 0) { 4266002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 4276002Sroot break; 4286002Sroot c = *ptyip++ & 0377, pcc--; 4296002Sroot if (c == IAC) 4306002Sroot *nfrontp++ = c; 4316002Sroot *nfrontp++ = c; 43227020Sminshall if (c == '\r') { 43327020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 43427020Sminshall *nfrontp++ = *ptyip++ & 0377; 43527020Sminshall pcc--; 43627020Sminshall } else 43727020Sminshall *nfrontp++ = '\0'; 43827020Sminshall } 4396002Sroot } 44027185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 4416002Sroot netflush(); 4426002Sroot if (ncc > 0) 4436002Sroot telrcv(); 44427185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 4456002Sroot ptyflush(); 4466002Sroot } 4476002Sroot cleanup(); 4486002Sroot } 4496002Sroot 4506002Sroot /* 4516002Sroot * State for recv fsm 4526002Sroot */ 4536002Sroot #define TS_DATA 0 /* base state */ 4546002Sroot #define TS_IAC 1 /* look for double IAC's */ 4556002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 4566002Sroot #define TS_BEGINNEG 3 /* throw away begin's... */ 4576002Sroot #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 4586002Sroot #define TS_WILL 5 /* will option negotiation */ 4596002Sroot #define TS_WONT 6 /* wont " */ 4606002Sroot #define TS_DO 7 /* do " */ 4616002Sroot #define TS_DONT 8 /* dont " */ 4626002Sroot 4636002Sroot telrcv() 4646002Sroot { 4656002Sroot register int c; 4666002Sroot static int state = TS_DATA; 4676002Sroot 4686002Sroot while (ncc > 0) { 4696002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 4706002Sroot return; 4716002Sroot c = *netip++ & 0377, ncc--; 4726002Sroot switch (state) { 4736002Sroot 47426090Sminshall case TS_CR: 47526090Sminshall state = TS_DATA; 47626499Sminshall if ((c == 0) || (c == '\n')) { 47726090Sminshall break; 47826499Sminshall } 47926090Sminshall /* FALL THROUGH */ 48026090Sminshall 4816002Sroot case TS_DATA: 4826002Sroot if (c == IAC) { 4836002Sroot state = TS_IAC; 4846002Sroot break; 4856002Sroot } 4866002Sroot if (inter > 0) 4876002Sroot break; 48827020Sminshall /* 48927020Sminshall * We map \r\n ==> \n, since \r\n says 49027020Sminshall * that we want to be in column 1 of the next 49127020Sminshall * printable line, and \n is the standard 49227020Sminshall * unix way of saying that (\r is only good 49327020Sminshall * if CRMOD is set, which it normally is). 49427020Sminshall */ 49526499Sminshall if (!myopts[TELOPT_BINARY] && c == '\r') { 49627020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 49727020Sminshall netip++; ncc--; 49827020Sminshall c = '\n'; 49927020Sminshall } else { 50027020Sminshall state = TS_CR; 50127020Sminshall } 50226499Sminshall } 50326499Sminshall *pfrontp++ = c; 5046002Sroot break; 5056002Sroot 5066002Sroot case TS_IAC: 5076002Sroot switch (c) { 5086002Sroot 5096002Sroot /* 5106002Sroot * Send the process on the pty side an 5116002Sroot * interrupt. Do this with a NULL or 5126002Sroot * interrupt char; depending on the tty mode. 5136002Sroot */ 5146002Sroot case IP: 5156002Sroot interrupt(); 5166002Sroot break; 5176002Sroot 518*27229Sminshall case BREAK: 519*27229Sminshall sendbrk(); 520*27229Sminshall break; 521*27229Sminshall 5226002Sroot /* 5236002Sroot * Are You There? 5246002Sroot */ 5256002Sroot case AYT: 52617583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 52717583Ssam nfrontp += 9; 5286002Sroot break; 5296002Sroot 5306002Sroot /* 53127185Sminshall * Abort Output 53227185Sminshall */ 53327185Sminshall case AO: { 53427185Sminshall struct ltchars tmpltc; 53527185Sminshall 53627185Sminshall ptyflush(); /* half-hearted */ 53727185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 53827185Sminshall if (tmpltc.t_flushc != '\377') { 53927185Sminshall *pfrontp++ = tmpltc.t_flushc; 54027185Sminshall } 54127185Sminshall *nfrontp++ = IAC; 54227185Sminshall *nfrontp++ = DM; 54327187Sminshall neturg = nfrontp-1; /* off by one XXX */ 54427185Sminshall break; 54527185Sminshall } 54627185Sminshall 54727185Sminshall /* 5486002Sroot * Erase Character and 5496002Sroot * Erase Line 5506002Sroot */ 5516002Sroot case EC: 55227185Sminshall case EL: { 55327185Sminshall struct sgttyb b; 55427185Sminshall char ch; 5556002Sroot 55627185Sminshall ptyflush(); /* half-hearted */ 55727185Sminshall ioctl(pty, TIOCGETP, &b); 55827185Sminshall ch = (c == EC) ? 55927185Sminshall b.sg_erase : b.sg_kill; 56027185Sminshall if (ch != '\377') { 56127185Sminshall *pfrontp++ = ch; 56227185Sminshall } 56327185Sminshall break; 56427185Sminshall } 56527185Sminshall 5666002Sroot /* 5676002Sroot * Check for urgent data... 5686002Sroot */ 5696002Sroot case DM: 57027185Sminshall SYNCHing = stilloob(net); 57127185Sminshall settimer(gotDM); 5726002Sroot break; 5736002Sroot 57427185Sminshall 5756002Sroot /* 5766002Sroot * Begin option subnegotiation... 5776002Sroot */ 5786002Sroot case SB: 5796002Sroot state = TS_BEGINNEG; 5806002Sroot continue; 5816002Sroot 5826002Sroot case WILL: 58327188Sminshall state = TS_WILL; 58427188Sminshall continue; 58527188Sminshall 5866002Sroot case WONT: 58727188Sminshall state = TS_WONT; 58827188Sminshall continue; 58927188Sminshall 5906002Sroot case DO: 59127188Sminshall state = TS_DO; 59227188Sminshall continue; 59327188Sminshall 5946002Sroot case DONT: 59527188Sminshall state = TS_DONT; 5966002Sroot continue; 5976002Sroot 5986002Sroot case IAC: 5996002Sroot *pfrontp++ = c; 6006002Sroot break; 6016002Sroot } 6026002Sroot state = TS_DATA; 6036002Sroot break; 6046002Sroot 6056002Sroot case TS_BEGINNEG: 6066002Sroot if (c == IAC) 6076002Sroot state = TS_ENDNEG; 6086002Sroot break; 6096002Sroot 6106002Sroot case TS_ENDNEG: 6116002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 6126002Sroot break; 6136002Sroot 6146002Sroot case TS_WILL: 6156002Sroot if (!hisopts[c]) 6166002Sroot willoption(c); 6176002Sroot state = TS_DATA; 6186002Sroot continue; 6196002Sroot 6206002Sroot case TS_WONT: 6216002Sroot if (hisopts[c]) 6226002Sroot wontoption(c); 6236002Sroot state = TS_DATA; 6246002Sroot continue; 6256002Sroot 6266002Sroot case TS_DO: 6276002Sroot if (!myopts[c]) 6286002Sroot dooption(c); 6296002Sroot state = TS_DATA; 6306002Sroot continue; 6316002Sroot 6326002Sroot case TS_DONT: 6336002Sroot if (myopts[c]) { 6346002Sroot myopts[c] = 0; 6356002Sroot sprintf(nfrontp, wont, c); 6368379Ssam nfrontp += sizeof (wont) - 2; 6376002Sroot } 6386002Sroot state = TS_DATA; 6396002Sroot continue; 6406002Sroot 6416002Sroot default: 6429218Ssam printf("telnetd: panic state=%d\n", state); 6436002Sroot exit(1); 6446002Sroot } 6456002Sroot } 6466002Sroot } 6476002Sroot 6486002Sroot willoption(option) 6496002Sroot int option; 6506002Sroot { 6516002Sroot char *fmt; 6526002Sroot 6536002Sroot switch (option) { 6546002Sroot 6556002Sroot case TELOPT_BINARY: 6566002Sroot mode(RAW, 0); 65727188Sminshall fmt = doopt; 65827188Sminshall break; 6596002Sroot 6606002Sroot case TELOPT_ECHO: 6616002Sroot mode(0, ECHO|CRMOD); 66227188Sminshall fmt = doopt; 66327188Sminshall break; 6646002Sroot 6656002Sroot case TELOPT_SGA: 6666002Sroot fmt = doopt; 6676002Sroot break; 6686002Sroot 6696002Sroot case TELOPT_TM: 6706002Sroot fmt = dont; 6716002Sroot break; 6726002Sroot 6736002Sroot default: 6746002Sroot fmt = dont; 6756002Sroot break; 6766002Sroot } 67727188Sminshall if (fmt == doopt) { 67827188Sminshall hisopts[option] = 1; 67927188Sminshall } else { 68027188Sminshall hisopts[option] = 0; 68127188Sminshall } 6826023Ssam sprintf(nfrontp, fmt, option); 6838379Ssam nfrontp += sizeof (dont) - 2; 6846002Sroot } 6856002Sroot 6866002Sroot wontoption(option) 6876002Sroot int option; 6886002Sroot { 6896002Sroot char *fmt; 6906002Sroot 6916002Sroot switch (option) { 6926002Sroot case TELOPT_ECHO: 6936002Sroot mode(ECHO|CRMOD, 0); 69427188Sminshall break; 6956002Sroot 6966002Sroot case TELOPT_BINARY: 6976002Sroot mode(0, RAW); 6986002Sroot break; 6996002Sroot } 70027188Sminshall fmt = dont; 70127188Sminshall hisopts[option] = 0; 7026002Sroot sprintf(nfrontp, fmt, option); 7038379Ssam nfrontp += sizeof (doopt) - 2; 7046002Sroot } 7056002Sroot 7066002Sroot dooption(option) 7076002Sroot int option; 7086002Sroot { 7096002Sroot char *fmt; 7106002Sroot 7116002Sroot switch (option) { 7126002Sroot 7136002Sroot case TELOPT_TM: 7146002Sroot fmt = wont; 7156002Sroot break; 7166002Sroot 7176002Sroot case TELOPT_ECHO: 7186002Sroot mode(ECHO|CRMOD, 0); 71927188Sminshall fmt = will; 72027188Sminshall break; 7216002Sroot 7226002Sroot case TELOPT_BINARY: 7236002Sroot mode(RAW, 0); 72427188Sminshall fmt = will; 72527188Sminshall break; 7266002Sroot 7276002Sroot case TELOPT_SGA: 7286002Sroot fmt = will; 7296002Sroot break; 7306002Sroot 7316002Sroot default: 7326002Sroot fmt = wont; 7336002Sroot break; 7346002Sroot } 73527188Sminshall if (fmt == will) { 73627188Sminshall myopts[option] = 1; 73727188Sminshall } else { 73827188Sminshall myopts[option] = 0; 73927188Sminshall } 7406002Sroot sprintf(nfrontp, fmt, option); 7418379Ssam nfrontp += sizeof (doopt) - 2; 7426002Sroot } 7436002Sroot 7446002Sroot mode(on, off) 7456002Sroot int on, off; 7466002Sroot { 7476002Sroot struct sgttyb b; 7486002Sroot 7496002Sroot ptyflush(); 7506002Sroot ioctl(pty, TIOCGETP, &b); 7516002Sroot b.sg_flags |= on; 7526002Sroot b.sg_flags &= ~off; 7536002Sroot ioctl(pty, TIOCSETP, &b); 7546002Sroot } 7556002Sroot 7566002Sroot /* 7576002Sroot * Send interrupt to process on other side of pty. 7586002Sroot * If it is in raw mode, just write NULL; 7596002Sroot * otherwise, write intr char. 7606002Sroot */ 7616002Sroot interrupt() 7626002Sroot { 7636002Sroot struct sgttyb b; 7646002Sroot struct tchars tchars; 7656002Sroot 7666002Sroot ptyflush(); /* half-hearted */ 7676002Sroot ioctl(pty, TIOCGETP, &b); 7686002Sroot if (b.sg_flags & RAW) { 7696002Sroot *pfrontp++ = '\0'; 7706002Sroot return; 7716002Sroot } 7726002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 7736002Sroot '\177' : tchars.t_intrc; 7746002Sroot } 7756002Sroot 776*27229Sminshall /* 777*27229Sminshall * Send quit to process on other side of pty. 778*27229Sminshall * If it is in raw mode, just write NULL; 779*27229Sminshall * otherwise, write quit char. 780*27229Sminshall */ 781*27229Sminshall sendbrk() 782*27229Sminshall { 783*27229Sminshall struct sgttyb b; 784*27229Sminshall struct tchars tchars; 785*27229Sminshall 786*27229Sminshall ptyflush(); /* half-hearted */ 787*27229Sminshall ioctl(pty, TIOCGETP, &b); 788*27229Sminshall if (b.sg_flags & RAW) { 789*27229Sminshall *pfrontp++ = '\0'; 790*27229Sminshall return; 791*27229Sminshall } 792*27229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 793*27229Sminshall '\034' : tchars.t_quitc; 794*27229Sminshall } 795*27229Sminshall 7966002Sroot ptyflush() 7976002Sroot { 7986002Sroot int n; 7996002Sroot 8006002Sroot if ((n = pfrontp - pbackp) > 0) 8016002Sroot n = write(pty, pbackp, n); 8028346Ssam if (n < 0) 8038346Ssam return; 8046002Sroot pbackp += n; 8056002Sroot if (pbackp == pfrontp) 8066002Sroot pbackp = pfrontp = ptyobuf; 8076002Sroot } 8086002Sroot 80927185Sminshall #if 0 8106002Sroot netflush() 8116002Sroot { 8126002Sroot int n; 8136002Sroot 8146002Sroot if ((n = nfrontp - nbackp) > 0) 8156002Sroot n = write(net, nbackp, n); 8168346Ssam if (n < 0) { 8178346Ssam if (errno == EWOULDBLOCK) 8188346Ssam return; 8198346Ssam /* should blow this guy away... */ 8208346Ssam return; 8218346Ssam } 8226002Sroot nbackp += n; 8236002Sroot if (nbackp == nfrontp) 8246002Sroot nbackp = nfrontp = netobuf; 8256002Sroot } 82627185Sminshall #else /* 0 */ 8276002Sroot 82827185Sminshall 82927185Sminshall /* 83027185Sminshall * netflush 83127185Sminshall * Send as much data as possible to the network, 83227185Sminshall * handling requests for urgent data. 83327185Sminshall */ 83427185Sminshall 83527185Sminshall 83627185Sminshall netflush() 83727185Sminshall { 83827185Sminshall int n; 83927185Sminshall 84027185Sminshall if ((n = nfrontp - nbackp) > 0) { 84127185Sminshall if (!neturg) { 84227185Sminshall n = write(net, nbackp, n); /* normal write */ 84327185Sminshall } else { 84427185Sminshall n = neturg - nbackp; 84527185Sminshall /* 84627185Sminshall * In 4.2 (and 4.3) systems, there is some question about 84727185Sminshall * what byte in a sendOOB operation is the "OOB" data. 84827185Sminshall * To make ourselves compatible, we only send ONE byte 84927185Sminshall * out of band, the one WE THINK should be OOB (though 85027185Sminshall * we really have more the TCP philosophy of urgent data 85127185Sminshall * rather than the Unix philosophy of OOB data). 85227185Sminshall */ 85327185Sminshall if (n > 1) { 85427185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 85527185Sminshall } else { 85627185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 85727185Sminshall } 85827185Sminshall } 85927185Sminshall } 86027185Sminshall if (n < 0) { 86127185Sminshall if (errno == EWOULDBLOCK) 86227185Sminshall return; 86327185Sminshall /* should blow this guy away... */ 86427185Sminshall return; 86527185Sminshall } 86627185Sminshall nbackp += n; 86727185Sminshall if (nbackp >= neturg) { 86827185Sminshall neturg = 0; 86927185Sminshall } 87027185Sminshall if (nbackp == nfrontp) { 87127185Sminshall nbackp = nfrontp = netobuf; 87227185Sminshall } 87327185Sminshall } 87427185Sminshall #endif /* 0 */ 87527185Sminshall 8766002Sroot cleanup() 8776002Sroot { 8786002Sroot 8796002Sroot rmut(); 88010008Ssam vhangup(); /* XXX */ 88110191Ssam shutdown(net, 2); 8826002Sroot exit(1); 8836002Sroot } 8846002Sroot 8856002Sroot #include <utmp.h> 8866002Sroot 8876002Sroot struct utmp wtmp; 8886002Sroot char wtmpf[] = "/usr/adm/wtmp"; 88923567Sbloom char utmpf[] = "/etc/utmp"; 89023567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 89123567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 8926002Sroot 8936002Sroot rmut() 8946002Sroot { 8956002Sroot register f; 8966002Sroot int found = 0; 89723567Sbloom struct utmp *u, *utmp; 89823567Sbloom int nutmp; 89923567Sbloom struct stat statbf; 9006002Sroot 90123567Sbloom f = open(utmpf, O_RDWR); 9026002Sroot if (f >= 0) { 90323567Sbloom fstat(f, &statbf); 90423567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 90523567Sbloom if (!utmp) 90623567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 90723567Sbloom if (statbf.st_size && utmp) { 90823567Sbloom nutmp = read(f, utmp, statbf.st_size); 90923567Sbloom nutmp /= sizeof(struct utmp); 91023567Sbloom 91123567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 91223567Sbloom if (SCMPN(u->ut_line, line+5) || 91323567Sbloom u->ut_name[0]==0) 91423567Sbloom continue; 91523567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 91623567Sbloom SCPYN(u->ut_name, ""); 91723567Sbloom SCPYN(u->ut_host, ""); 91823567Sbloom time(&u->ut_time); 91923567Sbloom write(f, (char *)u, sizeof(wtmp)); 92023567Sbloom found++; 92123567Sbloom } 9226002Sroot } 9236002Sroot close(f); 9246002Sroot } 9256002Sroot if (found) { 92617583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 9276002Sroot if (f >= 0) { 9286002Sroot SCPYN(wtmp.ut_line, line+5); 9296002Sroot SCPYN(wtmp.ut_name, ""); 93012683Ssam SCPYN(wtmp.ut_host, ""); 9316002Sroot time(&wtmp.ut_time); 93223567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 9336002Sroot close(f); 9346002Sroot } 9356002Sroot } 9366002Sroot chmod(line, 0666); 9376002Sroot chown(line, 0, 0); 9386002Sroot line[strlen("/dev/")] = 'p'; 9396002Sroot chmod(line, 0666); 9406002Sroot chown(line, 0, 0); 9416002Sroot } 942