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*27188Sminshall static char sccsid[] = "@(#)telnetd.c 5.11 (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> 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); 2776002Sroot signal(SIGTSTP, SIG_IGN); 27813028Ssam signal(SIGCHLD, cleanup); 27926083Slepreau setpgrp(0, 0); 2806002Sroot 2818379Ssam /* 28227185Sminshall * Request to do remote echo and to suppress go ahead. 2838379Ssam */ 2848379Ssam dooption(TELOPT_ECHO); 28527185Sminshall 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 (;;) { 29327185Sminshall fd_set ibits, obits, xbits; 2946002Sroot register int c; 2956002Sroot 29627185Sminshall if (ncc < 0 && pcc < 0) 29727185Sminshall break; 29827185Sminshall 29927185Sminshall FD_ZERO(&ibits); 30027185Sminshall FD_ZERO(&obits); 30127185Sminshall FD_ZERO(&xbits); 3026002Sroot /* 3036002Sroot * Never look for input if there's still 3046002Sroot * stuff in the corresponding output buffer 3056002Sroot */ 30627185Sminshall if (nfrontp - nbackp || pcc > 0) { 30727185Sminshall FD_SET(f, &obits); 30827185Sminshall } else { 30927185Sminshall FD_SET(p, &ibits); 31027185Sminshall } 31127185Sminshall if (pfrontp - pbackp || ncc > 0) { 31227185Sminshall FD_SET(p, &obits); 31327185Sminshall } else { 31427185Sminshall FD_SET(f, &ibits); 31527185Sminshall } 31627185Sminshall if (!SYNCHing) { 31727185Sminshall FD_SET(f, &xbits); 31827185Sminshall } 31927185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 32027185Sminshall (struct timeval *)0)) < 1) { 32127185Sminshall if (c == -1) { 32227185Sminshall if (errno == EINTR) { 32327185Sminshall continue; 32427185Sminshall } 32527185Sminshall } 3266002Sroot sleep(5); 3276002Sroot continue; 3286002Sroot } 3296002Sroot 3306002Sroot /* 33127185Sminshall * Any urgent data? 33227185Sminshall */ 33327185Sminshall if (FD_ISSET(net, &xbits)) { 33427185Sminshall SYNCHing = 1; 33527185Sminshall } 33627185Sminshall 33727185Sminshall /* 3386002Sroot * Something to read from the network... 3396002Sroot */ 34027185Sminshall if (FD_ISSET(net, &ibits)) { 34127185Sminshall #if !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) 34227185Sminshall /* 34327185Sminshall * In 4.2 (and some early 4.3) systems, the 34427185Sminshall * OOB indication and data handling in the kernel 34527185Sminshall * is such that if two separate TCP Urgent requests 34627185Sminshall * come in, one byte of TCP data will be overlaid. 34727185Sminshall * This is fatal for Telnet, but we try to live 34827185Sminshall * with it. 34927185Sminshall * 35027185Sminshall * In addition, in 4.2 (and...), a special protocol 35127185Sminshall * is needed to pick up the TCP Urgent data in 35227185Sminshall * the correct sequence. 35327185Sminshall * 35427185Sminshall * What we do is: if we think we are in urgent 35527185Sminshall * mode, we look to see if we are "at the mark". 35627185Sminshall * If we are, we do an OOB receive. If we run 35727185Sminshall * this twice, we will do the OOB receive twice, 35827185Sminshall * but the second will fail, since the second 35927185Sminshall * time we were "at the mark", but there wasn't 36027185Sminshall * any data there (the kernel doesn't reset 36127185Sminshall * "at the mark" until we do a normal read). 36227185Sminshall * Once we've read the OOB data, we go ahead 36327185Sminshall * and do normal reads. 36427185Sminshall * 36527185Sminshall * There is also another problem, which is that 36627185Sminshall * since the OOB byte we read doesn't put us 36727185Sminshall * out of OOB state, and since that byte is most 36827185Sminshall * likely the TELNET DM (data mark), we would 36927185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 37027185Sminshall * So, clocks to the rescue. If we've "just" 37127185Sminshall * received a DM, then we test for the 37227185Sminshall * presence of OOB data when the receive OOB 37327185Sminshall * fails (and AFTER we did the normal mode read 37427185Sminshall * to clear "at the mark"). 37527185Sminshall */ 37627185Sminshall if (SYNCHing) { 37727185Sminshall int atmark; 37827185Sminshall 37927185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 38027185Sminshall if (atmark) { 38127185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 38227185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 38327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 38427185Sminshall if (clocks.didnetreceive < clocks.gotDM) { 38527185Sminshall SYNCHing = stilloob(net); 38627185Sminshall } 38727185Sminshall } 38827185Sminshall } else { 38927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 3906002Sroot } 39127185Sminshall } else { 39227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 39327185Sminshall } 39427185Sminshall settimer(didnetreceive); 39527185Sminshall #else /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ 39627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 39727185Sminshall #endif /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ 39827185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 39927185Sminshall ncc = 0; 40027185Sminshall else { 40127185Sminshall if (ncc <= 0) { 40227185Sminshall break; 40327185Sminshall } 40427185Sminshall netip = netibuf; 40527185Sminshall } 4066002Sroot } 4076002Sroot 4086002Sroot /* 4096002Sroot * Something to read from the pty... 4106002Sroot */ 41127185Sminshall 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 } 43727185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 4386002Sroot netflush(); 4396002Sroot if (ncc > 0) 4406002Sroot telrcv(); 44127185Sminshall 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 /* 52527185Sminshall * Abort Output 52627185Sminshall */ 52727185Sminshall case AO: { 52827185Sminshall struct ltchars tmpltc; 52927185Sminshall 53027185Sminshall ptyflush(); /* half-hearted */ 53127185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 53227185Sminshall if (tmpltc.t_flushc != '\377') { 53327185Sminshall *pfrontp++ = tmpltc.t_flushc; 53427185Sminshall } 53527185Sminshall *nfrontp++ = IAC; 53627185Sminshall *nfrontp++ = DM; 53727187Sminshall neturg = nfrontp-1; /* off by one XXX */ 53827185Sminshall break; 53927185Sminshall } 54027185Sminshall 54127185Sminshall /* 5426002Sroot * Erase Character and 5436002Sroot * Erase Line 5446002Sroot */ 5456002Sroot case EC: 54627185Sminshall case EL: { 54727185Sminshall struct sgttyb b; 54827185Sminshall char ch; 5496002Sroot 55027185Sminshall ptyflush(); /* half-hearted */ 55127185Sminshall ioctl(pty, TIOCGETP, &b); 55227185Sminshall ch = (c == EC) ? 55327185Sminshall b.sg_erase : b.sg_kill; 55427185Sminshall if (ch != '\377') { 55527185Sminshall *pfrontp++ = ch; 55627185Sminshall } 55727185Sminshall break; 55827185Sminshall } 55927185Sminshall 5606002Sroot /* 5616002Sroot * Check for urgent data... 5626002Sroot */ 5636002Sroot case DM: 56427185Sminshall SYNCHing = stilloob(net); 56527185Sminshall settimer(gotDM); 5666002Sroot break; 5676002Sroot 56827185Sminshall 5696002Sroot /* 5706002Sroot * Begin option subnegotiation... 5716002Sroot */ 5726002Sroot case SB: 5736002Sroot state = TS_BEGINNEG; 5746002Sroot continue; 5756002Sroot 5766002Sroot case WILL: 577*27188Sminshall state = TS_WILL; 578*27188Sminshall continue; 579*27188Sminshall 5806002Sroot case WONT: 581*27188Sminshall state = TS_WONT; 582*27188Sminshall continue; 583*27188Sminshall 5846002Sroot case DO: 585*27188Sminshall state = TS_DO; 586*27188Sminshall continue; 587*27188Sminshall 5886002Sroot case DONT: 589*27188Sminshall state = TS_DONT; 5906002Sroot continue; 5916002Sroot 5926002Sroot case IAC: 5936002Sroot *pfrontp++ = c; 5946002Sroot break; 5956002Sroot } 5966002Sroot state = TS_DATA; 5976002Sroot break; 5986002Sroot 5996002Sroot case TS_BEGINNEG: 6006002Sroot if (c == IAC) 6016002Sroot state = TS_ENDNEG; 6026002Sroot break; 6036002Sroot 6046002Sroot case TS_ENDNEG: 6056002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 6066002Sroot break; 6076002Sroot 6086002Sroot case TS_WILL: 6096002Sroot if (!hisopts[c]) 6106002Sroot willoption(c); 6116002Sroot state = TS_DATA; 6126002Sroot continue; 6136002Sroot 6146002Sroot case TS_WONT: 6156002Sroot if (hisopts[c]) 6166002Sroot wontoption(c); 6176002Sroot state = TS_DATA; 6186002Sroot continue; 6196002Sroot 6206002Sroot case TS_DO: 6216002Sroot if (!myopts[c]) 6226002Sroot dooption(c); 6236002Sroot state = TS_DATA; 6246002Sroot continue; 6256002Sroot 6266002Sroot case TS_DONT: 6276002Sroot if (myopts[c]) { 6286002Sroot myopts[c] = 0; 6296002Sroot sprintf(nfrontp, wont, c); 6308379Ssam nfrontp += sizeof (wont) - 2; 6316002Sroot } 6326002Sroot state = TS_DATA; 6336002Sroot continue; 6346002Sroot 6356002Sroot default: 6369218Ssam printf("telnetd: panic state=%d\n", state); 6376002Sroot exit(1); 6386002Sroot } 6396002Sroot } 6406002Sroot } 6416002Sroot 6426002Sroot willoption(option) 6436002Sroot int option; 6446002Sroot { 6456002Sroot char *fmt; 6466002Sroot 6476002Sroot switch (option) { 6486002Sroot 6496002Sroot case TELOPT_BINARY: 6506002Sroot mode(RAW, 0); 651*27188Sminshall fmt = doopt; 652*27188Sminshall break; 6536002Sroot 6546002Sroot case TELOPT_ECHO: 6556002Sroot mode(0, ECHO|CRMOD); 656*27188Sminshall fmt = doopt; 657*27188Sminshall break; 6586002Sroot 6596002Sroot case TELOPT_SGA: 6606002Sroot fmt = doopt; 6616002Sroot break; 6626002Sroot 6636002Sroot case TELOPT_TM: 6646002Sroot fmt = dont; 6656002Sroot break; 6666002Sroot 6676002Sroot default: 6686002Sroot fmt = dont; 6696002Sroot break; 6706002Sroot } 671*27188Sminshall if (fmt == doopt) { 672*27188Sminshall hisopts[option] = 1; 673*27188Sminshall } else { 674*27188Sminshall hisopts[option] = 0; 675*27188Sminshall } 6766023Ssam sprintf(nfrontp, fmt, option); 6778379Ssam nfrontp += sizeof (dont) - 2; 6786002Sroot } 6796002Sroot 6806002Sroot wontoption(option) 6816002Sroot int option; 6826002Sroot { 6836002Sroot char *fmt; 6846002Sroot 6856002Sroot switch (option) { 6866002Sroot case TELOPT_ECHO: 6876002Sroot mode(ECHO|CRMOD, 0); 688*27188Sminshall break; 6896002Sroot 6906002Sroot case TELOPT_BINARY: 6916002Sroot mode(0, RAW); 6926002Sroot break; 6936002Sroot } 694*27188Sminshall fmt = dont; 695*27188Sminshall hisopts[option] = 0; 6966002Sroot sprintf(nfrontp, fmt, option); 6978379Ssam nfrontp += sizeof (doopt) - 2; 6986002Sroot } 6996002Sroot 7006002Sroot dooption(option) 7016002Sroot int option; 7026002Sroot { 7036002Sroot char *fmt; 7046002Sroot 7056002Sroot switch (option) { 7066002Sroot 7076002Sroot case TELOPT_TM: 7086002Sroot fmt = wont; 7096002Sroot break; 7106002Sroot 7116002Sroot case TELOPT_ECHO: 7126002Sroot mode(ECHO|CRMOD, 0); 713*27188Sminshall fmt = will; 714*27188Sminshall break; 7156002Sroot 7166002Sroot case TELOPT_BINARY: 7176002Sroot mode(RAW, 0); 718*27188Sminshall fmt = will; 719*27188Sminshall break; 7206002Sroot 7216002Sroot case TELOPT_SGA: 7226002Sroot fmt = will; 7236002Sroot break; 7246002Sroot 7256002Sroot default: 7266002Sroot fmt = wont; 7276002Sroot break; 7286002Sroot } 729*27188Sminshall if (fmt == will) { 730*27188Sminshall myopts[option] = 1; 731*27188Sminshall } else { 732*27188Sminshall myopts[option] = 0; 733*27188Sminshall } 7346002Sroot sprintf(nfrontp, fmt, option); 7358379Ssam nfrontp += sizeof (doopt) - 2; 7366002Sroot } 7376002Sroot 7386002Sroot mode(on, off) 7396002Sroot int on, off; 7406002Sroot { 7416002Sroot struct sgttyb b; 7426002Sroot 7436002Sroot ptyflush(); 7446002Sroot ioctl(pty, TIOCGETP, &b); 7456002Sroot b.sg_flags |= on; 7466002Sroot b.sg_flags &= ~off; 7476002Sroot ioctl(pty, TIOCSETP, &b); 7486002Sroot } 7496002Sroot 7506002Sroot /* 7516002Sroot * Send interrupt to process on other side of pty. 7526002Sroot * If it is in raw mode, just write NULL; 7536002Sroot * otherwise, write intr char. 7546002Sroot */ 7556002Sroot interrupt() 7566002Sroot { 7576002Sroot struct sgttyb b; 7586002Sroot struct tchars tchars; 7596002Sroot 7606002Sroot ptyflush(); /* half-hearted */ 7616002Sroot ioctl(pty, TIOCGETP, &b); 7626002Sroot if (b.sg_flags & RAW) { 7636002Sroot *pfrontp++ = '\0'; 7646002Sroot return; 7656002Sroot } 7666002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 7676002Sroot '\177' : tchars.t_intrc; 7686002Sroot } 7696002Sroot 7706002Sroot ptyflush() 7716002Sroot { 7726002Sroot int n; 7736002Sroot 7746002Sroot if ((n = pfrontp - pbackp) > 0) 7756002Sroot n = write(pty, pbackp, n); 7768346Ssam if (n < 0) 7778346Ssam return; 7786002Sroot pbackp += n; 7796002Sroot if (pbackp == pfrontp) 7806002Sroot pbackp = pfrontp = ptyobuf; 7816002Sroot } 7826002Sroot 78327185Sminshall #if 0 7846002Sroot netflush() 7856002Sroot { 7866002Sroot int n; 7876002Sroot 7886002Sroot if ((n = nfrontp - nbackp) > 0) 7896002Sroot n = write(net, nbackp, n); 7908346Ssam if (n < 0) { 7918346Ssam if (errno == EWOULDBLOCK) 7928346Ssam return; 7938346Ssam /* should blow this guy away... */ 7948346Ssam return; 7958346Ssam } 7966002Sroot nbackp += n; 7976002Sroot if (nbackp == nfrontp) 7986002Sroot nbackp = nfrontp = netobuf; 7996002Sroot } 80027185Sminshall #else /* 0 */ 8016002Sroot 80227185Sminshall 80327185Sminshall /* 80427185Sminshall * netflush 80527185Sminshall * Send as much data as possible to the network, 80627185Sminshall * handling requests for urgent data. 80727185Sminshall */ 80827185Sminshall 80927185Sminshall 81027185Sminshall netflush() 81127185Sminshall { 81227185Sminshall int n; 81327185Sminshall 81427185Sminshall if ((n = nfrontp - nbackp) > 0) { 81527185Sminshall if (!neturg) { 81627185Sminshall n = write(net, nbackp, n); /* normal write */ 81727185Sminshall } else { 81827185Sminshall n = neturg - nbackp; 81927185Sminshall /* 82027185Sminshall * In 4.2 (and 4.3) systems, there is some question about 82127185Sminshall * what byte in a sendOOB operation is the "OOB" data. 82227185Sminshall * To make ourselves compatible, we only send ONE byte 82327185Sminshall * out of band, the one WE THINK should be OOB (though 82427185Sminshall * we really have more the TCP philosophy of urgent data 82527185Sminshall * rather than the Unix philosophy of OOB data). 82627185Sminshall */ 82727185Sminshall if (n > 1) { 82827185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 82927185Sminshall } else { 83027185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 83127185Sminshall } 83227185Sminshall } 83327185Sminshall } 83427185Sminshall if (n < 0) { 83527185Sminshall if (errno == EWOULDBLOCK) 83627185Sminshall return; 83727185Sminshall /* should blow this guy away... */ 83827185Sminshall return; 83927185Sminshall } 84027185Sminshall nbackp += n; 84127185Sminshall if (nbackp >= neturg) { 84227185Sminshall neturg = 0; 84327185Sminshall } 84427185Sminshall if (nbackp == nfrontp) { 84527185Sminshall nbackp = nfrontp = netobuf; 84627185Sminshall } 84727185Sminshall } 84827185Sminshall #endif /* 0 */ 84927185Sminshall 8506002Sroot cleanup() 8516002Sroot { 8526002Sroot 8536002Sroot rmut(); 85410008Ssam vhangup(); /* XXX */ 85510191Ssam shutdown(net, 2); 8566002Sroot exit(1); 8576002Sroot } 8586002Sroot 8596002Sroot #include <utmp.h> 8606002Sroot 8616002Sroot struct utmp wtmp; 8626002Sroot char wtmpf[] = "/usr/adm/wtmp"; 86323567Sbloom char utmpf[] = "/etc/utmp"; 86423567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 86523567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 8666002Sroot 8676002Sroot rmut() 8686002Sroot { 8696002Sroot register f; 8706002Sroot int found = 0; 87123567Sbloom struct utmp *u, *utmp; 87223567Sbloom int nutmp; 87323567Sbloom struct stat statbf; 8746002Sroot 87523567Sbloom f = open(utmpf, O_RDWR); 8766002Sroot if (f >= 0) { 87723567Sbloom fstat(f, &statbf); 87823567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 87923567Sbloom if (!utmp) 88023567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 88123567Sbloom if (statbf.st_size && utmp) { 88223567Sbloom nutmp = read(f, utmp, statbf.st_size); 88323567Sbloom nutmp /= sizeof(struct utmp); 88423567Sbloom 88523567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 88623567Sbloom if (SCMPN(u->ut_line, line+5) || 88723567Sbloom u->ut_name[0]==0) 88823567Sbloom continue; 88923567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 89023567Sbloom SCPYN(u->ut_name, ""); 89123567Sbloom SCPYN(u->ut_host, ""); 89223567Sbloom time(&u->ut_time); 89323567Sbloom write(f, (char *)u, sizeof(wtmp)); 89423567Sbloom found++; 89523567Sbloom } 8966002Sroot } 8976002Sroot close(f); 8986002Sroot } 8996002Sroot if (found) { 90017583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 9016002Sroot if (f >= 0) { 9026002Sroot SCPYN(wtmp.ut_line, line+5); 9036002Sroot SCPYN(wtmp.ut_name, ""); 90412683Ssam SCPYN(wtmp.ut_host, ""); 9056002Sroot time(&wtmp.ut_time); 90623567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 9076002Sroot close(f); 9086002Sroot } 9096002Sroot } 9106002Sroot chmod(line, 0666); 9116002Sroot chown(line, 0, 0); 9126002Sroot line[strlen("/dev/")] = 'p'; 9136002Sroot chmod(line, 0666); 9146002Sroot chown(line, 0, 0); 9156002Sroot } 916