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