1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)rlogin.c 5.10 (Berkeley) 3/30/86"; 15: #endif not lint 16: 17: /* 18: * rlogin - remote login 19: */ 20: #include <sys/param.h> 21: #include <sys/errno.h> 22: #include <sys/file.h> 23: #include <sys/socket.h> 24: #include <sys/wait.h> 25: 26: #include <netinet/in.h> 27: 28: #include <stdio.h> 29: #include <sgtty.h> 30: #include <errno.h> 31: #include <pwd.h> 32: #include <signal.h> 33: #include <setjmp.h> 34: #include <netdb.h> 35: 36: # ifndef TIOCPKT_WINDOW 37: # define TIOCPKT_WINDOW 0x80 38: # endif TIOCPKT_WINDOW 39: 40: char *index(), *rindex(), *malloc(), *getenv(); 41: struct passwd *getpwuid(); 42: char *name; 43: int rem; 44: char cmdchar = '~'; 45: int eight; 46: int litout; 47: char *speeds[] = 48: { "0", "50", "75", "110", "134", "150", "200", "300", 49: "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; 50: char term[256] = "network"; 51: extern int errno; 52: int lostpeer(); 53: int dosigwinch = 0; 54: #ifndef sigmask 55: #define sigmask(m) (1 << ((m)-1)) 56: #endif 57: #ifdef sun 58: struct ttysize winsize; 59: struct winsize { 60: unsigned short ws_row, ws_col; 61: unsigned short ws_xpixel, ws_ypixel; 62: }; 63: #else sun 64: struct winsize winsize; 65: #endif sun 66: int sigwinch(), oob(); 67: 68: main(argc, argv) 69: int argc; 70: char **argv; 71: { 72: char *host, *cp; 73: struct sgttyb ttyb; 74: struct passwd *pwd; 75: struct servent *sp; 76: int uid, options = 0, oldmask; 77: int on = 1; 78: 79: host = rindex(argv[0], '/'); 80: if (host) 81: host++; 82: else 83: host = argv[0]; 84: argv++, --argc; 85: if (!strcmp(host, "rlogin")) 86: host = *argv++, --argc; 87: another: 88: if (argc > 0 && !strcmp(*argv, "-d")) { 89: argv++, argc--; 90: options |= SO_DEBUG; 91: goto another; 92: } 93: if (argc > 0 && !strcmp(*argv, "-l")) { 94: argv++, argc--; 95: if (argc == 0) 96: goto usage; 97: name = *argv++; argc--; 98: goto another; 99: } 100: if (argc > 0 && !strncmp(*argv, "-e", 2)) { 101: cmdchar = argv[0][2]; 102: argv++, argc--; 103: goto another; 104: } 105: if (argc > 0 && !strcmp(*argv, "-8")) { 106: eight = 1; 107: argv++, argc--; 108: goto another; 109: } 110: if (argc > 0 && !strcmp(*argv, "-L")) { 111: litout = 1; 112: argv++, argc--; 113: goto another; 114: } 115: if (host == 0) 116: goto usage; 117: if (argc > 0) 118: goto usage; 119: pwd = getpwuid(getuid()); 120: if (pwd == 0) { 121: fprintf(stderr, "Who are you?\n"); 122: exit(1); 123: } 124: sp = getservbyname("login", "tcp"); 125: if (sp == 0) { 126: fprintf(stderr, "rlogin: login/tcp: unknown service\n"); 127: exit(2); 128: } 129: cp = getenv("TERM"); 130: if (cp) 131: strcpy(term, cp); 132: if (ioctl(0, TIOCGETP, &ttyb) == 0) { 133: strcat(term, "/"); 134: strcat(term, speeds[ttyb.sg_ospeed]); 135: } 136: #ifdef sun 137: (void) ioctl(0, TIOCGSIZE, &winsize); 138: #else sun 139: (void) ioctl(0, TIOCGWINSZ, &winsize); 140: #endif sun 141: signal(SIGPIPE, lostpeer); 142: signal(SIGURG, oob); 143: oldmask = sigblock(sigmask(SIGURG)); 144: rem = rcmd(&host, sp->s_port, pwd->pw_name, 145: name ? name : pwd->pw_name, term, 0); 146: if (rem < 0) 147: exit(1); 148: if (options & SO_DEBUG && 149: setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 150: perror("rlogin: setsockopt (SO_DEBUG)"); 151: uid = getuid(); 152: if (setuid(uid) < 0) { 153: perror("rlogin: setuid"); 154: exit(1); 155: } 156: doit(oldmask); 157: /*NOTREACHED*/ 158: usage: 159: fprintf(stderr, 160: "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n"); 161: exit(1); 162: } 163: 164: #define CRLF "\r\n" 165: 166: int child; 167: int catchild(); 168: int writeroob(); 169: 170: int defflags, tabflag; 171: int deflflags; 172: char deferase, defkill; 173: struct tchars deftc; 174: struct ltchars defltc; 175: struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 176: struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 177: 178: doit(oldmask) 179: { 180: int exit(); 181: struct sgttyb sb; 182: 183: ioctl(0, TIOCGETP, (char *)&sb); 184: defflags = sb.sg_flags; 185: tabflag = defflags & TBDELAY; 186: defflags &= ECHO | CRMOD; 187: deferase = sb.sg_erase; 188: defkill = sb.sg_kill; 189: ioctl(0, TIOCLGET, (char *)&deflflags); 190: ioctl(0, TIOCGETC, (char *)&deftc); 191: notc.t_startc = deftc.t_startc; 192: notc.t_stopc = deftc.t_stopc; 193: ioctl(0, TIOCGLTC, (char *)&defltc); 194: signal(SIGINT, SIG_IGN); 195: signal(SIGHUP, exit); 196: signal(SIGQUIT, exit); 197: child = fork(); 198: if (child == -1) { 199: perror("rlogin: fork"); 200: done(1); 201: } 202: if (child == 0) { 203: mode(1); 204: sigsetmask(oldmask); 205: if (reader() == 0) { 206: prf("Connection closed."); 207: exit(0); 208: } 209: sleep(1); 210: prf("\007Connection closed."); 211: exit(3); 212: } 213: signal(SIGURG, writeroob); 214: sigsetmask(oldmask); 215: signal(SIGCHLD, catchild); 216: writer(); 217: prf("Closed connection."); 218: done(0); 219: } 220: 221: done(status) 222: int status; 223: { 224: 225: mode(0); 226: if (child > 0 && kill(child, SIGKILL) >= 0) 227: wait((int *)0); 228: exit(status); 229: } 230: 231: /* 232: * This is called when the reader process gets the out-of-band (urgent) 233: * request to turn on the window-changing protocol. 234: */ 235: writeroob() 236: { 237: 238: if (dosigwinch == 0) { 239: sendwindow(); 240: signal(SIGWINCH, sigwinch); 241: } 242: dosigwinch = 1; 243: } 244: 245: catchild() 246: { 247: union wait status; 248: int pid; 249: 250: again: 251: pid = wait3(&status, WNOHANG|WUNTRACED, 0); 252: if (pid == 0) 253: return; 254: /* 255: * if the child (reader) dies, just quit 256: */ 257: if (pid < 0 || pid == child && !WIFSTOPPED(status)) 258: done(status.w_termsig | status.w_retcode); 259: goto again; 260: } 261: 262: /* 263: * writer: write to remote: 0 -> line. 264: * ~. terminate 265: * ~^Z suspend rlogin process. 266: * ~^Y suspend rlogin process, but leave reader alone. 267: */ 268: writer() 269: { 270: char c; 271: register n; 272: register bol = 1; /* beginning of line */ 273: register local = 0; 274: 275: for (;;) { 276: n = read(0, &c, 1); 277: if (n <= 0) { 278: if (n < 0 && errno == EINTR) 279: continue; 280: break; 281: } 282: /* 283: * If we're at the beginning of the line 284: * and recognize a command character, then 285: * we echo locally. Otherwise, characters 286: * are echo'd remotely. If the command 287: * character is doubled, this acts as a 288: * force and local echo is suppressed. 289: */ 290: if (bol) { 291: bol = 0; 292: if (c == cmdchar) { 293: bol = 0; 294: local = 1; 295: continue; 296: } 297: } else if (local) { 298: local = 0; 299: if (c == '.' || c == deftc.t_eofc) { 300: echo(c); 301: break; 302: } 303: if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 304: bol = 1; 305: echo(c); 306: stop(c); 307: continue; 308: } 309: if (c != cmdchar) 310: write(rem, &cmdchar, 1); 311: } 312: if (write(rem, &c, 1) == 0) { 313: prf("line gone"); 314: break; 315: } 316: bol = c == defkill || c == deftc.t_eofc || 317: c == deftc.t_intrc || c == defltc.t_suspc || 318: c == '\r' || c == '\n'; 319: } 320: } 321: 322: echo(c) 323: register char c; 324: { 325: char buf[8]; 326: register char *p = buf; 327: 328: c &= 0177; 329: *p++ = cmdchar; 330: if (c < ' ') { 331: *p++ = '^'; 332: *p++ = c + '@'; 333: } else if (c == 0177) { 334: *p++ = '^'; 335: *p++ = '?'; 336: } else 337: *p++ = c; 338: *p++ = '\r'; 339: *p++ = '\n'; 340: write(1, buf, p - buf); 341: } 342: 343: stop(cmdc) 344: char cmdc; 345: { 346: mode(0); 347: signal(SIGCHLD, SIG_IGN); 348: kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 349: signal(SIGCHLD, catchild); 350: mode(1); 351: sigwinch(); /* check for size changes */ 352: } 353: 354: #ifdef sun 355: sigwinch() 356: { 357: struct ttysize ws; 358: 359: if (dosigwinch && ioctl(0, TIOCGSIZE, &ws) == 0 && 360: bcmp(&ws, &winsize, sizeof (ws))) { 361: winsize = ws; 362: sendwindow(); 363: } 364: } 365: 366: #else sun 367: sigwinch() 368: { 369: struct winsize ws; 370: 371: if (dosigwinch && ioctl(0, TIOCGWINSZ, &ws) == 0 && 372: bcmp(&ws, &winsize, sizeof (ws))) { 373: winsize = ws; 374: sendwindow(); 375: } 376: } 377: #endif 378: 379: /* 380: * Send the window size to the server via the magic escape 381: */ 382: sendwindow() 383: { 384: char obuf[4 + sizeof (struct winsize)]; 385: struct winsize *wp = (struct winsize *)(obuf+4); 386: 387: obuf[0] = 0377; 388: obuf[1] = 0377; 389: obuf[2] = 's'; 390: obuf[3] = 's'; 391: #ifdef sun 392: wp->ws_row = htons(winsize.ts_lines); 393: wp->ws_col = htons(winsize.ts_cols); 394: wp->ws_xpixel = 0; 395: wp->ws_ypixel = 0; 396: #else sun 397: wp->ws_row = htons(winsize.ws_row); 398: wp->ws_col = htons(winsize.ws_col); 399: wp->ws_xpixel = htons(winsize.ws_xpixel); 400: wp->ws_ypixel = htons(winsize.ws_ypixel); 401: #endif sun 402: (void) write(rem, obuf, sizeof(obuf)); 403: } 404: 405: /* 406: * reader: read from remote: line -> 1 407: */ 408: #define READING 1 409: #define WRITING 2 410: 411: char rcvbuf[8 * 1024]; 412: int rcvcnt; 413: int rcvstate; 414: int ppid; 415: jmp_buf rcvtop; 416: 417: oob() 418: { 419: int out = FWRITE, atmark, n; 420: int rcvd = 0; 421: char waste[BUFSIZ], mark; 422: struct sgttyb sb; 423: 424: while (recv(rem, &mark, 1, MSG_OOB) < 0) 425: switch (errno) { 426: 427: case EWOULDBLOCK: 428: /* 429: * Urgent data not here yet. 430: * It may not be possible to send it yet 431: * if we are blocked for output 432: * and our input buffer is full. 433: */ 434: if (rcvcnt < sizeof(rcvbuf)) { 435: n = read(rem, rcvbuf + rcvcnt, 436: sizeof(rcvbuf) - rcvcnt); 437: if (n <= 0) 438: return; 439: rcvd += n; 440: } else { 441: n = read(rem, waste, sizeof(waste)); 442: if (n <= 0) 443: return; 444: } 445: continue; 446: 447: default: 448: return; 449: } 450: if (mark & TIOCPKT_WINDOW) { 451: /* 452: * Let server know about window size changes 453: */ 454: kill(ppid, SIGURG); 455: } 456: if (!eight && (mark & TIOCPKT_NOSTOP)) { 457: ioctl(0, TIOCGETP, (char *)&sb); 458: sb.sg_flags &= ~CBREAK; 459: sb.sg_flags |= RAW; 460: ioctl(0, TIOCSETN, (char *)&sb); 461: notc.t_stopc = -1; 462: notc.t_startc = -1; 463: ioctl(0, TIOCSETC, (char *)¬c); 464: } 465: if (!eight && (mark & TIOCPKT_DOSTOP)) { 466: ioctl(0, TIOCGETP, (char *)&sb); 467: sb.sg_flags &= ~RAW; 468: sb.sg_flags |= CBREAK; 469: ioctl(0, TIOCSETN, (char *)&sb); 470: notc.t_stopc = deftc.t_stopc; 471: notc.t_startc = deftc.t_startc; 472: ioctl(0, TIOCSETC, (char *)¬c); 473: } 474: if (mark & TIOCPKT_FLUSHWRITE) { 475: ioctl(1, TIOCFLUSH, (char *)&out); 476: for (;;) { 477: if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 478: perror("ioctl"); 479: break; 480: } 481: if (atmark) 482: break; 483: n = read(rem, waste, sizeof (waste)); 484: if (n <= 0) 485: break; 486: } 487: /* 488: * Don't want any pending data to be output, 489: * so clear the recv buffer. 490: * If we were hanging on a write when interrupted, 491: * don't want it to restart. If we were reading, 492: * restart anyway. 493: */ 494: rcvcnt = 0; 495: longjmp(rcvtop, 1); 496: } 497: /* 498: * If we filled the receive buffer while a read was pending, 499: * longjmp to the top to restart appropriately. Don't abort 500: * a pending write, however, or we won't know how much was written. 501: */ 502: if (rcvd && rcvstate == READING) 503: longjmp(rcvtop, 1); 504: } 505: 506: /* 507: * reader: read from remote: line -> 1 508: */ 509: reader() 510: { 511: #if !defined(BSD) || BSD < 43 512: int pid = -getpid(); 513: #else 514: int pid = getpid(); 515: #endif 516: int n, remaining; 517: char *bufp = rcvbuf; 518: 519: signal(SIGTTOU, SIG_IGN); 520: fcntl(rem, F_SETOWN, pid); 521: ppid = getppid(); 522: (void) setjmp(rcvtop); 523: for (;;) { 524: while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 525: rcvstate = WRITING; 526: n = write(1, bufp, remaining); 527: if (n < 0) { 528: if (errno != EINTR) 529: return (-1); 530: continue; 531: } 532: bufp += n; 533: } 534: bufp = rcvbuf; 535: rcvcnt = 0; 536: rcvstate = READING; 537: rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 538: if (rcvcnt == 0) 539: return (0); 540: if (rcvcnt < 0) { 541: if (errno == EINTR) 542: continue; 543: perror("read"); 544: return (-1); 545: } 546: } 547: } 548: 549: mode(f) 550: { 551: struct tchars *tc; 552: struct ltchars *ltc; 553: struct sgttyb sb; 554: int lflags; 555: 556: ioctl(0, TIOCGETP, (char *)&sb); 557: ioctl(0, TIOCLGET, (char *)&lflags); 558: switch (f) { 559: 560: case 0: 561: sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 562: sb.sg_flags |= defflags|tabflag; 563: tc = &deftc; 564: ltc = &defltc; 565: sb.sg_kill = defkill; 566: sb.sg_erase = deferase; 567: lflags = deflflags; 568: break; 569: 570: case 1: 571: sb.sg_flags |= (eight ? RAW : CBREAK); 572: sb.sg_flags &= ~defflags; 573: /* preserve tab delays, but turn off XTABS */ 574: if ((sb.sg_flags & TBDELAY) == XTABS) 575: sb.sg_flags &= ~TBDELAY; 576: tc = ¬c; 577: ltc = &noltc; 578: sb.sg_kill = sb.sg_erase = -1; 579: if (litout) 580: lflags |= LLITOUT; 581: break; 582: 583: default: 584: return; 585: } 586: ioctl(0, TIOCSLTC, (char *)ltc); 587: ioctl(0, TIOCSETC, (char *)tc); 588: ioctl(0, TIOCSETN, (char *)&sb); 589: ioctl(0, TIOCLSET, (char *)&lflags); 590: } 591: 592: /*VARARGS*/ 593: prf(f, a1, a2, a3, a4, a5) 594: char *f; 595: { 596: fprintf(stderr, f, a1, a2, a3, a4, a5); 597: fprintf(stderr, CRLF); 598: } 599: 600: lostpeer() 601: { 602: signal(SIGPIPE, SIG_IGN); 603: prf("\007Connection closed."); 604: done(1); 605: }