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