rlogin.c revision 200462
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1983, 1990, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 496196Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596196Sdes * All rights reserved. 61590Srgrimes * 796196Sdes * Portions of this software were developed for the FreeBSD Project by 896196Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996196Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096196Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196196Sdes * 121590Srgrimes * Redistribution and use in source and binary forms, with or without 131590Srgrimes * modification, are permitted provided that the following conditions 141590Srgrimes * are met: 151590Srgrimes * 1. Redistributions of source code must retain the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer. 171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181590Srgrimes * notice, this list of conditions and the following disclaimer in the 191590Srgrimes * documentation and/or other materials provided with the distribution. 201590Srgrimes * 3. All advertising materials mentioning features or use of this software 211590Srgrimes * must display the following acknowledgement: 221590Srgrimes * This product includes software developed by the University of 231590Srgrimes * California, Berkeley and its contributors. 241590Srgrimes * 4. Neither the name of the University nor the names of its contributors 251590Srgrimes * may be used to endorse or promote products derived from this software 261590Srgrimes * without specific prior written permission. 271590Srgrimes * 281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381590Srgrimes * SUCH DAMAGE. 391590Srgrimes */ 401590Srgrimes 411590Srgrimes#ifndef lint 4227919Scharnierstatic const char copyright[] = 431590Srgrimes"@(#) Copyright (c) 1983, 1990, 1993\n\ 441590Srgrimes The Regents of the University of California. All rights reserved.\n"; 451590Srgrimes#endif /* not lint */ 461590Srgrimes 47105235Scharnier#if 0 481590Srgrimes#ifndef lint 4929922Smarkmstatic const char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93"; 501590Srgrimes#endif /* not lint */ 51105235Scharnier#endif 521590Srgrimes 53105235Scharnier#include <sys/cdefs.h> 54105235Scharnier__FBSDID("$FreeBSD: head/usr.bin/rlogin/rlogin.c 200462 2009-12-13 03:14:06Z delphij $"); 55105235Scharnier 561590Srgrimes/* 571590Srgrimes * rlogin - remote login 581590Srgrimes */ 5995621Smarkm 601590Srgrimes#include <sys/param.h> 61120547Stjr#include <sys/ioctl.h> 621590Srgrimes#include <sys/socket.h> 631590Srgrimes#include <sys/time.h> 641590Srgrimes#include <sys/resource.h> 651590Srgrimes#include <sys/wait.h> 661590Srgrimes 671590Srgrimes#include <netinet/in.h> 681590Srgrimes#include <netinet/in_systm.h> 691590Srgrimes#include <netinet/ip.h> 708232Sdg#include <netinet/tcp.h> 711590Srgrimes 7227919Scharnier#include <err.h> 731590Srgrimes#include <errno.h> 741590Srgrimes#include <fcntl.h> 75200462Sdelphij#include <libutil.h> 761590Srgrimes#include <netdb.h> 77200462Sdelphij#include <paths.h> 781590Srgrimes#include <pwd.h> 791590Srgrimes#include <setjmp.h> 80120547Stjr#include <termios.h> 811590Srgrimes#include <signal.h> 821590Srgrimes#include <stdio.h> 831590Srgrimes#include <stdlib.h> 841590Srgrimes#include <string.h> 851590Srgrimes#include <unistd.h> 861590Srgrimes 871590Srgrimes#ifndef TIOCPKT_WINDOW 881590Srgrimes#define TIOCPKT_WINDOW 0x80 891590Srgrimes#endif 901590Srgrimes 911590Srgrimes/* concession to Sun */ 921590Srgrimes#ifndef SIGUSR1 931590Srgrimes#define SIGUSR1 30 941590Srgrimes#endif 951590Srgrimes 96120547Stjrint eight, rem; 97120547Stjrstruct termios deftty; 98120547Stjr 9957232Sshinint family = PF_UNSPEC; 1001590Srgrimes 1011590Srgrimesint noescape; 1021590Srgrimesu_char escapechar = '~'; 1031590Srgrimes 1041590Srgrimes#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 1051590Srgrimesstruct winsize winsize; 1061590Srgrimes 10792921Simpvoid catch_child(int); 10892921Simpvoid copytochild(int); 10992921Simpvoid doit(long) __dead2; 11092921Simpvoid done(int) __dead2; 11192921Simpvoid echo(char); 112105268Smarkmu_int getescape(const char *); 11392921Simpvoid lostpeer(int); 11492921Simpvoid mode(int); 11595621Smarkmvoid msg(const char *); 11692921Simpvoid oob(int); 11792921Simpint reader(int); 11892921Simpvoid sendwindow(void); 11992921Simpvoid setsignal(int); 12092921Simpvoid sigwinch(int); 12192921Simpvoid stop(char); 12292921Simpvoid usage(void) __dead2; 12392921Simpvoid writer(void); 12492921Simpvoid writeroob(int); 1251590Srgrimes 1261590Srgrimesint 12793057Simpmain(int argc, char *argv[]) 1281590Srgrimes{ 1291590Srgrimes struct passwd *pw; 1301590Srgrimes struct servent *sp; 131120547Stjr struct termios tty; 1321590Srgrimes long omask; 133105268Smarkm int argoff, ch, dflag, Dflag, one; 134105268Smarkm uid_t uid; 13547549Sbde char *host, *localname, *p, *user, term[1024]; 136120547Stjr speed_t ospeed; 13796196Sdes struct sockaddr_storage ss; 138148726Sstefanf socklen_t sslen; 139152397Sdwmalone size_t len, len2; 140152397Sdwmalone int i; 1411590Srgrimes 1428232Sdg argoff = dflag = Dflag = 0; 1431590Srgrimes one = 1; 14447549Sbde host = localname = user = NULL; 1451590Srgrimes 14629922Smarkm if ((p = rindex(argv[0], '/'))) 1471590Srgrimes ++p; 1481590Srgrimes else 1491590Srgrimes p = argv[0]; 1501590Srgrimes 1511590Srgrimes if (strcmp(p, "rlogin")) 1521590Srgrimes host = p; 1531590Srgrimes 1541590Srgrimes /* handle "rlogin host flags" */ 1551590Srgrimes if (!host && argc > 2 && argv[1][0] != '-') { 1561590Srgrimes host = argv[1]; 1571590Srgrimes argoff = 1; 1581590Srgrimes } 1591590Srgrimes 160140569Sru#define OPTIONS "468DEde:i:l:" 16124360Simp while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 1621590Srgrimes switch(ch) { 16357232Sshin case '4': 16457232Sshin family = PF_INET; 16557232Sshin break; 16657232Sshin 16757232Sshin case '6': 16857232Sshin family = PF_INET6; 16957232Sshin break; 17057232Sshin 1711590Srgrimes case '8': 1721590Srgrimes eight = 1; 1731590Srgrimes break; 1748232Sdg case 'D': 1758232Sdg Dflag = 1; 1768232Sdg break; 1771590Srgrimes case 'E': 1781590Srgrimes noescape = 1; 1791590Srgrimes break; 1801590Srgrimes case 'd': 1811590Srgrimes dflag = 1; 1821590Srgrimes break; 1831590Srgrimes case 'e': 1841590Srgrimes noescape = 0; 1851590Srgrimes escapechar = getescape(optarg); 1861590Srgrimes break; 18747488Speter case 'i': 18847549Sbde if (getuid() != 0) 18947549Sbde errx(1, "-i user: permission denied"); 19047488Speter localname = optarg; 19147488Speter break; 1921590Srgrimes case 'l': 1931590Srgrimes user = optarg; 1941590Srgrimes break; 1951590Srgrimes case '?': 1961590Srgrimes default: 1971590Srgrimes usage(); 1981590Srgrimes } 1991590Srgrimes optind += argoff; 2001590Srgrimes 2011590Srgrimes /* if haven't gotten a host yet, do so */ 20234897Smarkm if (!host && !(host = argv[optind++])) 2031590Srgrimes usage(); 2041590Srgrimes 20534897Smarkm if (argv[optind]) 2061590Srgrimes usage(); 2071590Srgrimes 20827919Scharnier if (!(pw = getpwuid(uid = getuid()))) 20927919Scharnier errx(1, "unknown user id"); 2101590Srgrimes if (!user) 2111590Srgrimes user = pw->pw_name; 21247488Speter if (!localname) 21347488Speter localname = pw->pw_name; 2141590Srgrimes 2151590Srgrimes sp = NULL; 216105268Smarkm sp = getservbyname("login", "tcp"); 2171590Srgrimes if (sp == NULL) 21827919Scharnier errx(1, "login/tcp: unknown service"); 2191590Srgrimes 220120547Stjr if ((p = getenv("TERM")) != NULL) 221120547Stjr (void)strlcpy(term, p, sizeof(term)); 222120547Stjr len = strlen(term); 223120547Stjr if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) { 224120547Stjr /* start at 2 to include the / */ 225120547Stjr for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++) 226120547Stjr i /= 10; 227120547Stjr if (len + len2 < sizeof(term)) 228120547Stjr (void)snprintf(term + len, len2 + 1, "/%d", ospeed); 2291590Srgrimes } 2301590Srgrimes 2311590Srgrimes (void)get_window_size(0, &winsize); 2321590Srgrimes 2331590Srgrimes (void)signal(SIGPIPE, lostpeer); 2341590Srgrimes /* will use SIGUSR1 for window size hack, so hold it off */ 2351590Srgrimes omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 2361590Srgrimes /* 2371590Srgrimes * We set SIGURG and SIGUSR1 below so that an 2381590Srgrimes * incoming signal will be held pending rather than being 2391590Srgrimes * discarded. Note that these routines will be ready to get 2401590Srgrimes * a signal by the time that they are unblocked below. 2411590Srgrimes */ 2421590Srgrimes (void)signal(SIGURG, copytochild); 2431590Srgrimes (void)signal(SIGUSR1, writeroob); 2441590Srgrimes 24557232Sshin rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 2461590Srgrimes 2471590Srgrimes if (rem < 0) 2481590Srgrimes exit(1); 2491590Srgrimes 2501590Srgrimes if (dflag && 2511590Srgrimes setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 25227919Scharnier warn("setsockopt"); 2538232Sdg if (Dflag && 2548232Sdg setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 25527919Scharnier warn("setsockopt NODELAY (ignored)"); 2568232Sdg 25756590Sshin sslen = sizeof(ss); 2581590Srgrimes one = IPTOS_LOWDELAY; 25956590Sshin if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 26056590Sshin ss.ss_family == AF_INET) { 26156590Sshin if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 26256590Sshin sizeof(int)) < 0) 26356590Sshin warn("setsockopt TOS (ignored)"); 26456590Sshin } else 26556590Sshin if (ss.ss_family == AF_INET) 26656590Sshin warn("setsockopt getsockname failed"); 2671590Srgrimes 2681590Srgrimes (void)setuid(uid); 2691590Srgrimes doit(omask); 2701590Srgrimes /*NOTREACHED*/ 2711590Srgrimes} 2721590Srgrimes 273120547Stjrint child; 2741590Srgrimes 2751590Srgrimesvoid 27693057Simpdoit(long omask) 2771590Srgrimes{ 2781590Srgrimes 2791590Srgrimes (void)signal(SIGINT, SIG_IGN); 2801590Srgrimes setsignal(SIGHUP); 2811590Srgrimes setsignal(SIGQUIT); 282120547Stjr mode(1); 2831590Srgrimes child = fork(); 2841590Srgrimes if (child == -1) { 28527919Scharnier warn("fork"); 2861590Srgrimes done(1); 2871590Srgrimes } 2881590Srgrimes if (child == 0) { 2891590Srgrimes if (reader(omask) == 0) { 290105235Scharnier msg("connection closed"); 2911590Srgrimes exit(0); 2921590Srgrimes } 2931590Srgrimes sleep(1); 294105235Scharnier msg("\007connection closed"); 2951590Srgrimes exit(1); 2961590Srgrimes } 2971590Srgrimes 2981590Srgrimes /* 2991590Srgrimes * We may still own the socket, and may have a pending SIGURG (or might 3001590Srgrimes * receive one soon) that we really want to send to the reader. When 3011590Srgrimes * one of these comes in, the trap copytochild simply copies such 3021590Srgrimes * signals to the child. We can now unblock SIGURG and SIGUSR1 3031590Srgrimes * that were set above. 3041590Srgrimes */ 3051590Srgrimes (void)sigsetmask(omask); 3061590Srgrimes (void)signal(SIGCHLD, catch_child); 3071590Srgrimes writer(); 308105235Scharnier msg("closed connection"); 3091590Srgrimes done(0); 3101590Srgrimes} 3111590Srgrimes 3121590Srgrimes/* trap a signal, unless it is being ignored. */ 3131590Srgrimesvoid 31493057Simpsetsignal(int sig) 3151590Srgrimes{ 3161590Srgrimes int omask = sigblock(sigmask(sig)); 3171590Srgrimes 3181590Srgrimes if (signal(sig, exit) == SIG_IGN) 3191590Srgrimes (void)signal(sig, SIG_IGN); 3201590Srgrimes (void)sigsetmask(omask); 3211590Srgrimes} 3221590Srgrimes 32318286Sbdevoid 32493057Simpdone(int status) 3251590Srgrimes{ 3261590Srgrimes int w, wstatus; 3271590Srgrimes 3281590Srgrimes mode(0); 3291590Srgrimes if (child > 0) { 3301590Srgrimes /* make sure catch_child does not snap it up */ 3311590Srgrimes (void)signal(SIGCHLD, SIG_DFL); 3321590Srgrimes if (kill(child, SIGKILL) >= 0) 3331590Srgrimes while ((w = wait(&wstatus)) > 0 && w != child); 3341590Srgrimes } 3351590Srgrimes exit(status); 3361590Srgrimes} 3371590Srgrimes 3381590Srgrimesint dosigwinch; 3391590Srgrimes 3401590Srgrimes/* 3411590Srgrimes * This is called when the reader process gets the out-of-band (urgent) 3421590Srgrimes * request to turn on the window-changing protocol. 3431590Srgrimes */ 344105268Smarkm/* ARGSUSED */ 3451590Srgrimesvoid 34695621Smarkmwriteroob(int signo __unused) 3471590Srgrimes{ 3481590Srgrimes if (dosigwinch == 0) { 3491590Srgrimes sendwindow(); 3501590Srgrimes (void)signal(SIGWINCH, sigwinch); 3511590Srgrimes } 3521590Srgrimes dosigwinch = 1; 3531590Srgrimes} 3541590Srgrimes 355105268Smarkm/* ARGSUSED */ 3561590Srgrimesvoid 35795621Smarkmcatch_child(int signo __unused) 3581590Srgrimes{ 35997788Smike pid_t pid; 36097788Smike int status; 3611590Srgrimes 3621590Srgrimes for (;;) { 36397788Smike pid = wait3(&status, WNOHANG|WUNTRACED, NULL); 3641590Srgrimes if (pid == 0) 3651590Srgrimes return; 3661590Srgrimes /* if the child (reader) dies, just quit */ 3671590Srgrimes if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 36897788Smike done(WTERMSIG(status) | WEXITSTATUS(status)); 3691590Srgrimes } 3701590Srgrimes /* NOTREACHED */ 3711590Srgrimes} 3721590Srgrimes 3731590Srgrimes/* 3741590Srgrimes * writer: write to remote: 0 -> line. 3751590Srgrimes * ~. terminate 3761590Srgrimes * ~^Z suspend rlogin process. 3771590Srgrimes * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 3781590Srgrimes */ 3791590Srgrimesvoid 38093057Simpwriter(void) 3811590Srgrimes{ 38293057Simp int bol, local, n; 3831590Srgrimes char c; 3841590Srgrimes 3851590Srgrimes bol = 1; /* beginning of line */ 3861590Srgrimes local = 0; 3871590Srgrimes for (;;) { 3881590Srgrimes n = read(STDIN_FILENO, &c, 1); 3891590Srgrimes if (n <= 0) { 3901590Srgrimes if (n < 0 && errno == EINTR) 3911590Srgrimes continue; 3921590Srgrimes break; 3931590Srgrimes } 3941590Srgrimes /* 3951590Srgrimes * If we're at the beginning of the line and recognize a 3961590Srgrimes * command character, then we echo locally. Otherwise, 3971590Srgrimes * characters are echo'd remotely. If the command character 3981590Srgrimes * is doubled, this acts as a force and local echo is 3991590Srgrimes * suppressed. 4001590Srgrimes */ 4011590Srgrimes if (bol) { 4021590Srgrimes bol = 0; 4031590Srgrimes if (!noescape && c == escapechar) { 4041590Srgrimes local = 1; 4051590Srgrimes continue; 4061590Srgrimes } 4071590Srgrimes } else if (local) { 4081590Srgrimes local = 0; 409120547Stjr if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { 4101590Srgrimes echo(c); 4111590Srgrimes break; 4121590Srgrimes } 413120547Stjr if (CCEQ(deftty.c_cc[VSUSP], c) || 414120547Stjr CCEQ(deftty.c_cc[VDSUSP], c)) { 4151590Srgrimes bol = 1; 4161590Srgrimes echo(c); 4171590Srgrimes stop(c); 4181590Srgrimes continue; 4191590Srgrimes } 4201590Srgrimes if (c != escapechar) 421105268Smarkm (void)write(rem, &escapechar, 1); 4221590Srgrimes } 4231590Srgrimes 424105268Smarkm if (write(rem, &c, 1) == 0) { 425105268Smarkm msg("line gone"); 426105268Smarkm break; 427105268Smarkm } 428120547Stjr bol = CCEQ(deftty.c_cc[VKILL], c) || 429120547Stjr CCEQ(deftty.c_cc[VEOF], c) || 430120547Stjr CCEQ(deftty.c_cc[VINTR], c) || 431120547Stjr CCEQ(deftty.c_cc[VSUSP], c) || 4321590Srgrimes c == '\r' || c == '\n'; 4331590Srgrimes } 4341590Srgrimes} 4351590Srgrimes 4361590Srgrimesvoid 43793057Simpecho(char c) 4381590Srgrimes{ 43993057Simp char *p; 4401590Srgrimes char buf[8]; 4411590Srgrimes 4421590Srgrimes p = buf; 4431590Srgrimes c &= 0177; 4441590Srgrimes *p++ = escapechar; 4451590Srgrimes if (c < ' ') { 4461590Srgrimes *p++ = '^'; 4471590Srgrimes *p++ = c + '@'; 4481590Srgrimes } else if (c == 0177) { 4491590Srgrimes *p++ = '^'; 4501590Srgrimes *p++ = '?'; 4511590Srgrimes } else 4521590Srgrimes *p++ = c; 4531590Srgrimes *p++ = '\r'; 4541590Srgrimes *p++ = '\n'; 4551590Srgrimes (void)write(STDOUT_FILENO, buf, p - buf); 4561590Srgrimes} 4571590Srgrimes 4581590Srgrimesvoid 4591590Srgrimesstop(char cmdc) 4601590Srgrimes{ 4611590Srgrimes mode(0); 4621590Srgrimes (void)signal(SIGCHLD, SIG_IGN); 463120547Stjr (void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP); 4641590Srgrimes (void)signal(SIGCHLD, catch_child); 4651590Srgrimes mode(1); 4661590Srgrimes sigwinch(0); /* check for size changes */ 4671590Srgrimes} 4681590Srgrimes 469105268Smarkm/* ARGSUSED */ 4701590Srgrimesvoid 47195621Smarkmsigwinch(int signo __unused) 4721590Srgrimes{ 4731590Srgrimes struct winsize ws; 4741590Srgrimes 4751590Srgrimes if (dosigwinch && get_window_size(0, &ws) == 0 && 4761590Srgrimes bcmp(&ws, &winsize, sizeof(ws))) { 4771590Srgrimes winsize = ws; 4781590Srgrimes sendwindow(); 4791590Srgrimes } 4801590Srgrimes} 4811590Srgrimes 4821590Srgrimes/* 4831590Srgrimes * Send the window size to the server via the magic escape 4841590Srgrimes */ 4851590Srgrimesvoid 48693057Simpsendwindow(void) 4871590Srgrimes{ 4881590Srgrimes struct winsize *wp; 4891590Srgrimes char obuf[4 + sizeof (struct winsize)]; 4901590Srgrimes 4911590Srgrimes wp = (struct winsize *)(obuf+4); 4921590Srgrimes obuf[0] = 0377; 4931590Srgrimes obuf[1] = 0377; 4941590Srgrimes obuf[2] = 's'; 4951590Srgrimes obuf[3] = 's'; 4961590Srgrimes wp->ws_row = htons(winsize.ws_row); 4971590Srgrimes wp->ws_col = htons(winsize.ws_col); 4981590Srgrimes wp->ws_xpixel = htons(winsize.ws_xpixel); 4991590Srgrimes wp->ws_ypixel = htons(winsize.ws_ypixel); 5001590Srgrimes 501105268Smarkm (void)write(rem, obuf, sizeof(obuf)); 5021590Srgrimes} 5031590Srgrimes 5041590Srgrimes/* 5051590Srgrimes * reader: read from remote: line -> 1 5061590Srgrimes */ 5071590Srgrimes#define READING 1 5081590Srgrimes#define WRITING 2 5091590Srgrimes 5101590Srgrimesjmp_buf rcvtop; 511105268Smarkmint rcvcnt, rcvstate; 512105268Smarkmpid_t ppid; 5131590Srgrimeschar rcvbuf[8 * 1024]; 5141590Srgrimes 515105268Smarkm/* ARGSUSED */ 5161590Srgrimesvoid 51795621Smarkmoob(int signo __unused) 5181590Srgrimes{ 519120547Stjr struct termios tty; 520120547Stjr int atmark, n, rcvd; 5211590Srgrimes char waste[BUFSIZ], mark; 5221590Srgrimes 5231590Srgrimes rcvd = 0; 5241590Srgrimes while (recv(rem, &mark, 1, MSG_OOB) < 0) { 5251590Srgrimes switch (errno) { 5261590Srgrimes case EWOULDBLOCK: 5271590Srgrimes /* 5281590Srgrimes * Urgent data not here yet. It may not be possible 5291590Srgrimes * to send it yet if we are blocked for output and 5301590Srgrimes * our input buffer is full. 5311590Srgrimes */ 53295621Smarkm if (rcvcnt < (int)sizeof(rcvbuf)) { 5331590Srgrimes n = read(rem, rcvbuf + rcvcnt, 5341590Srgrimes sizeof(rcvbuf) - rcvcnt); 5351590Srgrimes if (n <= 0) 5361590Srgrimes return; 5371590Srgrimes rcvd += n; 5381590Srgrimes } else { 5391590Srgrimes n = read(rem, waste, sizeof(waste)); 5401590Srgrimes if (n <= 0) 5411590Srgrimes return; 5421590Srgrimes } 5431590Srgrimes continue; 5441590Srgrimes default: 5451590Srgrimes return; 5461590Srgrimes } 5471590Srgrimes } 5481590Srgrimes if (mark & TIOCPKT_WINDOW) { 5491590Srgrimes /* Let server know about window size changes */ 5501590Srgrimes (void)kill(ppid, SIGUSR1); 5511590Srgrimes } 5521590Srgrimes if (!eight && (mark & TIOCPKT_NOSTOP)) { 553120547Stjr (void)tcgetattr(0, &tty); 554120547Stjr tty.c_iflag &= ~IXON; 555120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 5561590Srgrimes } 5571590Srgrimes if (!eight && (mark & TIOCPKT_DOSTOP)) { 558120547Stjr (void)tcgetattr(0, &tty); 559120547Stjr tty.c_iflag |= (deftty.c_iflag & IXON); 560120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 5611590Srgrimes } 5621590Srgrimes if (mark & TIOCPKT_FLUSHWRITE) { 563120547Stjr (void)tcflush(1, TCIOFLUSH); 5641590Srgrimes for (;;) { 5651590Srgrimes if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 56627919Scharnier warn("ioctl"); 5671590Srgrimes break; 5681590Srgrimes } 5691590Srgrimes if (atmark) 5701590Srgrimes break; 5711590Srgrimes n = read(rem, waste, sizeof (waste)); 5721590Srgrimes if (n <= 0) 5731590Srgrimes break; 5741590Srgrimes } 5751590Srgrimes /* 5761590Srgrimes * Don't want any pending data to be output, so clear the recv 5771590Srgrimes * buffer. If we were hanging on a write when interrupted, 5781590Srgrimes * don't want it to restart. If we were reading, restart 5791590Srgrimes * anyway. 5801590Srgrimes */ 5811590Srgrimes rcvcnt = 0; 5821590Srgrimes longjmp(rcvtop, 1); 5831590Srgrimes } 5841590Srgrimes 5851590Srgrimes /* oob does not do FLUSHREAD (alas!) */ 5861590Srgrimes 5871590Srgrimes /* 5881590Srgrimes * If we filled the receive buffer while a read was pending, longjmp 5891590Srgrimes * to the top to restart appropriately. Don't abort a pending write, 5901590Srgrimes * however, or we won't know how much was written. 5911590Srgrimes */ 5921590Srgrimes if (rcvd && rcvstate == READING) 5931590Srgrimes longjmp(rcvtop, 1); 5941590Srgrimes} 5951590Srgrimes 5961590Srgrimes/* reader: read from remote: line -> 1 */ 5971590Srgrimesint 59893057Simpreader(int omask) 5991590Srgrimes{ 600105268Smarkm int n, remaining; 6011590Srgrimes char *bufp; 602105268Smarkm pid_t pid; 6031590Srgrimes 604105268Smarkm pid = getpid(); 6051590Srgrimes (void)signal(SIGTTOU, SIG_IGN); 6061590Srgrimes (void)signal(SIGURG, oob); 60791434Sfenner (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 6081590Srgrimes ppid = getppid(); 6091590Srgrimes (void)fcntl(rem, F_SETOWN, pid); 6101590Srgrimes (void)setjmp(rcvtop); 6111590Srgrimes (void)sigsetmask(omask); 6121590Srgrimes bufp = rcvbuf; 6131590Srgrimes for (;;) { 6141590Srgrimes while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 6151590Srgrimes rcvstate = WRITING; 6161590Srgrimes n = write(STDOUT_FILENO, bufp, remaining); 6171590Srgrimes if (n < 0) { 6181590Srgrimes if (errno != EINTR) 6191590Srgrimes return (-1); 6201590Srgrimes continue; 6211590Srgrimes } 6221590Srgrimes bufp += n; 6231590Srgrimes } 6241590Srgrimes bufp = rcvbuf; 6251590Srgrimes rcvcnt = 0; 6261590Srgrimes rcvstate = READING; 6271590Srgrimes 628105268Smarkm rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 6291590Srgrimes if (rcvcnt == 0) 6301590Srgrimes return (0); 6311590Srgrimes if (rcvcnt < 0) { 6321590Srgrimes if (errno == EINTR) 6331590Srgrimes continue; 63427919Scharnier warn("read"); 6351590Srgrimes return (-1); 6361590Srgrimes } 6371590Srgrimes } 6381590Srgrimes} 6391590Srgrimes 6401590Srgrimesvoid 64193057Simpmode(int f) 6421590Srgrimes{ 643120547Stjr struct termios tty; 6441590Srgrimes 645120547Stjr switch (f) { 6461590Srgrimes case 0: 647120547Stjr (void)tcsetattr(0, TCSANOW, &deftty); 6481590Srgrimes break; 6491590Srgrimes case 1: 650120547Stjr (void)tcgetattr(0, &deftty); 651120547Stjr tty = deftty; 652120547Stjr /* This is loosely derived from sys/kern/tty_compat.c. */ 653120547Stjr tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); 654120547Stjr tty.c_iflag &= ~ICRNL; 655120547Stjr tty.c_oflag &= ~OPOST; 656120547Stjr tty.c_cc[VMIN] = 1; 657120547Stjr tty.c_cc[VTIME] = 0; 658120547Stjr if (eight) { 659120547Stjr tty.c_iflag &= IXOFF; 660120547Stjr tty.c_cflag &= ~(CSIZE|PARENB); 661120547Stjr tty.c_cflag |= CS8; 662120547Stjr } 663120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 6641590Srgrimes break; 6651590Srgrimes default: 6661590Srgrimes return; 6671590Srgrimes } 6681590Srgrimes} 6691590Srgrimes 670105268Smarkm/* ARGSUSED */ 6711590Srgrimesvoid 67295621Smarkmlostpeer(int signo __unused) 6731590Srgrimes{ 6741590Srgrimes (void)signal(SIGPIPE, SIG_IGN); 675105235Scharnier msg("\007connection closed"); 6761590Srgrimes done(1); 6771590Srgrimes} 6781590Srgrimes 67991434Sfenner/* copy SIGURGs to the child process via SIGUSR1. */ 680105268Smarkm/* ARGSUSED */ 6811590Srgrimesvoid 68295621Smarkmcopytochild(int signo __unused) 6831590Srgrimes{ 68491434Sfenner (void)kill(child, SIGUSR1); 6851590Srgrimes} 6861590Srgrimes 6871590Srgrimesvoid 68895621Smarkmmsg(const char *str) 6891590Srgrimes{ 6901590Srgrimes (void)fprintf(stderr, "rlogin: %s\r\n", str); 6911590Srgrimes} 6921590Srgrimes 6931590Srgrimesvoid 69493057Simpusage(void) 6951590Srgrimes{ 6961590Srgrimes (void)fprintf(stderr, 69757232Sshin "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 698120547Stjr "8DEd", " "); 6991590Srgrimes exit(1); 7001590Srgrimes} 7011590Srgrimes 7021590Srgrimesu_int 703105268Smarkmgetescape(const char *p) 7041590Srgrimes{ 7051590Srgrimes long val; 706105268Smarkm size_t len; 7071590Srgrimes 7081590Srgrimes if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 7091590Srgrimes return ((u_int)*p); 7101590Srgrimes /* otherwise, \nnn */ 7111590Srgrimes if (*p == '\\' && len >= 2 && len <= 4) { 7121590Srgrimes val = strtol(++p, NULL, 8); 7131590Srgrimes for (;;) { 7141590Srgrimes if (!*++p) 7151590Srgrimes return ((u_int)val); 7161590Srgrimes if (*p < '0' || *p > '8') 7171590Srgrimes break; 7181590Srgrimes } 7191590Srgrimes } 7201590Srgrimes msg("illegal option value -- e"); 7211590Srgrimes usage(); 7221590Srgrimes /* NOTREACHED */ 7231590Srgrimes} 724