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*27260Sminshall static char sccsid[] = "@(#)telnetd.c 5.13 (Berkeley) 04/22/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); 27727229Sminshall #if defined(xxxSO_OOBINLINE) 27827229Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, on, sizeof on); 27927229Sminshall #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)) { 34427229Sminshall #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); 39827229Sminshall #else /* !defined(xxxSO_OOBINLINE)) */ 39927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 40027229Sminshall #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 51827229Sminshall case BREAK: 51927229Sminshall sendbrk(); 52027229Sminshall break; 52127229Sminshall 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 } 541*27260Sminshall netclear(); /* clear buffer back */ 54227185Sminshall *nfrontp++ = IAC; 54327185Sminshall *nfrontp++ = DM; 54427187Sminshall neturg = nfrontp-1; /* off by one XXX */ 54527185Sminshall break; 54627185Sminshall } 54727185Sminshall 54827185Sminshall /* 5496002Sroot * Erase Character and 5506002Sroot * Erase Line 5516002Sroot */ 5526002Sroot case EC: 55327185Sminshall case EL: { 55427185Sminshall struct sgttyb b; 55527185Sminshall char ch; 5566002Sroot 55727185Sminshall ptyflush(); /* half-hearted */ 55827185Sminshall ioctl(pty, TIOCGETP, &b); 55927185Sminshall ch = (c == EC) ? 56027185Sminshall b.sg_erase : b.sg_kill; 56127185Sminshall if (ch != '\377') { 56227185Sminshall *pfrontp++ = ch; 56327185Sminshall } 56427185Sminshall break; 56527185Sminshall } 56627185Sminshall 5676002Sroot /* 5686002Sroot * Check for urgent data... 5696002Sroot */ 5706002Sroot case DM: 57127185Sminshall SYNCHing = stilloob(net); 57227185Sminshall settimer(gotDM); 5736002Sroot break; 5746002Sroot 57527185Sminshall 5766002Sroot /* 5776002Sroot * Begin option subnegotiation... 5786002Sroot */ 5796002Sroot case SB: 5806002Sroot state = TS_BEGINNEG; 5816002Sroot continue; 5826002Sroot 5836002Sroot case WILL: 58427188Sminshall state = TS_WILL; 58527188Sminshall continue; 58627188Sminshall 5876002Sroot case WONT: 58827188Sminshall state = TS_WONT; 58927188Sminshall continue; 59027188Sminshall 5916002Sroot case DO: 59227188Sminshall state = TS_DO; 59327188Sminshall continue; 59427188Sminshall 5956002Sroot case DONT: 59627188Sminshall state = TS_DONT; 5976002Sroot continue; 5986002Sroot 5996002Sroot case IAC: 6006002Sroot *pfrontp++ = c; 6016002Sroot break; 6026002Sroot } 6036002Sroot state = TS_DATA; 6046002Sroot break; 6056002Sroot 6066002Sroot case TS_BEGINNEG: 6076002Sroot if (c == IAC) 6086002Sroot state = TS_ENDNEG; 6096002Sroot break; 6106002Sroot 6116002Sroot case TS_ENDNEG: 6126002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 6136002Sroot break; 6146002Sroot 6156002Sroot case TS_WILL: 6166002Sroot if (!hisopts[c]) 6176002Sroot willoption(c); 6186002Sroot state = TS_DATA; 6196002Sroot continue; 6206002Sroot 6216002Sroot case TS_WONT: 6226002Sroot if (hisopts[c]) 6236002Sroot wontoption(c); 6246002Sroot state = TS_DATA; 6256002Sroot continue; 6266002Sroot 6276002Sroot case TS_DO: 6286002Sroot if (!myopts[c]) 6296002Sroot dooption(c); 6306002Sroot state = TS_DATA; 6316002Sroot continue; 6326002Sroot 6336002Sroot case TS_DONT: 6346002Sroot if (myopts[c]) { 6356002Sroot myopts[c] = 0; 6366002Sroot sprintf(nfrontp, wont, c); 6378379Ssam nfrontp += sizeof (wont) - 2; 6386002Sroot } 6396002Sroot state = TS_DATA; 6406002Sroot continue; 6416002Sroot 6426002Sroot default: 6439218Ssam printf("telnetd: panic state=%d\n", state); 6446002Sroot exit(1); 6456002Sroot } 6466002Sroot } 6476002Sroot } 6486002Sroot 6496002Sroot willoption(option) 6506002Sroot int option; 6516002Sroot { 6526002Sroot char *fmt; 6536002Sroot 6546002Sroot switch (option) { 6556002Sroot 6566002Sroot case TELOPT_BINARY: 6576002Sroot mode(RAW, 0); 65827188Sminshall fmt = doopt; 65927188Sminshall break; 6606002Sroot 6616002Sroot case TELOPT_ECHO: 6626002Sroot mode(0, ECHO|CRMOD); 66327188Sminshall fmt = doopt; 66427188Sminshall break; 6656002Sroot 6666002Sroot case TELOPT_SGA: 6676002Sroot fmt = doopt; 6686002Sroot break; 6696002Sroot 6706002Sroot case TELOPT_TM: 6716002Sroot fmt = dont; 6726002Sroot break; 6736002Sroot 6746002Sroot default: 6756002Sroot fmt = dont; 6766002Sroot break; 6776002Sroot } 67827188Sminshall if (fmt == doopt) { 67927188Sminshall hisopts[option] = 1; 68027188Sminshall } else { 68127188Sminshall hisopts[option] = 0; 68227188Sminshall } 6836023Ssam sprintf(nfrontp, fmt, option); 6848379Ssam nfrontp += sizeof (dont) - 2; 6856002Sroot } 6866002Sroot 6876002Sroot wontoption(option) 6886002Sroot int option; 6896002Sroot { 6906002Sroot char *fmt; 6916002Sroot 6926002Sroot switch (option) { 6936002Sroot case TELOPT_ECHO: 6946002Sroot mode(ECHO|CRMOD, 0); 69527188Sminshall break; 6966002Sroot 6976002Sroot case TELOPT_BINARY: 6986002Sroot mode(0, RAW); 6996002Sroot break; 7006002Sroot } 70127188Sminshall fmt = dont; 70227188Sminshall hisopts[option] = 0; 7036002Sroot sprintf(nfrontp, fmt, option); 7048379Ssam nfrontp += sizeof (doopt) - 2; 7056002Sroot } 7066002Sroot 7076002Sroot dooption(option) 7086002Sroot int option; 7096002Sroot { 7106002Sroot char *fmt; 7116002Sroot 7126002Sroot switch (option) { 7136002Sroot 7146002Sroot case TELOPT_TM: 7156002Sroot fmt = wont; 7166002Sroot break; 7176002Sroot 7186002Sroot case TELOPT_ECHO: 7196002Sroot mode(ECHO|CRMOD, 0); 72027188Sminshall fmt = will; 72127188Sminshall break; 7226002Sroot 7236002Sroot case TELOPT_BINARY: 7246002Sroot mode(RAW, 0); 72527188Sminshall fmt = will; 72627188Sminshall break; 7276002Sroot 7286002Sroot case TELOPT_SGA: 7296002Sroot fmt = will; 7306002Sroot break; 7316002Sroot 7326002Sroot default: 7336002Sroot fmt = wont; 7346002Sroot break; 7356002Sroot } 73627188Sminshall if (fmt == will) { 73727188Sminshall myopts[option] = 1; 73827188Sminshall } else { 73927188Sminshall myopts[option] = 0; 74027188Sminshall } 7416002Sroot sprintf(nfrontp, fmt, option); 7428379Ssam nfrontp += sizeof (doopt) - 2; 7436002Sroot } 7446002Sroot 7456002Sroot mode(on, off) 7466002Sroot int on, off; 7476002Sroot { 7486002Sroot struct sgttyb b; 7496002Sroot 7506002Sroot ptyflush(); 7516002Sroot ioctl(pty, TIOCGETP, &b); 7526002Sroot b.sg_flags |= on; 7536002Sroot b.sg_flags &= ~off; 7546002Sroot ioctl(pty, TIOCSETP, &b); 7556002Sroot } 7566002Sroot 7576002Sroot /* 7586002Sroot * Send interrupt to process on other side of pty. 7596002Sroot * If it is in raw mode, just write NULL; 7606002Sroot * otherwise, write intr char. 7616002Sroot */ 7626002Sroot interrupt() 7636002Sroot { 7646002Sroot struct sgttyb b; 7656002Sroot struct tchars tchars; 7666002Sroot 7676002Sroot ptyflush(); /* half-hearted */ 7686002Sroot ioctl(pty, TIOCGETP, &b); 7696002Sroot if (b.sg_flags & RAW) { 7706002Sroot *pfrontp++ = '\0'; 7716002Sroot return; 7726002Sroot } 7736002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 7746002Sroot '\177' : tchars.t_intrc; 7756002Sroot } 7766002Sroot 77727229Sminshall /* 77827229Sminshall * Send quit to process on other side of pty. 77927229Sminshall * If it is in raw mode, just write NULL; 78027229Sminshall * otherwise, write quit char. 78127229Sminshall */ 78227229Sminshall sendbrk() 78327229Sminshall { 78427229Sminshall struct sgttyb b; 78527229Sminshall struct tchars tchars; 78627229Sminshall 78727229Sminshall ptyflush(); /* half-hearted */ 78827229Sminshall ioctl(pty, TIOCGETP, &b); 78927229Sminshall if (b.sg_flags & RAW) { 79027229Sminshall *pfrontp++ = '\0'; 79127229Sminshall return; 79227229Sminshall } 79327229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 79427229Sminshall '\034' : tchars.t_quitc; 79527229Sminshall } 79627229Sminshall 7976002Sroot ptyflush() 7986002Sroot { 7996002Sroot int n; 8006002Sroot 8016002Sroot if ((n = pfrontp - pbackp) > 0) 8026002Sroot n = write(pty, pbackp, n); 8038346Ssam if (n < 0) 8048346Ssam return; 8056002Sroot pbackp += n; 8066002Sroot if (pbackp == pfrontp) 8076002Sroot pbackp = pfrontp = ptyobuf; 8086002Sroot } 809*27260Sminshall 810*27260Sminshall /* 811*27260Sminshall * nextitem() 812*27260Sminshall * 813*27260Sminshall * Return the address of the next "item" in the TELNET data 814*27260Sminshall * stream. This will be the address of the next character if 815*27260Sminshall * the current address is a user data character, or it will 816*27260Sminshall * be the address of the character following the TELNET command 817*27260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 818*27260Sminshall * character. 819*27260Sminshall */ 8206002Sroot 821*27260Sminshall char * 822*27260Sminshall nextitem(current) 823*27260Sminshall char *current; 8246002Sroot { 825*27260Sminshall if ((*current&0xff) != IAC) { 826*27260Sminshall return current+1; 827*27260Sminshall } 828*27260Sminshall switch (*(current+1)&0xff) { 829*27260Sminshall case DO: 830*27260Sminshall case DONT: 831*27260Sminshall case WILL: 832*27260Sminshall case WONT: 833*27260Sminshall return current+3; 834*27260Sminshall case SB: /* loop forever looking for the SE */ 835*27260Sminshall { 836*27260Sminshall register char *look = current+2; 8376002Sroot 838*27260Sminshall for (;;) { 839*27260Sminshall if ((*look++&0xff) == IAC) { 840*27260Sminshall if ((*look++&0xff) == SE) { 841*27260Sminshall return look; 842*27260Sminshall } 843*27260Sminshall } 844*27260Sminshall } 8458346Ssam } 846*27260Sminshall default: 847*27260Sminshall return current+2; 848*27260Sminshall } 8496002Sroot } 8506002Sroot 85127185Sminshall 85227185Sminshall /* 853*27260Sminshall * netclear() 854*27260Sminshall * 855*27260Sminshall * We are about to do a TELNET SYNCH operation. Clear 856*27260Sminshall * the path to the network. 857*27260Sminshall * 858*27260Sminshall * Things are a bit tricky since we may have sent the first 859*27260Sminshall * byte or so of a previous TELNET command into the network. 860*27260Sminshall * So, we have to scan the network buffer from the beginning 861*27260Sminshall * until we are up to where we want to be. 862*27260Sminshall * 863*27260Sminshall * A side effect of what we do, just to keep things 864*27260Sminshall * simple, is to clear the urgent data pointer. The principal 865*27260Sminshall * caller should be setting the urgent data pointer AFTER calling 866*27260Sminshall * us in any case. 867*27260Sminshall */ 868*27260Sminshall 869*27260Sminshall netclear() 870*27260Sminshall { 871*27260Sminshall register char *thisitem, *next; 872*27260Sminshall char *good; 873*27260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 874*27260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 875*27260Sminshall 876*27260Sminshall thisitem = netobuf; 877*27260Sminshall 878*27260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 879*27260Sminshall thisitem = next; 880*27260Sminshall } 881*27260Sminshall 882*27260Sminshall /* Now, thisitem is first before/at boundary. */ 883*27260Sminshall 884*27260Sminshall good = netobuf; /* where the good bytes go */ 885*27260Sminshall 886*27260Sminshall while (nfrontp > thisitem) { 887*27260Sminshall if (wewant(thisitem)) { 888*27260Sminshall int length; 889*27260Sminshall 890*27260Sminshall next = thisitem; 891*27260Sminshall do { 892*27260Sminshall next = nextitem(next); 893*27260Sminshall } while (wewant(next) && (nfrontp > next)); 894*27260Sminshall length = next-thisitem; 895*27260Sminshall bcopy(thisitem, good, length); 896*27260Sminshall good += length; 897*27260Sminshall thisitem = next; 898*27260Sminshall } else { 899*27260Sminshall thisitem = nextitem(thisitem); 900*27260Sminshall } 901*27260Sminshall } 902*27260Sminshall 903*27260Sminshall nbackp = netobuf; 904*27260Sminshall nfrontp = good; /* next byte to be sent */ 905*27260Sminshall neturg = 0; 906*27260Sminshall } 907*27260Sminshall 908*27260Sminshall /* 90927185Sminshall * netflush 91027185Sminshall * Send as much data as possible to the network, 91127185Sminshall * handling requests for urgent data. 91227185Sminshall */ 91327185Sminshall 91427185Sminshall 91527185Sminshall netflush() 91627185Sminshall { 91727185Sminshall int n; 91827185Sminshall 91927185Sminshall if ((n = nfrontp - nbackp) > 0) { 92027185Sminshall if (!neturg) { 92127185Sminshall n = write(net, nbackp, n); /* normal write */ 92227185Sminshall } else { 92327185Sminshall n = neturg - nbackp; 92427185Sminshall /* 92527185Sminshall * In 4.2 (and 4.3) systems, there is some question about 92627185Sminshall * what byte in a sendOOB operation is the "OOB" data. 92727185Sminshall * To make ourselves compatible, we only send ONE byte 92827185Sminshall * out of band, the one WE THINK should be OOB (though 92927185Sminshall * we really have more the TCP philosophy of urgent data 93027185Sminshall * rather than the Unix philosophy of OOB data). 93127185Sminshall */ 93227185Sminshall if (n > 1) { 93327185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 93427185Sminshall } else { 93527185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 93627185Sminshall } 93727185Sminshall } 93827185Sminshall } 93927185Sminshall if (n < 0) { 94027185Sminshall if (errno == EWOULDBLOCK) 94127185Sminshall return; 94227185Sminshall /* should blow this guy away... */ 94327185Sminshall return; 94427185Sminshall } 94527185Sminshall nbackp += n; 94627185Sminshall if (nbackp >= neturg) { 94727185Sminshall neturg = 0; 94827185Sminshall } 94927185Sminshall if (nbackp == nfrontp) { 95027185Sminshall nbackp = nfrontp = netobuf; 95127185Sminshall } 95227185Sminshall } 95327185Sminshall 9546002Sroot cleanup() 9556002Sroot { 9566002Sroot 9576002Sroot rmut(); 95810008Ssam vhangup(); /* XXX */ 95910191Ssam shutdown(net, 2); 9606002Sroot exit(1); 9616002Sroot } 9626002Sroot 9636002Sroot #include <utmp.h> 9646002Sroot 9656002Sroot struct utmp wtmp; 9666002Sroot char wtmpf[] = "/usr/adm/wtmp"; 96723567Sbloom char utmpf[] = "/etc/utmp"; 96823567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 96923567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 9706002Sroot 9716002Sroot rmut() 9726002Sroot { 9736002Sroot register f; 9746002Sroot int found = 0; 97523567Sbloom struct utmp *u, *utmp; 97623567Sbloom int nutmp; 97723567Sbloom struct stat statbf; 9786002Sroot 97923567Sbloom f = open(utmpf, O_RDWR); 9806002Sroot if (f >= 0) { 98123567Sbloom fstat(f, &statbf); 98223567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 98323567Sbloom if (!utmp) 98423567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 98523567Sbloom if (statbf.st_size && utmp) { 98623567Sbloom nutmp = read(f, utmp, statbf.st_size); 98723567Sbloom nutmp /= sizeof(struct utmp); 98823567Sbloom 98923567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 99023567Sbloom if (SCMPN(u->ut_line, line+5) || 99123567Sbloom u->ut_name[0]==0) 99223567Sbloom continue; 99323567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 99423567Sbloom SCPYN(u->ut_name, ""); 99523567Sbloom SCPYN(u->ut_host, ""); 99623567Sbloom time(&u->ut_time); 99723567Sbloom write(f, (char *)u, sizeof(wtmp)); 99823567Sbloom found++; 99923567Sbloom } 10006002Sroot } 10016002Sroot close(f); 10026002Sroot } 10036002Sroot if (found) { 100417583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 10056002Sroot if (f >= 0) { 10066002Sroot SCPYN(wtmp.ut_line, line+5); 10076002Sroot SCPYN(wtmp.ut_name, ""); 100812683Ssam SCPYN(wtmp.ut_host, ""); 10096002Sroot time(&wtmp.ut_time); 101023567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 10116002Sroot close(f); 10126002Sroot } 10136002Sroot } 10146002Sroot chmod(line, 0666); 10156002Sroot chown(line, 0, 0); 10166002Sroot line[strlen("/dev/")] = 'p'; 10176002Sroot chmod(line, 0666); 10186002Sroot chown(line, 0, 0); 10196002Sroot } 1020