rlogin.c revision 120547
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 120547 2003-09-28 08:54:56Z tjr $"); 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> 7540103Smarkm#include <libutil.h> 761590Srgrimes#include <netdb.h> 7796197Sdes#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; 138120547Stjr int i, len, len2, sslen; 1391590Srgrimes 1408232Sdg argoff = dflag = Dflag = 0; 1411590Srgrimes one = 1; 14247549Sbde host = localname = user = NULL; 1431590Srgrimes 14429922Smarkm if ((p = rindex(argv[0], '/'))) 1451590Srgrimes ++p; 1461590Srgrimes else 1471590Srgrimes p = argv[0]; 1481590Srgrimes 1491590Srgrimes if (strcmp(p, "rlogin")) 1501590Srgrimes host = p; 1511590Srgrimes 1521590Srgrimes /* handle "rlogin host flags" */ 1531590Srgrimes if (!host && argc > 2 && argv[1][0] != '-') { 1541590Srgrimes host = argv[1]; 1551590Srgrimes argoff = 1; 1561590Srgrimes } 1571590Srgrimes 158120547Stjr#define OPTIONS "468DEKde:i:l:" 15924360Simp while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 1601590Srgrimes switch(ch) { 16157232Sshin case '4': 16257232Sshin family = PF_INET; 16357232Sshin break; 16457232Sshin 16557232Sshin case '6': 16657232Sshin family = PF_INET6; 16757232Sshin break; 16857232Sshin 1691590Srgrimes case '8': 1701590Srgrimes eight = 1; 1711590Srgrimes break; 1728232Sdg case 'D': 1738232Sdg Dflag = 1; 1748232Sdg break; 1751590Srgrimes case 'E': 1761590Srgrimes noescape = 1; 1771590Srgrimes break; 1781590Srgrimes case 'd': 1791590Srgrimes dflag = 1; 1801590Srgrimes break; 1811590Srgrimes case 'e': 1821590Srgrimes noescape = 0; 1831590Srgrimes escapechar = getescape(optarg); 1841590Srgrimes break; 18547488Speter case 'i': 18647549Sbde if (getuid() != 0) 18747549Sbde errx(1, "-i user: permission denied"); 18847488Speter localname = optarg; 18947488Speter break; 1901590Srgrimes case 'l': 1911590Srgrimes user = optarg; 1921590Srgrimes break; 1931590Srgrimes case '?': 1941590Srgrimes default: 1951590Srgrimes usage(); 1961590Srgrimes } 1971590Srgrimes optind += argoff; 1981590Srgrimes 1991590Srgrimes /* if haven't gotten a host yet, do so */ 20034897Smarkm if (!host && !(host = argv[optind++])) 2011590Srgrimes usage(); 2021590Srgrimes 20334897Smarkm if (argv[optind]) 2041590Srgrimes usage(); 2051590Srgrimes 20627919Scharnier if (!(pw = getpwuid(uid = getuid()))) 20727919Scharnier errx(1, "unknown user id"); 2081590Srgrimes if (!user) 2091590Srgrimes user = pw->pw_name; 21047488Speter if (!localname) 21147488Speter localname = pw->pw_name; 2121590Srgrimes 2131590Srgrimes sp = NULL; 214105268Smarkm sp = getservbyname("login", "tcp"); 2151590Srgrimes if (sp == NULL) 21627919Scharnier errx(1, "login/tcp: unknown service"); 2171590Srgrimes 218120547Stjr if ((p = getenv("TERM")) != NULL) 219120547Stjr (void)strlcpy(term, p, sizeof(term)); 220120547Stjr len = strlen(term); 221120547Stjr if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) { 222120547Stjr /* start at 2 to include the / */ 223120547Stjr for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++) 224120547Stjr i /= 10; 225120547Stjr if (len + len2 < sizeof(term)) 226120547Stjr (void)snprintf(term + len, len2 + 1, "/%d", ospeed); 2271590Srgrimes } 2281590Srgrimes 2291590Srgrimes (void)get_window_size(0, &winsize); 2301590Srgrimes 2311590Srgrimes (void)signal(SIGPIPE, lostpeer); 2321590Srgrimes /* will use SIGUSR1 for window size hack, so hold it off */ 2331590Srgrimes omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 2341590Srgrimes /* 2351590Srgrimes * We set SIGURG and SIGUSR1 below so that an 2361590Srgrimes * incoming signal will be held pending rather than being 2371590Srgrimes * discarded. Note that these routines will be ready to get 2381590Srgrimes * a signal by the time that they are unblocked below. 2391590Srgrimes */ 2401590Srgrimes (void)signal(SIGURG, copytochild); 2411590Srgrimes (void)signal(SIGUSR1, writeroob); 2421590Srgrimes 24357232Sshin rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 2441590Srgrimes 2451590Srgrimes if (rem < 0) 2461590Srgrimes exit(1); 2471590Srgrimes 2481590Srgrimes if (dflag && 2491590Srgrimes setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 25027919Scharnier warn("setsockopt"); 2518232Sdg if (Dflag && 2528232Sdg setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 25327919Scharnier warn("setsockopt NODELAY (ignored)"); 2548232Sdg 25556590Sshin sslen = sizeof(ss); 2561590Srgrimes one = IPTOS_LOWDELAY; 25756590Sshin if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 25856590Sshin ss.ss_family == AF_INET) { 25956590Sshin if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 26056590Sshin sizeof(int)) < 0) 26156590Sshin warn("setsockopt TOS (ignored)"); 26256590Sshin } else 26356590Sshin if (ss.ss_family == AF_INET) 26456590Sshin warn("setsockopt getsockname failed"); 2651590Srgrimes 2661590Srgrimes (void)setuid(uid); 2671590Srgrimes doit(omask); 2681590Srgrimes /*NOTREACHED*/ 2691590Srgrimes} 2701590Srgrimes 271120547Stjrint child; 2721590Srgrimes 2731590Srgrimesvoid 27493057Simpdoit(long omask) 2751590Srgrimes{ 2761590Srgrimes 2771590Srgrimes (void)signal(SIGINT, SIG_IGN); 2781590Srgrimes setsignal(SIGHUP); 2791590Srgrimes setsignal(SIGQUIT); 280120547Stjr mode(1); 2811590Srgrimes child = fork(); 2821590Srgrimes if (child == -1) { 28327919Scharnier warn("fork"); 2841590Srgrimes done(1); 2851590Srgrimes } 2861590Srgrimes if (child == 0) { 2871590Srgrimes if (reader(omask) == 0) { 288105235Scharnier msg("connection closed"); 2891590Srgrimes exit(0); 2901590Srgrimes } 2911590Srgrimes sleep(1); 292105235Scharnier msg("\007connection closed"); 2931590Srgrimes exit(1); 2941590Srgrimes } 2951590Srgrimes 2961590Srgrimes /* 2971590Srgrimes * We may still own the socket, and may have a pending SIGURG (or might 2981590Srgrimes * receive one soon) that we really want to send to the reader. When 2991590Srgrimes * one of these comes in, the trap copytochild simply copies such 3001590Srgrimes * signals to the child. We can now unblock SIGURG and SIGUSR1 3011590Srgrimes * that were set above. 3021590Srgrimes */ 3031590Srgrimes (void)sigsetmask(omask); 3041590Srgrimes (void)signal(SIGCHLD, catch_child); 3051590Srgrimes writer(); 306105235Scharnier msg("closed connection"); 3071590Srgrimes done(0); 3081590Srgrimes} 3091590Srgrimes 3101590Srgrimes/* trap a signal, unless it is being ignored. */ 3111590Srgrimesvoid 31293057Simpsetsignal(int sig) 3131590Srgrimes{ 3141590Srgrimes int omask = sigblock(sigmask(sig)); 3151590Srgrimes 3161590Srgrimes if (signal(sig, exit) == SIG_IGN) 3171590Srgrimes (void)signal(sig, SIG_IGN); 3181590Srgrimes (void)sigsetmask(omask); 3191590Srgrimes} 3201590Srgrimes 32118286Sbdevoid 32293057Simpdone(int status) 3231590Srgrimes{ 3241590Srgrimes int w, wstatus; 3251590Srgrimes 3261590Srgrimes mode(0); 3271590Srgrimes if (child > 0) { 3281590Srgrimes /* make sure catch_child does not snap it up */ 3291590Srgrimes (void)signal(SIGCHLD, SIG_DFL); 3301590Srgrimes if (kill(child, SIGKILL) >= 0) 3311590Srgrimes while ((w = wait(&wstatus)) > 0 && w != child); 3321590Srgrimes } 3331590Srgrimes exit(status); 3341590Srgrimes} 3351590Srgrimes 3361590Srgrimesint dosigwinch; 3371590Srgrimes 3381590Srgrimes/* 3391590Srgrimes * This is called when the reader process gets the out-of-band (urgent) 3401590Srgrimes * request to turn on the window-changing protocol. 3411590Srgrimes */ 342105268Smarkm/* ARGSUSED */ 3431590Srgrimesvoid 34495621Smarkmwriteroob(int signo __unused) 3451590Srgrimes{ 3461590Srgrimes if (dosigwinch == 0) { 3471590Srgrimes sendwindow(); 3481590Srgrimes (void)signal(SIGWINCH, sigwinch); 3491590Srgrimes } 3501590Srgrimes dosigwinch = 1; 3511590Srgrimes} 3521590Srgrimes 353105268Smarkm/* ARGSUSED */ 3541590Srgrimesvoid 35595621Smarkmcatch_child(int signo __unused) 3561590Srgrimes{ 35797788Smike pid_t pid; 35897788Smike int status; 3591590Srgrimes 3601590Srgrimes for (;;) { 36197788Smike pid = wait3(&status, WNOHANG|WUNTRACED, NULL); 3621590Srgrimes if (pid == 0) 3631590Srgrimes return; 3641590Srgrimes /* if the child (reader) dies, just quit */ 3651590Srgrimes if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 36697788Smike done(WTERMSIG(status) | WEXITSTATUS(status)); 3671590Srgrimes } 3681590Srgrimes /* NOTREACHED */ 3691590Srgrimes} 3701590Srgrimes 3711590Srgrimes/* 3721590Srgrimes * writer: write to remote: 0 -> line. 3731590Srgrimes * ~. terminate 3741590Srgrimes * ~^Z suspend rlogin process. 3751590Srgrimes * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 3761590Srgrimes */ 3771590Srgrimesvoid 37893057Simpwriter(void) 3791590Srgrimes{ 38093057Simp int bol, local, n; 3811590Srgrimes char c; 3821590Srgrimes 3831590Srgrimes bol = 1; /* beginning of line */ 3841590Srgrimes local = 0; 3851590Srgrimes for (;;) { 3861590Srgrimes n = read(STDIN_FILENO, &c, 1); 3871590Srgrimes if (n <= 0) { 3881590Srgrimes if (n < 0 && errno == EINTR) 3891590Srgrimes continue; 3901590Srgrimes break; 3911590Srgrimes } 3921590Srgrimes /* 3931590Srgrimes * If we're at the beginning of the line and recognize a 3941590Srgrimes * command character, then we echo locally. Otherwise, 3951590Srgrimes * characters are echo'd remotely. If the command character 3961590Srgrimes * is doubled, this acts as a force and local echo is 3971590Srgrimes * suppressed. 3981590Srgrimes */ 3991590Srgrimes if (bol) { 4001590Srgrimes bol = 0; 4011590Srgrimes if (!noescape && c == escapechar) { 4021590Srgrimes local = 1; 4031590Srgrimes continue; 4041590Srgrimes } 4051590Srgrimes } else if (local) { 4061590Srgrimes local = 0; 407120547Stjr if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { 4081590Srgrimes echo(c); 4091590Srgrimes break; 4101590Srgrimes } 411120547Stjr if (CCEQ(deftty.c_cc[VSUSP], c) || 412120547Stjr CCEQ(deftty.c_cc[VDSUSP], c)) { 4131590Srgrimes bol = 1; 4141590Srgrimes echo(c); 4151590Srgrimes stop(c); 4161590Srgrimes continue; 4171590Srgrimes } 4181590Srgrimes if (c != escapechar) 419105268Smarkm (void)write(rem, &escapechar, 1); 4201590Srgrimes } 4211590Srgrimes 422105268Smarkm if (write(rem, &c, 1) == 0) { 423105268Smarkm msg("line gone"); 424105268Smarkm break; 425105268Smarkm } 426120547Stjr bol = CCEQ(deftty.c_cc[VKILL], c) || 427120547Stjr CCEQ(deftty.c_cc[VEOF], c) || 428120547Stjr CCEQ(deftty.c_cc[VINTR], c) || 429120547Stjr CCEQ(deftty.c_cc[VSUSP], c) || 4301590Srgrimes c == '\r' || c == '\n'; 4311590Srgrimes } 4321590Srgrimes} 4331590Srgrimes 4341590Srgrimesvoid 43593057Simpecho(char c) 4361590Srgrimes{ 43793057Simp char *p; 4381590Srgrimes char buf[8]; 4391590Srgrimes 4401590Srgrimes p = buf; 4411590Srgrimes c &= 0177; 4421590Srgrimes *p++ = escapechar; 4431590Srgrimes if (c < ' ') { 4441590Srgrimes *p++ = '^'; 4451590Srgrimes *p++ = c + '@'; 4461590Srgrimes } else if (c == 0177) { 4471590Srgrimes *p++ = '^'; 4481590Srgrimes *p++ = '?'; 4491590Srgrimes } else 4501590Srgrimes *p++ = c; 4511590Srgrimes *p++ = '\r'; 4521590Srgrimes *p++ = '\n'; 4531590Srgrimes (void)write(STDOUT_FILENO, buf, p - buf); 4541590Srgrimes} 4551590Srgrimes 4561590Srgrimesvoid 4571590Srgrimesstop(char cmdc) 4581590Srgrimes{ 4591590Srgrimes mode(0); 4601590Srgrimes (void)signal(SIGCHLD, SIG_IGN); 461120547Stjr (void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP); 4621590Srgrimes (void)signal(SIGCHLD, catch_child); 4631590Srgrimes mode(1); 4641590Srgrimes sigwinch(0); /* check for size changes */ 4651590Srgrimes} 4661590Srgrimes 467105268Smarkm/* ARGSUSED */ 4681590Srgrimesvoid 46995621Smarkmsigwinch(int signo __unused) 4701590Srgrimes{ 4711590Srgrimes struct winsize ws; 4721590Srgrimes 4731590Srgrimes if (dosigwinch && get_window_size(0, &ws) == 0 && 4741590Srgrimes bcmp(&ws, &winsize, sizeof(ws))) { 4751590Srgrimes winsize = ws; 4761590Srgrimes sendwindow(); 4771590Srgrimes } 4781590Srgrimes} 4791590Srgrimes 4801590Srgrimes/* 4811590Srgrimes * Send the window size to the server via the magic escape 4821590Srgrimes */ 4831590Srgrimesvoid 48493057Simpsendwindow(void) 4851590Srgrimes{ 4861590Srgrimes struct winsize *wp; 4871590Srgrimes char obuf[4 + sizeof (struct winsize)]; 4881590Srgrimes 4891590Srgrimes wp = (struct winsize *)(obuf+4); 4901590Srgrimes obuf[0] = 0377; 4911590Srgrimes obuf[1] = 0377; 4921590Srgrimes obuf[2] = 's'; 4931590Srgrimes obuf[3] = 's'; 4941590Srgrimes wp->ws_row = htons(winsize.ws_row); 4951590Srgrimes wp->ws_col = htons(winsize.ws_col); 4961590Srgrimes wp->ws_xpixel = htons(winsize.ws_xpixel); 4971590Srgrimes wp->ws_ypixel = htons(winsize.ws_ypixel); 4981590Srgrimes 499105268Smarkm (void)write(rem, obuf, sizeof(obuf)); 5001590Srgrimes} 5011590Srgrimes 5021590Srgrimes/* 5031590Srgrimes * reader: read from remote: line -> 1 5041590Srgrimes */ 5051590Srgrimes#define READING 1 5061590Srgrimes#define WRITING 2 5071590Srgrimes 5081590Srgrimesjmp_buf rcvtop; 509105268Smarkmint rcvcnt, rcvstate; 510105268Smarkmpid_t ppid; 5111590Srgrimeschar rcvbuf[8 * 1024]; 5121590Srgrimes 513105268Smarkm/* ARGSUSED */ 5141590Srgrimesvoid 51595621Smarkmoob(int signo __unused) 5161590Srgrimes{ 517120547Stjr struct termios tty; 518120547Stjr int atmark, n, rcvd; 5191590Srgrimes char waste[BUFSIZ], mark; 5201590Srgrimes 5211590Srgrimes rcvd = 0; 5221590Srgrimes while (recv(rem, &mark, 1, MSG_OOB) < 0) { 5231590Srgrimes switch (errno) { 5241590Srgrimes case EWOULDBLOCK: 5251590Srgrimes /* 5261590Srgrimes * Urgent data not here yet. It may not be possible 5271590Srgrimes * to send it yet if we are blocked for output and 5281590Srgrimes * our input buffer is full. 5291590Srgrimes */ 53095621Smarkm if (rcvcnt < (int)sizeof(rcvbuf)) { 5311590Srgrimes n = read(rem, rcvbuf + rcvcnt, 5321590Srgrimes sizeof(rcvbuf) - rcvcnt); 5331590Srgrimes if (n <= 0) 5341590Srgrimes return; 5351590Srgrimes rcvd += n; 5361590Srgrimes } else { 5371590Srgrimes n = read(rem, waste, sizeof(waste)); 5381590Srgrimes if (n <= 0) 5391590Srgrimes return; 5401590Srgrimes } 5411590Srgrimes continue; 5421590Srgrimes default: 5431590Srgrimes return; 5441590Srgrimes } 5451590Srgrimes } 5461590Srgrimes if (mark & TIOCPKT_WINDOW) { 5471590Srgrimes /* Let server know about window size changes */ 5481590Srgrimes (void)kill(ppid, SIGUSR1); 5491590Srgrimes } 5501590Srgrimes if (!eight && (mark & TIOCPKT_NOSTOP)) { 551120547Stjr (void)tcgetattr(0, &tty); 552120547Stjr tty.c_iflag &= ~IXON; 553120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 5541590Srgrimes } 5551590Srgrimes if (!eight && (mark & TIOCPKT_DOSTOP)) { 556120547Stjr (void)tcgetattr(0, &tty); 557120547Stjr tty.c_iflag |= (deftty.c_iflag & IXON); 558120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 5591590Srgrimes } 5601590Srgrimes if (mark & TIOCPKT_FLUSHWRITE) { 561120547Stjr (void)tcflush(1, TCIOFLUSH); 5621590Srgrimes for (;;) { 5631590Srgrimes if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 56427919Scharnier warn("ioctl"); 5651590Srgrimes break; 5661590Srgrimes } 5671590Srgrimes if (atmark) 5681590Srgrimes break; 5691590Srgrimes n = read(rem, waste, sizeof (waste)); 5701590Srgrimes if (n <= 0) 5711590Srgrimes break; 5721590Srgrimes } 5731590Srgrimes /* 5741590Srgrimes * Don't want any pending data to be output, so clear the recv 5751590Srgrimes * buffer. If we were hanging on a write when interrupted, 5761590Srgrimes * don't want it to restart. If we were reading, restart 5771590Srgrimes * anyway. 5781590Srgrimes */ 5791590Srgrimes rcvcnt = 0; 5801590Srgrimes longjmp(rcvtop, 1); 5811590Srgrimes } 5821590Srgrimes 5831590Srgrimes /* oob does not do FLUSHREAD (alas!) */ 5841590Srgrimes 5851590Srgrimes /* 5861590Srgrimes * If we filled the receive buffer while a read was pending, longjmp 5871590Srgrimes * to the top to restart appropriately. Don't abort a pending write, 5881590Srgrimes * however, or we won't know how much was written. 5891590Srgrimes */ 5901590Srgrimes if (rcvd && rcvstate == READING) 5911590Srgrimes longjmp(rcvtop, 1); 5921590Srgrimes} 5931590Srgrimes 5941590Srgrimes/* reader: read from remote: line -> 1 */ 5951590Srgrimesint 59693057Simpreader(int omask) 5971590Srgrimes{ 598105268Smarkm int n, remaining; 5991590Srgrimes char *bufp; 600105268Smarkm pid_t pid; 6011590Srgrimes 602105268Smarkm pid = getpid(); 6031590Srgrimes (void)signal(SIGTTOU, SIG_IGN); 6041590Srgrimes (void)signal(SIGURG, oob); 60591434Sfenner (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 6061590Srgrimes ppid = getppid(); 6071590Srgrimes (void)fcntl(rem, F_SETOWN, pid); 6081590Srgrimes (void)setjmp(rcvtop); 6091590Srgrimes (void)sigsetmask(omask); 6101590Srgrimes bufp = rcvbuf; 6111590Srgrimes for (;;) { 6121590Srgrimes while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 6131590Srgrimes rcvstate = WRITING; 6141590Srgrimes n = write(STDOUT_FILENO, bufp, remaining); 6151590Srgrimes if (n < 0) { 6161590Srgrimes if (errno != EINTR) 6171590Srgrimes return (-1); 6181590Srgrimes continue; 6191590Srgrimes } 6201590Srgrimes bufp += n; 6211590Srgrimes } 6221590Srgrimes bufp = rcvbuf; 6231590Srgrimes rcvcnt = 0; 6241590Srgrimes rcvstate = READING; 6251590Srgrimes 626105268Smarkm rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 6271590Srgrimes if (rcvcnt == 0) 6281590Srgrimes return (0); 6291590Srgrimes if (rcvcnt < 0) { 6301590Srgrimes if (errno == EINTR) 6311590Srgrimes continue; 63227919Scharnier warn("read"); 6331590Srgrimes return (-1); 6341590Srgrimes } 6351590Srgrimes } 6361590Srgrimes} 6371590Srgrimes 6381590Srgrimesvoid 63993057Simpmode(int f) 6401590Srgrimes{ 641120547Stjr struct termios tty; 6421590Srgrimes 643120547Stjr switch (f) { 6441590Srgrimes case 0: 645120547Stjr (void)tcsetattr(0, TCSANOW, &deftty); 6461590Srgrimes break; 6471590Srgrimes case 1: 648120547Stjr (void)tcgetattr(0, &deftty); 649120547Stjr tty = deftty; 650120547Stjr /* This is loosely derived from sys/kern/tty_compat.c. */ 651120547Stjr tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); 652120547Stjr tty.c_iflag &= ~ICRNL; 653120547Stjr tty.c_oflag &= ~OPOST; 654120547Stjr tty.c_cc[VMIN] = 1; 655120547Stjr tty.c_cc[VTIME] = 0; 656120547Stjr if (eight) { 657120547Stjr tty.c_iflag &= IXOFF; 658120547Stjr tty.c_cflag &= ~(CSIZE|PARENB); 659120547Stjr tty.c_cflag |= CS8; 660120547Stjr } 661120547Stjr (void)tcsetattr(0, TCSANOW, &tty); 6621590Srgrimes break; 6631590Srgrimes default: 6641590Srgrimes return; 6651590Srgrimes } 6661590Srgrimes} 6671590Srgrimes 668105268Smarkm/* ARGSUSED */ 6691590Srgrimesvoid 67095621Smarkmlostpeer(int signo __unused) 6711590Srgrimes{ 6721590Srgrimes (void)signal(SIGPIPE, SIG_IGN); 673105235Scharnier msg("\007connection closed"); 6741590Srgrimes done(1); 6751590Srgrimes} 6761590Srgrimes 67791434Sfenner/* copy SIGURGs to the child process via SIGUSR1. */ 678105268Smarkm/* ARGSUSED */ 6791590Srgrimesvoid 68095621Smarkmcopytochild(int signo __unused) 6811590Srgrimes{ 68291434Sfenner (void)kill(child, SIGUSR1); 6831590Srgrimes} 6841590Srgrimes 6851590Srgrimesvoid 68695621Smarkmmsg(const char *str) 6871590Srgrimes{ 6881590Srgrimes (void)fprintf(stderr, "rlogin: %s\r\n", str); 6891590Srgrimes} 6901590Srgrimes 6911590Srgrimesvoid 69293057Simpusage(void) 6931590Srgrimes{ 6941590Srgrimes (void)fprintf(stderr, 69557232Sshin "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 696120547Stjr "8DEd", " "); 6971590Srgrimes exit(1); 6981590Srgrimes} 6991590Srgrimes 7001590Srgrimesu_int 701105268Smarkmgetescape(const char *p) 7021590Srgrimes{ 7031590Srgrimes long val; 704105268Smarkm size_t len; 7051590Srgrimes 7061590Srgrimes if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 7071590Srgrimes return ((u_int)*p); 7081590Srgrimes /* otherwise, \nnn */ 7091590Srgrimes if (*p == '\\' && len >= 2 && len <= 4) { 7101590Srgrimes val = strtol(++p, NULL, 8); 7111590Srgrimes for (;;) { 7121590Srgrimes if (!*++p) 7131590Srgrimes return ((u_int)val); 7141590Srgrimes if (*p < '0' || *p > '8') 7151590Srgrimes break; 7161590Srgrimes } 7171590Srgrimes } 7181590Srgrimes msg("illegal option value -- e"); 7191590Srgrimes usage(); 7201590Srgrimes /* NOTREACHED */ 7211590Srgrimes} 722