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*27187Sminshall static char sccsid[] = "@(#)telnetd.c 5.10 (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; 537*27187Sminshall 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: 5776002Sroot case WONT: 5786002Sroot case DO: 5796002Sroot case DONT: 5806002Sroot state = TS_WILL + (c - WILL); 5816002Sroot continue; 5826002Sroot 5836002Sroot case IAC: 5846002Sroot *pfrontp++ = c; 5856002Sroot break; 5866002Sroot } 5876002Sroot state = TS_DATA; 5886002Sroot break; 5896002Sroot 5906002Sroot case TS_BEGINNEG: 5916002Sroot if (c == IAC) 5926002Sroot state = TS_ENDNEG; 5936002Sroot break; 5946002Sroot 5956002Sroot case TS_ENDNEG: 5966002Sroot state = c == SE ? TS_DATA : TS_BEGINNEG; 5976002Sroot break; 5986002Sroot 5996002Sroot case TS_WILL: 6006002Sroot if (!hisopts[c]) 6016002Sroot willoption(c); 6026002Sroot state = TS_DATA; 6036002Sroot continue; 6046002Sroot 6056002Sroot case TS_WONT: 6066002Sroot if (hisopts[c]) 6076002Sroot wontoption(c); 6086002Sroot state = TS_DATA; 6096002Sroot continue; 6106002Sroot 6116002Sroot case TS_DO: 6126002Sroot if (!myopts[c]) 6136002Sroot dooption(c); 6146002Sroot state = TS_DATA; 6156002Sroot continue; 6166002Sroot 6176002Sroot case TS_DONT: 6186002Sroot if (myopts[c]) { 6196002Sroot myopts[c] = 0; 6206002Sroot sprintf(nfrontp, wont, c); 6218379Ssam nfrontp += sizeof (wont) - 2; 6226002Sroot } 6236002Sroot state = TS_DATA; 6246002Sroot continue; 6256002Sroot 6266002Sroot default: 6279218Ssam printf("telnetd: panic state=%d\n", state); 6286002Sroot exit(1); 6296002Sroot } 6306002Sroot } 6316002Sroot } 6326002Sroot 6336002Sroot willoption(option) 6346002Sroot int option; 6356002Sroot { 6366002Sroot char *fmt; 6376002Sroot 6386002Sroot switch (option) { 6396002Sroot 6406002Sroot case TELOPT_BINARY: 6416002Sroot mode(RAW, 0); 6426002Sroot goto common; 6436002Sroot 6446002Sroot case TELOPT_ECHO: 6456002Sroot mode(0, ECHO|CRMOD); 6466002Sroot /*FALL THRU*/ 6476002Sroot 6486002Sroot case TELOPT_SGA: 6496002Sroot common: 6506002Sroot hisopts[option] = 1; 6516002Sroot fmt = doopt; 6526002Sroot break; 6536002Sroot 6546002Sroot case TELOPT_TM: 6556002Sroot fmt = dont; 6566002Sroot break; 6576002Sroot 6586002Sroot default: 6596002Sroot fmt = dont; 6606002Sroot break; 6616002Sroot } 6626023Ssam sprintf(nfrontp, fmt, option); 6638379Ssam nfrontp += sizeof (dont) - 2; 6646002Sroot } 6656002Sroot 6666002Sroot wontoption(option) 6676002Sroot int option; 6686002Sroot { 6696002Sroot char *fmt; 6706002Sroot 6716002Sroot switch (option) { 6726002Sroot 6736002Sroot case TELOPT_ECHO: 6746002Sroot mode(ECHO|CRMOD, 0); 6756002Sroot goto common; 6766002Sroot 6776002Sroot case TELOPT_BINARY: 6786002Sroot mode(0, RAW); 6796002Sroot /*FALL THRU*/ 6806002Sroot 6816002Sroot case TELOPT_SGA: 6826002Sroot common: 6836002Sroot hisopts[option] = 0; 6846002Sroot fmt = dont; 6856002Sroot break; 6866002Sroot 6876002Sroot default: 6886002Sroot fmt = dont; 6896002Sroot } 6906002Sroot sprintf(nfrontp, fmt, option); 6918379Ssam nfrontp += sizeof (doopt) - 2; 6926002Sroot } 6936002Sroot 6946002Sroot dooption(option) 6956002Sroot int option; 6966002Sroot { 6976002Sroot char *fmt; 6986002Sroot 6996002Sroot switch (option) { 7006002Sroot 7016002Sroot case TELOPT_TM: 7026002Sroot fmt = wont; 7036002Sroot break; 7046002Sroot 7056002Sroot case TELOPT_ECHO: 7066002Sroot mode(ECHO|CRMOD, 0); 7076002Sroot goto common; 7086002Sroot 7096002Sroot case TELOPT_BINARY: 7106002Sroot mode(RAW, 0); 7116002Sroot /*FALL THRU*/ 7126002Sroot 7136002Sroot case TELOPT_SGA: 7146002Sroot common: 7156002Sroot fmt = will; 7166002Sroot break; 7176002Sroot 7186002Sroot default: 7196002Sroot fmt = wont; 7206002Sroot break; 7216002Sroot } 7226002Sroot sprintf(nfrontp, fmt, option); 7238379Ssam nfrontp += sizeof (doopt) - 2; 7246002Sroot } 7256002Sroot 7266002Sroot mode(on, off) 7276002Sroot int on, off; 7286002Sroot { 7296002Sroot struct sgttyb b; 7306002Sroot 7316002Sroot ptyflush(); 7326002Sroot ioctl(pty, TIOCGETP, &b); 7336002Sroot b.sg_flags |= on; 7346002Sroot b.sg_flags &= ~off; 7356002Sroot ioctl(pty, TIOCSETP, &b); 7366002Sroot } 7376002Sroot 7386002Sroot /* 7396002Sroot * Send interrupt to process on other side of pty. 7406002Sroot * If it is in raw mode, just write NULL; 7416002Sroot * otherwise, write intr char. 7426002Sroot */ 7436002Sroot interrupt() 7446002Sroot { 7456002Sroot struct sgttyb b; 7466002Sroot struct tchars tchars; 7476002Sroot 7486002Sroot ptyflush(); /* half-hearted */ 7496002Sroot ioctl(pty, TIOCGETP, &b); 7506002Sroot if (b.sg_flags & RAW) { 7516002Sroot *pfrontp++ = '\0'; 7526002Sroot return; 7536002Sroot } 7546002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 7556002Sroot '\177' : tchars.t_intrc; 7566002Sroot } 7576002Sroot 7586002Sroot ptyflush() 7596002Sroot { 7606002Sroot int n; 7616002Sroot 7626002Sroot if ((n = pfrontp - pbackp) > 0) 7636002Sroot n = write(pty, pbackp, n); 7648346Ssam if (n < 0) 7658346Ssam return; 7666002Sroot pbackp += n; 7676002Sroot if (pbackp == pfrontp) 7686002Sroot pbackp = pfrontp = ptyobuf; 7696002Sroot } 7706002Sroot 77127185Sminshall #if 0 7726002Sroot netflush() 7736002Sroot { 7746002Sroot int n; 7756002Sroot 7766002Sroot if ((n = nfrontp - nbackp) > 0) 7776002Sroot n = write(net, nbackp, n); 7788346Ssam if (n < 0) { 7798346Ssam if (errno == EWOULDBLOCK) 7808346Ssam return; 7818346Ssam /* should blow this guy away... */ 7828346Ssam return; 7838346Ssam } 7846002Sroot nbackp += n; 7856002Sroot if (nbackp == nfrontp) 7866002Sroot nbackp = nfrontp = netobuf; 7876002Sroot } 78827185Sminshall #else /* 0 */ 7896002Sroot 79027185Sminshall 79127185Sminshall /* 79227185Sminshall * netflush 79327185Sminshall * Send as much data as possible to the network, 79427185Sminshall * handling requests for urgent data. 79527185Sminshall */ 79627185Sminshall 79727185Sminshall 79827185Sminshall netflush() 79927185Sminshall { 80027185Sminshall int n; 80127185Sminshall 80227185Sminshall if ((n = nfrontp - nbackp) > 0) { 80327185Sminshall if (!neturg) { 80427185Sminshall n = write(net, nbackp, n); /* normal write */ 80527185Sminshall } else { 80627185Sminshall n = neturg - nbackp; 80727185Sminshall /* 80827185Sminshall * In 4.2 (and 4.3) systems, there is some question about 80927185Sminshall * what byte in a sendOOB operation is the "OOB" data. 81027185Sminshall * To make ourselves compatible, we only send ONE byte 81127185Sminshall * out of band, the one WE THINK should be OOB (though 81227185Sminshall * we really have more the TCP philosophy of urgent data 81327185Sminshall * rather than the Unix philosophy of OOB data). 81427185Sminshall */ 81527185Sminshall if (n > 1) { 81627185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 81727185Sminshall } else { 81827185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 81927185Sminshall } 82027185Sminshall } 82127185Sminshall } 82227185Sminshall if (n < 0) { 82327185Sminshall if (errno == EWOULDBLOCK) 82427185Sminshall return; 82527185Sminshall /* should blow this guy away... */ 82627185Sminshall return; 82727185Sminshall } 82827185Sminshall nbackp += n; 82927185Sminshall if (nbackp >= neturg) { 83027185Sminshall neturg = 0; 83127185Sminshall } 83227185Sminshall if (nbackp == nfrontp) { 83327185Sminshall nbackp = nfrontp = netobuf; 83427185Sminshall } 83527185Sminshall } 83627185Sminshall #endif /* 0 */ 83727185Sminshall 8386002Sroot cleanup() 8396002Sroot { 8406002Sroot 8416002Sroot rmut(); 84210008Ssam vhangup(); /* XXX */ 84310191Ssam shutdown(net, 2); 8446002Sroot exit(1); 8456002Sroot } 8466002Sroot 8476002Sroot #include <utmp.h> 8486002Sroot 8496002Sroot struct utmp wtmp; 8506002Sroot char wtmpf[] = "/usr/adm/wtmp"; 85123567Sbloom char utmpf[] = "/etc/utmp"; 85223567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 85323567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 8546002Sroot 8556002Sroot rmut() 8566002Sroot { 8576002Sroot register f; 8586002Sroot int found = 0; 85923567Sbloom struct utmp *u, *utmp; 86023567Sbloom int nutmp; 86123567Sbloom struct stat statbf; 8626002Sroot 86323567Sbloom f = open(utmpf, O_RDWR); 8646002Sroot if (f >= 0) { 86523567Sbloom fstat(f, &statbf); 86623567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 86723567Sbloom if (!utmp) 86823567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 86923567Sbloom if (statbf.st_size && utmp) { 87023567Sbloom nutmp = read(f, utmp, statbf.st_size); 87123567Sbloom nutmp /= sizeof(struct utmp); 87223567Sbloom 87323567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 87423567Sbloom if (SCMPN(u->ut_line, line+5) || 87523567Sbloom u->ut_name[0]==0) 87623567Sbloom continue; 87723567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 87823567Sbloom SCPYN(u->ut_name, ""); 87923567Sbloom SCPYN(u->ut_host, ""); 88023567Sbloom time(&u->ut_time); 88123567Sbloom write(f, (char *)u, sizeof(wtmp)); 88223567Sbloom found++; 88323567Sbloom } 8846002Sroot } 8856002Sroot close(f); 8866002Sroot } 8876002Sroot if (found) { 88817583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 8896002Sroot if (f >= 0) { 8906002Sroot SCPYN(wtmp.ut_line, line+5); 8916002Sroot SCPYN(wtmp.ut_name, ""); 89212683Ssam SCPYN(wtmp.ut_host, ""); 8936002Sroot time(&wtmp.ut_time); 89423567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 8956002Sroot close(f); 8966002Sroot } 8976002Sroot } 8986002Sroot chmod(line, 0666); 8996002Sroot chown(line, 0, 0); 9006002Sroot line[strlen("/dev/")] = 'p'; 9016002Sroot chmod(line, 0666); 9026002Sroot chown(line, 0, 0); 9036002Sroot } 904