121595Sdist /* 221595Sdist * Copyright (c) 1983 Regents of the University of California. 321595Sdist * All rights reserved. The Berkeley software License Agreement 421595Sdist * specifies the terms and conditions for redistribution. 521595Sdist */ 621595Sdist 76444Swnj #ifndef lint 821595Sdist char copyright[] = 921595Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1021595Sdist All rights reserved.\n"; 1121595Sdist #endif not lint 126444Swnj 1321595Sdist #ifndef lint 14*26189Slepreau static char sccsid[] = "@(#)rlogin.c 5.9 (Berkeley) 02/17/86"; 1521595Sdist #endif not lint 1621595Sdist 1712990Ssam /* 1812990Ssam * rlogin - remote login 1912990Ssam */ 206444Swnj #include <sys/types.h> 2125424Skarels #include <sys/errno.h> 2224727Smckusick #include <sys/file.h> 236444Swnj #include <sys/socket.h> 2413620Ssam #include <sys/wait.h> 259365Ssam 269207Ssam #include <netinet/in.h> 279365Ssam 289365Ssam #include <stdio.h> 299365Ssam #include <sgtty.h> 306444Swnj #include <errno.h> 316444Swnj #include <pwd.h> 329365Ssam #include <signal.h> 3325424Skarels #include <setjmp.h> 349365Ssam #include <netdb.h> 356444Swnj 3624726Smckusick # ifndef TIOCPKT_WINDOW 3724726Smckusick # define TIOCPKT_WINDOW 0x80 3824726Smckusick # endif TIOCPKT_WINDOW 3924726Smckusick 406444Swnj char *index(), *rindex(), *malloc(), *getenv(); 416444Swnj struct passwd *getpwuid(); 429365Ssam char *name; 436444Swnj int rem; 446444Swnj char cmdchar = '~'; 456444Swnj int eight; 4621583Sbloom int litout; 476444Swnj char *speeds[] = 486444Swnj { "0", "50", "75", "110", "134", "150", "200", "300", 496444Swnj "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; 5018358Ssam char term[256] = "network"; 519365Ssam extern int errno; 529365Ssam int lostpeer(); 5324726Smckusick int dosigwinch = 0; 5418358Ssam struct winsize winsize; 5524726Smckusick int sigwinch(), oob(); 566444Swnj 576444Swnj main(argc, argv) 586444Swnj int argc; 596444Swnj char **argv; 606444Swnj { 619365Ssam char *host, *cp; 626444Swnj struct sgttyb ttyb; 636444Swnj struct passwd *pwd; 649365Ssam struct servent *sp; 6524726Smckusick int uid, options = 0, oldmask; 6617449Slepreau int on = 1; 676444Swnj 686444Swnj host = rindex(argv[0], '/'); 696444Swnj if (host) 706444Swnj host++; 716444Swnj else 726444Swnj host = argv[0]; 736444Swnj argv++, --argc; 746444Swnj if (!strcmp(host, "rlogin")) 756444Swnj host = *argv++, --argc; 766444Swnj another: 7710839Ssam if (argc > 0 && !strcmp(*argv, "-d")) { 786444Swnj argv++, argc--; 7910415Ssam options |= SO_DEBUG; 806444Swnj goto another; 816444Swnj } 8210839Ssam if (argc > 0 && !strcmp(*argv, "-l")) { 836444Swnj argv++, argc--; 846444Swnj if (argc == 0) 856444Swnj goto usage; 866444Swnj name = *argv++; argc--; 876444Swnj goto another; 886444Swnj } 8910839Ssam if (argc > 0 && !strncmp(*argv, "-e", 2)) { 906444Swnj cmdchar = argv[0][2]; 916444Swnj argv++, argc--; 926444Swnj goto another; 936444Swnj } 9410839Ssam if (argc > 0 && !strcmp(*argv, "-8")) { 956444Swnj eight = 1; 966444Swnj argv++, argc--; 976444Swnj goto another; 986444Swnj } 9921583Sbloom if (argc > 0 && !strcmp(*argv, "-L")) { 10021583Sbloom litout = 1; 10121583Sbloom argv++, argc--; 10221583Sbloom goto another; 10321583Sbloom } 1046444Swnj if (host == 0) 1056444Swnj goto usage; 1066444Swnj if (argc > 0) 1076444Swnj goto usage; 1086444Swnj pwd = getpwuid(getuid()); 1096444Swnj if (pwd == 0) { 1106444Swnj fprintf(stderr, "Who are you?\n"); 1116444Swnj exit(1); 1126444Swnj } 1139365Ssam sp = getservbyname("login", "tcp"); 1149365Ssam if (sp == 0) { 1159365Ssam fprintf(stderr, "rlogin: login/tcp: unknown service\n"); 1169365Ssam exit(2); 1179365Ssam } 1189241Ssam cp = getenv("TERM"); 1199241Ssam if (cp) 1209241Ssam strcpy(term, cp); 12118358Ssam if (ioctl(0, TIOCGETP, &ttyb) == 0) { 1226444Swnj strcat(term, "/"); 1236444Swnj strcat(term, speeds[ttyb.sg_ospeed]); 1246444Swnj } 12524726Smckusick (void) ioctl(0, TIOCGWINSZ, &winsize); 12612990Ssam signal(SIGPIPE, lostpeer); 12724726Smckusick signal(SIGURG, oob); 12824726Smckusick oldmask = sigblock(sigmask(SIGURG)); 1299365Ssam rem = rcmd(&host, sp->s_port, pwd->pw_name, 1306444Swnj name ? name : pwd->pw_name, term, 0); 1316444Swnj if (rem < 0) 1326444Swnj exit(1); 13310415Ssam if (options & SO_DEBUG && 13417449Slepreau setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 13510415Ssam perror("rlogin: setsockopt (SO_DEBUG)"); 1369365Ssam uid = getuid(); 1379365Ssam if (setuid(uid) < 0) { 1389365Ssam perror("rlogin: setuid"); 1399365Ssam exit(1); 1409365Ssam } 14124726Smckusick doit(oldmask); 1429365Ssam /*NOTREACHED*/ 1436444Swnj usage: 1446444Swnj fprintf(stderr, 14525341Smckusick "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n"); 1466444Swnj exit(1); 1476444Swnj } 1486444Swnj 1496444Swnj #define CRLF "\r\n" 1506444Swnj 1519365Ssam int child; 15211803Sedward int catchild(); 15324726Smckusick int writeroob(); 1549365Ssam 15513075Ssam int defflags, tabflag; 15621583Sbloom int deflflags; 15713075Ssam char deferase, defkill; 15813075Ssam struct tchars deftc; 15913075Ssam struct ltchars defltc; 16013075Ssam struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 16113075Ssam struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 1626444Swnj 16324726Smckusick doit(oldmask) 1646444Swnj { 1656444Swnj int exit(); 16613075Ssam struct sgttyb sb; 1676444Swnj 16813075Ssam ioctl(0, TIOCGETP, (char *)&sb); 16913075Ssam defflags = sb.sg_flags; 17012155Ssam tabflag = defflags & TBDELAY; 1719962Ssam defflags &= ECHO | CRMOD; 17213075Ssam deferase = sb.sg_erase; 17313075Ssam defkill = sb.sg_kill; 17421583Sbloom ioctl(0, TIOCLGET, (char *)&deflflags); 17513075Ssam ioctl(0, TIOCGETC, (char *)&deftc); 17613075Ssam notc.t_startc = deftc.t_startc; 17713075Ssam notc.t_stopc = deftc.t_stopc; 17813075Ssam ioctl(0, TIOCGLTC, (char *)&defltc); 17924726Smckusick signal(SIGINT, SIG_IGN); 18012990Ssam signal(SIGHUP, exit); 18112990Ssam signal(SIGQUIT, exit); 1829365Ssam child = fork(); 1839365Ssam if (child == -1) { 1849365Ssam perror("rlogin: fork"); 18525424Skarels done(1); 1869365Ssam } 1879365Ssam if (child == 0) { 18824726Smckusick mode(1); 18924726Smckusick sigsetmask(oldmask); 19025424Skarels if (reader() == 0) { 19125424Skarels prf("Connection closed."); 19225424Skarels exit(0); 19325424Skarels } 19412155Ssam sleep(1); 19512155Ssam prf("\007Connection closed."); 1966444Swnj exit(3); 1976444Swnj } 19824726Smckusick signal(SIGURG, writeroob); 19924726Smckusick sigsetmask(oldmask); 20012990Ssam signal(SIGCHLD, catchild); 2019365Ssam writer(); 20212155Ssam prf("Closed connection."); 20325424Skarels done(0); 2046444Swnj } 2056444Swnj 20625424Skarels done(status) 20725424Skarels int status; 2086444Swnj { 2096444Swnj 2106444Swnj mode(0); 2119365Ssam if (child > 0 && kill(child, SIGKILL) >= 0) 2129365Ssam wait((int *)0); 21325424Skarels exit(status); 2146444Swnj } 2156444Swnj 21624726Smckusick /* 21724726Smckusick * This is called when the reader process gets the out-of-band (urgent) 21824726Smckusick * request to turn on the window-changing protocol. 21924726Smckusick */ 22024726Smckusick writeroob() 22124726Smckusick { 22224726Smckusick 22325341Smckusick if (dosigwinch == 0) { 22424919Smckusick sendwindow(); 22525341Smckusick signal(SIGWINCH, sigwinch); 22625341Smckusick } 22724726Smckusick dosigwinch = 1; 22824726Smckusick } 22924726Smckusick 23011803Sedward catchild() 23111803Sedward { 23211803Sedward union wait status; 23311803Sedward int pid; 23411803Sedward 23511803Sedward again: 23611803Sedward pid = wait3(&status, WNOHANG|WUNTRACED, 0); 23711803Sedward if (pid == 0) 23811803Sedward return; 23911803Sedward /* 24011803Sedward * if the child (reader) dies, just quit 24111803Sedward */ 24211803Sedward if (pid < 0 || pid == child && !WIFSTOPPED(status)) 24325424Skarels done(status.w_termsig | status.w_retcode); 24411803Sedward goto again; 24511803Sedward } 24611803Sedward 2476444Swnj /* 2489365Ssam * writer: write to remote: 0 -> line. 2499365Ssam * ~. terminate 2509365Ssam * ~^Z suspend rlogin process. 25110415Ssam * ~^Y suspend rlogin process, but leave reader alone. 2526444Swnj */ 2539365Ssam writer() 2546444Swnj { 25523530Sbloom char c; 25611803Sedward register n; 25723530Sbloom register bol = 1; /* beginning of line */ 25823530Sbloom register local = 0; 2596444Swnj 26011803Sedward for (;;) { 26111803Sedward n = read(0, &c, 1); 26218358Ssam if (n <= 0) { 26318358Ssam if (n < 0 && errno == EINTR) 26418358Ssam continue; 26511803Sedward break; 26618358Ssam } 2679365Ssam /* 2689365Ssam * If we're at the beginning of the line 2699365Ssam * and recognize a command character, then 2709365Ssam * we echo locally. Otherwise, characters 2719365Ssam * are echo'd remotely. If the command 2729365Ssam * character is doubled, this acts as a 2739365Ssam * force and local echo is suppressed. 2749365Ssam */ 27523530Sbloom if (bol) { 27623530Sbloom bol = 0; 27723530Sbloom if (c == cmdchar) { 27823530Sbloom bol = 0; 27923530Sbloom local = 1; 28023530Sbloom continue; 2816444Swnj } 28223530Sbloom } else if (local) { 28323530Sbloom local = 0; 28423530Sbloom if (c == '.' || c == deftc.t_eofc) { 28523530Sbloom echo(c); 28623530Sbloom break; 2876444Swnj } 28823530Sbloom if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 28923530Sbloom bol = 1; 29023530Sbloom echo(c); 29123530Sbloom stop(c); 29223530Sbloom continue; 29323530Sbloom } 29423530Sbloom if (c != cmdchar) 29523530Sbloom write(rem, &cmdchar, 1); 2966444Swnj } 29723530Sbloom if (write(rem, &c, 1) == 0) { 29823530Sbloom prf("line gone"); 29923530Sbloom break; 3006444Swnj } 30123530Sbloom bol = c == defkill || c == deftc.t_eofc || 30225424Skarels c == deftc.t_intrc || c == defltc.t_suspc || 30323530Sbloom c == '\r' || c == '\n'; 3046444Swnj } 3056444Swnj } 3066444Swnj 30723530Sbloom echo(c) 30823530Sbloom register char c; 30923530Sbloom { 31023530Sbloom char buf[8]; 31123530Sbloom register char *p = buf; 31223530Sbloom 31323530Sbloom c &= 0177; 31423530Sbloom *p++ = cmdchar; 31523530Sbloom if (c < ' ') { 31623530Sbloom *p++ = '^'; 31723530Sbloom *p++ = c + '@'; 31823530Sbloom } else if (c == 0177) { 31923530Sbloom *p++ = '^'; 32023530Sbloom *p++ = '?'; 32123530Sbloom } else 32223530Sbloom *p++ = c; 32323530Sbloom *p++ = '\r'; 32423530Sbloom *p++ = '\n'; 32523530Sbloom write(1, buf, p - buf); 32623530Sbloom } 32723530Sbloom 32818358Ssam stop(cmdc) 32918358Ssam char cmdc; 33018358Ssam { 33118358Ssam mode(0); 33218358Ssam signal(SIGCHLD, SIG_IGN); 33318358Ssam kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 33418358Ssam signal(SIGCHLD, catchild); 33518358Ssam mode(1); 33618358Ssam sigwinch(); /* check for size changes */ 33718358Ssam } 33818358Ssam 33918358Ssam sigwinch() 34018358Ssam { 34118358Ssam struct winsize ws; 34218358Ssam 34325341Smckusick if (dosigwinch && ioctl(0, TIOCGWINSZ, &ws) == 0 && 34418358Ssam bcmp(&ws, &winsize, sizeof (ws))) { 34518358Ssam winsize = ws; 34624726Smckusick sendwindow(); 34718358Ssam } 34818358Ssam } 34918358Ssam 35024726Smckusick /* 35124726Smckusick * Send the window size to the server via the magic escape 35224726Smckusick */ 35324726Smckusick sendwindow() 35424726Smckusick { 35524726Smckusick char obuf[4 + sizeof (struct winsize)]; 35624726Smckusick struct winsize *wp = (struct winsize *)(obuf+4); 35724726Smckusick 35824726Smckusick obuf[0] = 0377; 35924726Smckusick obuf[1] = 0377; 36024726Smckusick obuf[2] = 's'; 36124726Smckusick obuf[3] = 's'; 36224726Smckusick wp->ws_row = htons(winsize.ws_row); 36324726Smckusick wp->ws_col = htons(winsize.ws_col); 36424726Smckusick wp->ws_xpixel = htons(winsize.ws_xpixel); 36524726Smckusick wp->ws_ypixel = htons(winsize.ws_ypixel); 36624726Smckusick (void) write(rem, obuf, sizeof(obuf)); 36724726Smckusick } 36824726Smckusick 36925424Skarels /* 37025424Skarels * reader: read from remote: line -> 1 37125424Skarels */ 37225424Skarels #define READING 1 37325424Skarels #define WRITING 2 37425424Skarels 37525424Skarels char rcvbuf[8 * 1024]; 37625424Skarels int rcvcnt; 37725424Skarels int rcvstate; 37825424Skarels jmp_buf rcvtop; 37925424Skarels 3806444Swnj oob() 3816444Swnj { 38225424Skarels int out = FWRITE, atmark, n; 38325424Skarels int rcvd = 0; 3849365Ssam char waste[BUFSIZ], mark; 38524726Smckusick struct sgttyb sb; 3866444Swnj 38725424Skarels while (recv(rem, &mark, 1, MSG_OOB) < 0) 38825424Skarels switch (errno) { 38925424Skarels 39025424Skarels case EWOULDBLOCK: 39125424Skarels /* 39225424Skarels * Urgent data not here yet. 39325424Skarels * It may not be possible to send it yet 39425424Skarels * if we are blocked for output 39525424Skarels * and our input buffer is full. 39625424Skarels */ 39725424Skarels if (rcvcnt < sizeof(rcvbuf)) { 39825424Skarels n = read(rem, rcvbuf + rcvcnt, 39925424Skarels sizeof(rcvbuf) - rcvcnt); 40025424Skarels if (n <= 0) 40125424Skarels return; 40225424Skarels rcvd += n; 40325424Skarels } else { 40425424Skarels n = read(rem, waste, sizeof(waste)); 40525424Skarels if (n <= 0) 40625424Skarels return; 40725424Skarels } 40825424Skarels continue; 40925424Skarels 41025424Skarels default: 41125424Skarels return; 4126444Swnj } 41325424Skarels if (mark & TIOCPKT_WINDOW) { 41424726Smckusick /* 41524726Smckusick * Let server know about window size changes 41624726Smckusick */ 41724726Smckusick kill(getppid(), SIGURG); 41824726Smckusick } 41925424Skarels if (!eight && (mark & TIOCPKT_NOSTOP)) { 42024726Smckusick ioctl(0, TIOCGETP, (char *)&sb); 42124726Smckusick sb.sg_flags &= ~CBREAK; 42224726Smckusick sb.sg_flags |= RAW; 42324726Smckusick ioctl(0, TIOCSETN, (char *)&sb); 42413075Ssam notc.t_stopc = -1; 42513075Ssam notc.t_startc = -1; 42613075Ssam ioctl(0, TIOCSETC, (char *)¬c); 4276444Swnj } 42825424Skarels if (!eight && (mark & TIOCPKT_DOSTOP)) { 42924726Smckusick ioctl(0, TIOCGETP, (char *)&sb); 43024726Smckusick sb.sg_flags &= ~RAW; 43124726Smckusick sb.sg_flags |= CBREAK; 43224726Smckusick ioctl(0, TIOCSETN, (char *)&sb); 43313075Ssam notc.t_stopc = deftc.t_stopc; 43413075Ssam notc.t_startc = deftc.t_startc; 43513075Ssam ioctl(0, TIOCSETC, (char *)¬c); 4366444Swnj } 43725424Skarels if (mark & TIOCPKT_FLUSHWRITE) { 43825424Skarels ioctl(1, TIOCFLUSH, (char *)&out); 43925424Skarels for (;;) { 44025424Skarels if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 44125424Skarels perror("ioctl"); 44225424Skarels break; 44325424Skarels } 44425424Skarels if (atmark) 44525424Skarels break; 44625424Skarels n = read(rem, waste, sizeof (waste)); 44725424Skarels if (n <= 0) 44825424Skarels break; 44925424Skarels } 45025424Skarels /* 45125424Skarels * Don't want any pending data to be output, 45225424Skarels * so clear the recv buffer. 45325424Skarels * If we were hanging on a write when interrupted, 45425424Skarels * don't want it to restart. If we were reading, 45525424Skarels * restart anyway. 45625424Skarels */ 45725424Skarels rcvcnt = 0; 45825424Skarels longjmp(rcvtop, 1); 45925424Skarels } 46025424Skarels /* 46125424Skarels * If we filled the receive buffer while a read was pending, 46225424Skarels * longjmp to the top to restart appropriately. Don't abort 46325424Skarels * a pending write, however, or we won't know how much was written. 46425424Skarels */ 46525424Skarels if (rcvd && rcvstate == READING) 46625424Skarels longjmp(rcvtop, 1); 4676444Swnj } 4686444Swnj 4699365Ssam /* 4709365Ssam * reader: read from remote: line -> 1 4719365Ssam */ 4729365Ssam reader() 4736444Swnj { 47425424Skarels int pid = getpid(); 47525424Skarels int n, remaining; 47625424Skarels char *bufp = rcvbuf; 4776444Swnj 47823530Sbloom signal(SIGTTOU, SIG_IGN); 47925424Skarels fcntl(rem, F_SETOWN, pid); 48025424Skarels (void) setjmp(rcvtop); 4816444Swnj for (;;) { 48225424Skarels while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 48325424Skarels rcvstate = WRITING; 48425424Skarels n = write(1, bufp, remaining); 48525424Skarels if (n < 0) { 48625424Skarels if (errno != EINTR) 487*26189Slepreau return (-1); 48825424Skarels continue; 48925424Skarels } 49025424Skarels bufp += n; 49125424Skarels } 49225424Skarels bufp = rcvbuf; 49325424Skarels rcvcnt = 0; 49425424Skarels rcvstate = READING; 49525424Skarels rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 49625424Skarels if (rcvcnt == 0) 49725424Skarels return (0); 49825424Skarels if (rcvcnt < 0) { 4999365Ssam if (errno == EINTR) 5006444Swnj continue; 50125424Skarels return (-1); 5026444Swnj } 5036444Swnj } 5046444Swnj } 5056444Swnj 5066444Swnj mode(f) 5076444Swnj { 50813075Ssam struct tchars *tc; 50913075Ssam struct ltchars *ltc; 51013075Ssam struct sgttyb sb; 51121583Sbloom int lflags; 5129365Ssam 51313075Ssam ioctl(0, TIOCGETP, (char *)&sb); 51421583Sbloom ioctl(0, TIOCLGET, (char *)&lflags); 5159962Ssam switch (f) { 5169962Ssam 5179962Ssam case 0: 51813075Ssam sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 51913075Ssam sb.sg_flags |= defflags|tabflag; 5209962Ssam tc = &deftc; 52113075Ssam ltc = &defltc; 52213075Ssam sb.sg_kill = defkill; 52313075Ssam sb.sg_erase = deferase; 52421583Sbloom lflags = deflflags; 5259962Ssam break; 5269962Ssam 5279962Ssam case 1: 52813075Ssam sb.sg_flags |= (eight ? RAW : CBREAK); 52913075Ssam sb.sg_flags &= ~defflags; 53012155Ssam /* preserve tab delays, but turn off XTABS */ 53113075Ssam if ((sb.sg_flags & TBDELAY) == XTABS) 53213075Ssam sb.sg_flags &= ~TBDELAY; 5339962Ssam tc = ¬c; 53413075Ssam ltc = &noltc; 53513075Ssam sb.sg_kill = sb.sg_erase = -1; 53621583Sbloom if (litout) 53721583Sbloom lflags |= LLITOUT; 5389962Ssam break; 5399962Ssam 5409962Ssam default: 5419962Ssam return; 5426444Swnj } 54313075Ssam ioctl(0, TIOCSLTC, (char *)ltc); 54413075Ssam ioctl(0, TIOCSETC, (char *)tc); 54513075Ssam ioctl(0, TIOCSETN, (char *)&sb); 54621583Sbloom ioctl(0, TIOCLSET, (char *)&lflags); 5476444Swnj } 5486444Swnj 5499365Ssam /*VARARGS*/ 55024726Smckusick prf(f, a1, a2, a3, a4, a5) 5519365Ssam char *f; 5526444Swnj { 55324726Smckusick fprintf(stderr, f, a1, a2, a3, a4, a5); 5546444Swnj fprintf(stderr, CRLF); 5556444Swnj } 5566444Swnj 5579365Ssam lostpeer() 5586444Swnj { 55912990Ssam signal(SIGPIPE, SIG_IGN); 56012155Ssam prf("\007Connection closed."); 56125424Skarels done(1); 5626444Swnj } 563