rlogin.c revision 95621
1197679Sdes/* 260573Skris * Copyright (c) 1983, 1990, 1993 360573Skris * The Regents of the University of California. All rights reserved. 460573Skris * 560573Skris * Redistribution and use in source and binary forms, with or without 660573Skris * modification, are permitted provided that the following conditions 760573Skris * are met: 860573Skris * 1. Redistributions of source code must retain the above copyright 960573Skris * notice, this list of conditions and the following disclaimer. 1060573Skris * 2. Redistributions in binary form must reproduce the above copyright 1160573Skris * notice, this list of conditions and the following disclaimer in the 1260573Skris * documentation and/or other materials provided with the distribution. 1360573Skris * 3. All advertising materials mentioning features or use of this software 1460573Skris * must display the following acknowledgement: 1560573Skris * This product includes software developed by the University of 1660573Skris * California, Berkeley and its contributors. 1760573Skris * 4. Neither the name of the University nor the names of its contributors 1860573Skris * may be used to endorse or promote products derived from this software 1960573Skris * without specific prior written permission. 2060573Skris * 2160573Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2260573Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2360573Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2460573Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2565674Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2660573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27162856Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2860573Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29162856Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30181111Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31181111Sdes * SUCH DAMAGE. 32162856Sdes */ 33181111Sdes 34162856Sdes#ifndef lint 35162856Sdesstatic const char copyright[] = 36162856Sdes"@(#) Copyright (c) 1983, 1990, 1993\n\ 37181111Sdes The Regents of the University of California. All rights reserved.\n"; 38162856Sdes#endif /* not lint */ 39197679Sdes 40162856Sdes#ifndef lint 4176262Sgreenstatic const char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93"; 4260573Skris#endif /* not lint */ 4376262Sgreen 44162856Sdes/* 4560573Skris * rlogin - remote login 4660573Skris */ 47162856Sdes#include <sys/cdefs.h> 48162856Sdes__FBSDID("$FreeBSD: head/usr.bin/rlogin/rlogin.c 95621 2002-04-28 11:16:43Z markm $"); 4960573Skris 5060573Skris#include <sys/param.h> 5176262Sgreen#include <sys/socket.h> 52147005Sdes#include <sys/time.h> 53162856Sdes#include <sys/resource.h> 5460573Skris#include <sys/wait.h> 55124211Sdes 56124211Sdes#include <netinet/in.h> 57124211Sdes#include <netinet/in_systm.h> 58162856Sdes#include <netinet/ip.h> 59124211Sdes#include <netinet/tcp.h> 6060573Skris 6160573Skris#include <err.h> 6276262Sgreen#include <errno.h> 63124211Sdes#include <fcntl.h> 64147005Sdes#include <libutil.h> 6560573Skris#include <netdb.h> 6698684Sdes#include <pwd.h> 6798684Sdes#include <setjmp.h> 6898684Sdes#include <sgtty.h> 6998684Sdes#include <signal.h> 7098684Sdes#include <stdio.h> 7198684Sdes#include <stdlib.h> 7298684Sdes#include <string.h> 73124211Sdes#include <unistd.h> 74124211Sdes 75124211Sdes#ifdef KERBEROS 76192595Sdes#include <openssl/des.h> 77192595Sdes#include <krb.h> 78192595Sdes 7998684Sdes#include "../../bin/rcp/pathnames.h" 8098684Sdes#include "krb.h" 8198684Sdes 8298684SdesCREDENTIALS cred; 83124211SdesKey_schedule schedule; 84124211Sdesint use_kerberos = 1, doencrypt; 85124211Sdeschar dst_realm_buf[REALM_SZ], *dest_realm = NULL; 86192595Sdes#endif 87192595Sdes 88192595Sdes#ifndef TIOCPKT_WINDOW 8998684Sdes#define TIOCPKT_WINDOW 0x80 9098684Sdes#endif 9198684Sdes 9298684Sdes/* concession to Sun */ 9369591Sgreen#ifndef SIGUSR1 9469591Sgreen#define SIGUSR1 30 9560573Skris#endif 9660573Skris 9792559Sdesint eight, litout, rem; 9892559Sdesint family = PF_UNSPEC; 9960573Skris 10060573Skrisint noescape; 10192559Sdesu_char escapechar = '~'; 10292559Sdes 10360573Skrisconst char *speeds[] = { 104181111Sdes "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 105181111Sdes "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200" 106181111Sdes#define MAX_SPEED_LENGTH (sizeof("115200") - 1) 107181111Sdes}; 108181111Sdes 109181111Sdes#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 110181111Sdesstruct winsize winsize; 111181111Sdes 112181111Sdesvoid catch_child(int); 113181111Sdesvoid copytochild(int); 114181111Sdesvoid doit(long) __dead2; 115181111Sdesvoid done(int) __dead2; 116181111Sdesvoid echo(char); 117181111Sdesu_int getescape(char *); 118181111Sdesvoid lostpeer(int); 119181111Sdesvoid mode(int); 120181111Sdesvoid msg(const char *); 121181111Sdesvoid oob(int); 122181111Sdesint reader(int); 123181111Sdesvoid sendwindow(void); 124181111Sdesvoid setsignal(int); 125181111Sdesvoid sigwinch(int); 126181111Sdesvoid stop(char); 127181111Sdesvoid usage(void) __dead2; 128181111Sdesvoid writer(void); 129181111Sdesvoid writeroob(int); 130181111Sdes 131181111Sdesint 132181111Sdesmain(int argc, char *argv[]) 133181111Sdes{ 134181111Sdes struct passwd *pw; 135181111Sdes struct servent *sp; 136181111Sdes struct sgttyb ttyb; 137181111Sdes long omask; 138181111Sdes int argoff, ch, dflag, Dflag, one, uid; 139181111Sdes char *host, *localname, *p, *user, term[1024]; 140181111Sdes#ifdef KERBEROS 141181111Sdes char *k; 142181111Sdes#endif 143181111Sdes struct sockaddr_storage ss; 144181111Sdes int sslen; 145181111Sdes 146181111Sdes argoff = dflag = Dflag = 0; 147181111Sdes one = 1; 148181111Sdes host = localname = user = NULL; 149181111Sdes 150181111Sdes if ((p = rindex(argv[0], '/'))) 151181111Sdes ++p; 152181111Sdes else 153181111Sdes p = argv[0]; 154181111Sdes 155181111Sdes if (strcmp(p, "rlogin")) 156181111Sdes host = p; 157181111Sdes 158181111Sdes /* handle "rlogin host flags" */ 159181111Sdes if (!host && argc > 2 && argv[1][0] != '-') { 160181111Sdes host = argv[1]; 161181111Sdes argoff = 1; 162181111Sdes } 163181111Sdes 164181111Sdes#ifdef KERBEROS 165181111Sdes#define OPTIONS "468DEKLde:i:k:l:x" 166181111Sdes#else 167181111Sdes#define OPTIONS "468DEKLde:i:l:" 168181111Sdes#endif 16960573Skris while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 17069591Sgreen switch(ch) { 17160573Skris case '4': 172126277Sdes family = PF_INET; 173126277Sdes break; 17460573Skris 17592559Sdes case '6': 17660573Skris family = PF_INET6; 17769591Sgreen break; 17860573Skris 17960573Skris case '8': 180162856Sdes eight = 1; 18192559Sdes break; 18292559Sdes case 'D': 18360573Skris Dflag = 1; 18469591Sgreen break; 18576262Sgreen case 'E': 186106130Sdes noescape = 1; 18760573Skris break; 18892559Sdes case 'K': 18960573Skris#ifdef KERBEROS 19069591Sgreen use_kerberos = 0; 19169591Sgreen#endif 19269591Sgreen break; 19360573Skris case 'L': 19469591Sgreen litout = 1; 195106130Sdes break; 19660573Skris case 'd': 19760573Skris dflag = 1; 19860573Skris break; 19960573Skris case 'e': 20060573Skris noescape = 0; 20160573Skris escapechar = getescape(optarg); 202106130Sdes break; 20360573Skris case 'i': 20460573Skris if (getuid() != 0) 20560573Skris errx(1, "-i user: permission denied"); 20660573Skris localname = optarg; 20760573Skris break; 20860573Skris#ifdef KERBEROS 20960573Skris case 'k': 21060573Skris dest_realm = dst_realm_buf; 21160573Skris (void)strncpy(dest_realm, optarg, REALM_SZ); 21260573Skris break; 21360573Skris#endif 214162856Sdes case 'l': 21592559Sdes user = optarg; 21692559Sdes break; 21760573Skris#ifdef CRYPT 21869591Sgreen#ifdef KERBEROS 21969591Sgreen case 'x': 22076262Sgreen doencrypt = 1; 22160573Skris break; 22299053Sdes#endif 22399053Sdes#endif 22499053Sdes case '?': 22560573Skris default: 226124211Sdes usage(); 22799053Sdes } 22899053Sdes optind += argoff; 22999053Sdes 23069591Sgreen /* if haven't gotten a host yet, do so */ 23169591Sgreen if (!host && !(host = argv[optind++])) 23260573Skris usage(); 23369591Sgreen 23469591Sgreen if (argv[optind]) 23569591Sgreen usage(); 23660573Skris 23776262Sgreen if (!(pw = getpwuid(uid = getuid()))) 23860573Skris errx(1, "unknown user id"); 23976262Sgreen if (!user) 24076262Sgreen user = pw->pw_name; 24176262Sgreen if (!localname) 24276262Sgreen localname = pw->pw_name; 24369591Sgreen 24498684Sdes sp = NULL; 245128460Sdes#ifdef KERBEROS 24698684Sdes k = auth_getval("auth_list"); 24769591Sgreen if (k && !strstr(k, "kerberos")) 24869591Sgreen use_kerberos = 0; 24969591Sgreen if (use_kerberos) { 250137019Sdes sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp"); 251124211Sdes if (sp == NULL) { 252147005Sdes use_kerberos = 0; 253147005Sdes warn("can't get entry for %s/tcp service", 254147005Sdes doencrypt ? "eklogin" : "klogin"); 25560573Skris } 256157019Sdes } 257157019Sdes#endif 258157019Sdes if (sp == NULL) 259157019Sdes sp = getservbyname("login", "tcp"); 260137019Sdes if (sp == NULL) 26198684Sdes errx(1, "login/tcp: unknown service"); 26269591Sgreen 26392559Sdes#define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1) 26498684Sdes 26598684Sdes (void)strncpy(term, (p = getenv("TERM")) ? p : "network", 266181111Sdes MAX_TERM_LENGTH); 26792559Sdes term[MAX_TERM_LENGTH] = '\0'; 26892559Sdes if (ioctl(0, TIOCGETP, &ttyb) == 0) { 26992559Sdes (void)strcat(term, "/"); 27092559Sdes (void)strcat(term, speeds[(int)ttyb.sg_ospeed]); 27192559Sdes } 27260573Skris 27399053Sdes (void)get_window_size(0, &winsize); 27499053Sdes 27599053Sdes (void)signal(SIGPIPE, lostpeer); 27699053Sdes /* will use SIGUSR1 for window size hack, so hold it off */ 27799053Sdes omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 27899053Sdes /* 27999053Sdes * We set SIGURG and SIGUSR1 below so that an 280124211Sdes * incoming signal will be held pending rather than being 28199053Sdes * discarded. Note that these routines will be ready to get 28299053Sdes * a signal by the time that they are unblocked below. 28399053Sdes */ 28499053Sdes (void)signal(SIGURG, copytochild); 285124211Sdes (void)signal(SIGUSR1, writeroob); 28699053Sdes 28799053Sdes#ifdef KERBEROS 28899053Sdes if (use_kerberos) { 28999053Sdes setuid(getuid()); 29099053Sdes rem = KSUCCESS; 29199053Sdes errno = 0; 29299053Sdes if (dest_realm == NULL) 29399053Sdes dest_realm = krb_realmofhost(host); 29476262Sgreen 29592559Sdes#ifdef CRYPT 296192595Sdes if (doencrypt) { 297192595Sdes rem = krcmd_mutual(&host, sp->s_port, user, term, 0, 298192595Sdes dest_realm, &cred, schedule); 299124211Sdes des_set_key(&cred.session, schedule); 300124211Sdes } else 301192595Sdes#endif /* CRYPT */ 302124211Sdes rem = krcmd(&host, sp->s_port, user, term, 0, 303124211Sdes dest_realm); 304124211Sdes if (rem < 0) { 305124211Sdes int i; 30676262Sgreen char **newargv; 30768704Sgreen 30876262Sgreen sp = getservbyname("login", "tcp"); 30969591Sgreen if (sp == NULL) 310181111Sdes errx(1, "unknown service login/tcp"); 31169591Sgreen if (errno == ECONNREFUSED) 31269591Sgreen warn("remote host doesn't support Kerberos"); 31369591Sgreen if (errno == ENOENT) 31476262Sgreen warn("can't provide Kerberos auth data"); 31569591Sgreen newargv = malloc((argc + 2) * sizeof(*newargv)); 31669591Sgreen if (newargv == NULL) 31769591Sgreen err(1, "malloc"); 31869591Sgreen newargv[0] = argv[0]; 31969591Sgreen newargv[1] = "-K"; 32069591Sgreen for(i = 1; i < argc; ++i) 32169591Sgreen newargv[i + 1] = argv[i]; 32276262Sgreen newargv[argc + 1] = NULL; 32369591Sgreen execv(_PATH_RLOGIN, newargv); 32492559Sdes } 32592559Sdes } else { 32676262Sgreen#ifdef CRYPT 32776262Sgreen if (doencrypt) 32876262Sgreen errx(1, "the -x flag requires Kerberos authentication"); 32969591Sgreen#endif /* CRYPT */ 33076262Sgreen rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, 331113911Sdes family); 332147005Sdes } 33376262Sgreen#else 334147005Sdes rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 335147005Sdes#endif /* KERBEROS */ 336147005Sdes 337147005Sdes if (rem < 0) 33860573Skris exit(1); 33998941Sdes 340147005Sdes if (dflag && 341147005Sdes setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 342147005Sdes warn("setsockopt"); 343147005Sdes if (Dflag && 344147005Sdes setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 345147005Sdes warn("setsockopt NODELAY (ignored)"); 346147005Sdes 347147005Sdes sslen = sizeof(ss); 348147005Sdes one = IPTOS_LOWDELAY; 349149753Sdes if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 350147005Sdes ss.ss_family == AF_INET) { 351147005Sdes if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 352124211Sdes sizeof(int)) < 0) 35398941Sdes warn("setsockopt TOS (ignored)"); 354106130Sdes } else 355106130Sdes if (ss.ss_family == AF_INET) 356106130Sdes warn("setsockopt getsockname failed"); 357106130Sdes 358106130Sdes (void)setuid(uid); 359106130Sdes doit(omask); 360106130Sdes /*NOTREACHED*/ 36176262Sgreen} 36276262Sgreen 36369591Sgreenint child, defflags, deflflags, tabflag; 36492559Sdeschar deferase, defkill; 36592559Sdesstruct tchars deftc; 36692559Sdesstruct ltchars defltc; 36792559Sdesstruct tchars notc = { -1, -1, -1, -1, -1, -1 }; 36892559Sdesstruct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 36992559Sdes 37092559Sdesvoid 37192559Sdesdoit(long omask) 37292559Sdes{ 37392559Sdes struct sgttyb sb; 37492559Sdes 37592559Sdes (void)ioctl(0, TIOCGETP, (char *)&sb); 37692559Sdes defflags = sb.sg_flags; 377181111Sdes tabflag = defflags & TBDELAY; 378181111Sdes defflags &= ECHO | CRMOD; 379181111Sdes deferase = sb.sg_erase; 380181111Sdes defkill = sb.sg_kill; 381181111Sdes (void)ioctl(0, TIOCLGET, &deflflags); 382147005Sdes (void)ioctl(0, TIOCGETC, &deftc); 383147005Sdes notc.t_startc = deftc.t_startc; 384147005Sdes notc.t_stopc = deftc.t_stopc; 38592559Sdes (void)ioctl(0, TIOCGLTC, &defltc); 386147005Sdes (void)signal(SIGINT, SIG_IGN); 38792559Sdes setsignal(SIGHUP); 38892559Sdes setsignal(SIGQUIT); 38992559Sdes child = fork(); 39092559Sdes if (child == -1) { 39192559Sdes warn("fork"); 39292559Sdes done(1); 39392559Sdes } 39492559Sdes if (child == 0) { 39576262Sgreen mode(1); 39669591Sgreen if (reader(omask) == 0) { 39792559Sdes msg("connection closed."); 39869591Sgreen exit(0); 39960573Skris } 40092559Sdes sleep(1); 40169591Sgreen msg("\007connection closed."); 40298684Sdes exit(1); 40360573Skris } 40492559Sdes 40598684Sdes /* 40698684Sdes * We may still own the socket, and may have a pending SIGURG (or might 40769591Sgreen * receive one soon) that we really want to send to the reader. When 40898684Sdes * one of these comes in, the trap copytochild simply copies such 40998684Sdes * signals to the child. We can now unblock SIGURG and SIGUSR1 41092559Sdes * that were set above. 41192559Sdes */ 41298684Sdes (void)sigsetmask(omask); 41398684Sdes (void)signal(SIGCHLD, catch_child); 41460573Skris writer(); 41569591Sgreen msg("closed connection."); 41692559Sdes done(0); 41792559Sdes} 41892559Sdes 41969591Sgreen/* trap a signal, unless it is being ignored. */ 42060573Skrisvoid 42160573Skrissetsignal(int sig) 42292559Sdes{ 42369591Sgreen int omask = sigblock(sigmask(sig)); 42469591Sgreen 42598684Sdes if (signal(sig, exit) == SIG_IGN) 42698684Sdes (void)signal(sig, SIG_IGN); 42769591Sgreen (void)sigsetmask(omask); 42898684Sdes} 42998684Sdes 43098684Sdesvoid 43198684Sdesdone(int status) 43298684Sdes{ 43398684Sdes int w, wstatus; 43498684Sdes 43569591Sgreen mode(0); 43669591Sgreen if (child > 0) { 437181111Sdes /* make sure catch_child does not snap it up */ 438 (void)signal(SIGCHLD, SIG_DFL); 439 if (kill(child, SIGKILL) >= 0) 440 while ((w = wait(&wstatus)) > 0 && w != child); 441 } 442 exit(status); 443} 444 445int dosigwinch; 446 447/* 448 * This is called when the reader process gets the out-of-band (urgent) 449 * request to turn on the window-changing protocol. 450 */ 451void 452writeroob(int signo __unused) 453{ 454 if (dosigwinch == 0) { 455 sendwindow(); 456 (void)signal(SIGWINCH, sigwinch); 457 } 458 dosigwinch = 1; 459} 460 461void 462catch_child(int signo __unused) 463{ 464 union wait status; 465 int pid; 466 467 for (;;) { 468 pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL); 469 if (pid == 0) 470 return; 471 /* if the child (reader) dies, just quit */ 472 if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 473 done((int)(status.w_termsig | status.w_retcode)); 474 } 475 /* NOTREACHED */ 476} 477 478/* 479 * writer: write to remote: 0 -> line. 480 * ~. terminate 481 * ~^Z suspend rlogin process. 482 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 483 */ 484void 485writer(void) 486{ 487 int bol, local, n; 488 char c; 489 490 bol = 1; /* beginning of line */ 491 local = 0; 492 for (;;) { 493 n = read(STDIN_FILENO, &c, 1); 494 if (n <= 0) { 495 if (n < 0 && errno == EINTR) 496 continue; 497 break; 498 } 499 /* 500 * If we're at the beginning of the line and recognize a 501 * command character, then we echo locally. Otherwise, 502 * characters are echo'd remotely. If the command character 503 * is doubled, this acts as a force and local echo is 504 * suppressed. 505 */ 506 if (bol) { 507 bol = 0; 508 if (!noescape && c == escapechar) { 509 local = 1; 510 continue; 511 } 512 } else if (local) { 513 local = 0; 514 if (c == '.' || c == deftc.t_eofc) { 515 echo(c); 516 break; 517 } 518 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 519 bol = 1; 520 echo(c); 521 stop(c); 522 continue; 523 } 524 if (c != escapechar) 525#ifdef CRYPT 526#ifdef KERBEROS 527 if (doencrypt) 528 (void)des_enc_write(rem, 529 (char *)&escapechar, 1, 530 schedule, &cred.session); 531 else 532#endif 533#endif 534 (void)write(rem, &escapechar, 1); 535 } 536 537#ifdef CRYPT 538#ifdef KERBEROS 539 if (doencrypt) { 540 if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) { 541 msg("line gone"); 542 break; 543 } 544 } else 545#endif 546#endif 547 if (write(rem, &c, 1) == 0) { 548 msg("line gone"); 549 break; 550 } 551 bol = c == defkill || c == deftc.t_eofc || 552 c == deftc.t_intrc || c == defltc.t_suspc || 553 c == '\r' || c == '\n'; 554 } 555} 556 557void 558echo(char c) 559{ 560 char *p; 561 char buf[8]; 562 563 p = buf; 564 c &= 0177; 565 *p++ = escapechar; 566 if (c < ' ') { 567 *p++ = '^'; 568 *p++ = c + '@'; 569 } else if (c == 0177) { 570 *p++ = '^'; 571 *p++ = '?'; 572 } else 573 *p++ = c; 574 *p++ = '\r'; 575 *p++ = '\n'; 576 (void)write(STDOUT_FILENO, buf, p - buf); 577} 578 579void 580stop(char cmdc) 581{ 582 mode(0); 583 (void)signal(SIGCHLD, SIG_IGN); 584 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 585 (void)signal(SIGCHLD, catch_child); 586 mode(1); 587 sigwinch(0); /* check for size changes */ 588} 589 590void 591sigwinch(int signo __unused) 592{ 593 struct winsize ws; 594 595 if (dosigwinch && get_window_size(0, &ws) == 0 && 596 bcmp(&ws, &winsize, sizeof(ws))) { 597 winsize = ws; 598 sendwindow(); 599 } 600} 601 602/* 603 * Send the window size to the server via the magic escape 604 */ 605void 606sendwindow(void) 607{ 608 struct winsize *wp; 609 char obuf[4 + sizeof (struct winsize)]; 610 611 wp = (struct winsize *)(obuf+4); 612 obuf[0] = 0377; 613 obuf[1] = 0377; 614 obuf[2] = 's'; 615 obuf[3] = 's'; 616 wp->ws_row = htons(winsize.ws_row); 617 wp->ws_col = htons(winsize.ws_col); 618 wp->ws_xpixel = htons(winsize.ws_xpixel); 619 wp->ws_ypixel = htons(winsize.ws_ypixel); 620 621#ifdef CRYPT 622#ifdef KERBEROS 623 if(doencrypt) 624 (void)des_enc_write(rem, obuf, sizeof(obuf), 625 schedule, &cred.session); 626 else 627#endif 628#endif 629 (void)write(rem, obuf, sizeof(obuf)); 630} 631 632/* 633 * reader: read from remote: line -> 1 634 */ 635#define READING 1 636#define WRITING 2 637 638jmp_buf rcvtop; 639int ppid, rcvcnt, rcvstate; 640char rcvbuf[8 * 1024]; 641 642void 643oob(int signo __unused) 644{ 645 struct sgttyb sb; 646 int atmark, n, out, rcvd; 647 char waste[BUFSIZ], mark; 648 649 out = O_RDWR; 650 rcvd = 0; 651 while (recv(rem, &mark, 1, MSG_OOB) < 0) { 652 switch (errno) { 653 case EWOULDBLOCK: 654 /* 655 * Urgent data not here yet. It may not be possible 656 * to send it yet if we are blocked for output and 657 * our input buffer is full. 658 */ 659 if (rcvcnt < (int)sizeof(rcvbuf)) { 660 n = read(rem, rcvbuf + rcvcnt, 661 sizeof(rcvbuf) - rcvcnt); 662 if (n <= 0) 663 return; 664 rcvd += n; 665 } else { 666 n = read(rem, waste, sizeof(waste)); 667 if (n <= 0) 668 return; 669 } 670 continue; 671 default: 672 return; 673 } 674 } 675 if (mark & TIOCPKT_WINDOW) { 676 /* Let server know about window size changes */ 677 (void)kill(ppid, SIGUSR1); 678 } 679 if (!eight && (mark & TIOCPKT_NOSTOP)) { 680 (void)ioctl(0, TIOCGETP, (char *)&sb); 681 sb.sg_flags &= ~CBREAK; 682 sb.sg_flags |= RAW; 683 (void)ioctl(0, TIOCSETN, (char *)&sb); 684 notc.t_stopc = -1; 685 notc.t_startc = -1; 686 (void)ioctl(0, TIOCSETC, (char *)¬c); 687 } 688 if (!eight && (mark & TIOCPKT_DOSTOP)) { 689 (void)ioctl(0, TIOCGETP, (char *)&sb); 690 sb.sg_flags &= ~RAW; 691 sb.sg_flags |= CBREAK; 692 (void)ioctl(0, TIOCSETN, (char *)&sb); 693 notc.t_stopc = deftc.t_stopc; 694 notc.t_startc = deftc.t_startc; 695 (void)ioctl(0, TIOCSETC, (char *)¬c); 696 } 697 if (mark & TIOCPKT_FLUSHWRITE) { 698 (void)ioctl(1, TIOCFLUSH, (char *)&out); 699 for (;;) { 700 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 701 warn("ioctl"); 702 break; 703 } 704 if (atmark) 705 break; 706 n = read(rem, waste, sizeof (waste)); 707 if (n <= 0) 708 break; 709 } 710 /* 711 * Don't want any pending data to be output, so clear the recv 712 * buffer. If we were hanging on a write when interrupted, 713 * don't want it to restart. If we were reading, restart 714 * anyway. 715 */ 716 rcvcnt = 0; 717 longjmp(rcvtop, 1); 718 } 719 720 /* oob does not do FLUSHREAD (alas!) */ 721 722 /* 723 * If we filled the receive buffer while a read was pending, longjmp 724 * to the top to restart appropriately. Don't abort a pending write, 725 * however, or we won't know how much was written. 726 */ 727 if (rcvd && rcvstate == READING) 728 longjmp(rcvtop, 1); 729} 730 731/* reader: read from remote: line -> 1 */ 732int 733reader(int omask) 734{ 735 int pid, n, remaining; 736 char *bufp; 737 738#if BSD >= 43 || defined(SUNOS4) 739 pid = getpid(); /* modern systems use positives for pid */ 740#else 741 pid = -getpid(); /* old broken systems use negatives */ 742#endif 743 (void)signal(SIGTTOU, SIG_IGN); 744 (void)signal(SIGURG, oob); 745 (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 746 ppid = getppid(); 747 (void)fcntl(rem, F_SETOWN, pid); 748 (void)setjmp(rcvtop); 749 (void)sigsetmask(omask); 750 bufp = rcvbuf; 751 for (;;) { 752 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 753 rcvstate = WRITING; 754 n = write(STDOUT_FILENO, bufp, remaining); 755 if (n < 0) { 756 if (errno != EINTR) 757 return (-1); 758 continue; 759 } 760 bufp += n; 761 } 762 bufp = rcvbuf; 763 rcvcnt = 0; 764 rcvstate = READING; 765 766#ifdef CRYPT 767#ifdef KERBEROS 768 if (doencrypt) 769 rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf), 770 schedule, &cred.session); 771 else 772#endif 773#endif 774 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 775 if (rcvcnt == 0) 776 return (0); 777 if (rcvcnt < 0) { 778 if (errno == EINTR) 779 continue; 780 warn("read"); 781 return (-1); 782 } 783 } 784} 785 786void 787mode(int f) 788{ 789 struct ltchars *ltc; 790 struct sgttyb sb; 791 struct tchars *tc; 792 int lflags; 793 794 (void)ioctl(0, TIOCGETP, (char *)&sb); 795 (void)ioctl(0, TIOCLGET, (char *)&lflags); 796 switch(f) { 797 case 0: 798 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 799 sb.sg_flags |= defflags|tabflag; 800 tc = &deftc; 801 ltc = &defltc; 802 sb.sg_kill = defkill; 803 sb.sg_erase = deferase; 804 lflags = deflflags; 805 break; 806 case 1: 807 sb.sg_flags |= (eight ? RAW : CBREAK); 808 sb.sg_flags &= ~defflags; 809 /* preserve tab delays, but turn off XTABS */ 810 if ((sb.sg_flags & TBDELAY) == XTABS) 811 sb.sg_flags &= ~TBDELAY; 812 tc = ¬c; 813 ltc = &noltc; 814 sb.sg_kill = sb.sg_erase = -1; 815 if (litout) 816 lflags |= LLITOUT; 817 break; 818 default: 819 return; 820 } 821 (void)ioctl(0, TIOCSLTC, (char *)ltc); 822 (void)ioctl(0, TIOCSETC, (char *)tc); 823 (void)ioctl(0, TIOCSETN, (char *)&sb); 824 (void)ioctl(0, TIOCLSET, (char *)&lflags); 825} 826 827void 828lostpeer(int signo __unused) 829{ 830 (void)signal(SIGPIPE, SIG_IGN); 831 msg("\007connection closed."); 832 done(1); 833} 834 835/* copy SIGURGs to the child process via SIGUSR1. */ 836void 837copytochild(int signo __unused) 838{ 839 (void)kill(child, SIGUSR1); 840} 841 842void 843msg(const char *str) 844{ 845 (void)fprintf(stderr, "rlogin: %s\r\n", str); 846} 847 848void 849usage(void) 850{ 851 (void)fprintf(stderr, 852 "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 853#ifdef KERBEROS 854#ifdef CRYPT 855 "8DEKLdx", " [-k realm] "); 856#else 857 "8DEKLd", " [-k realm] "); 858#endif 859#else 860 "8DEKLd", " "); 861#endif 862 exit(1); 863} 864 865u_int 866getescape(char *p) 867{ 868 long val; 869 int len; 870 871 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 872 return ((u_int)*p); 873 /* otherwise, \nnn */ 874 if (*p == '\\' && len >= 2 && len <= 4) { 875 val = strtol(++p, NULL, 8); 876 for (;;) { 877 if (!*++p) 878 return ((u_int)val); 879 if (*p < '0' || *p > '8') 880 break; 881 } 882 } 883 msg("illegal option value -- e"); 884 usage(); 885 /* NOTREACHED */ 886} 887