rlogin.c revision 1.17 1 /* $NetBSD: rlogin.c,v 1.17 1997/02/17 06:14:25 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1983, 1990, 1993\n\
39 The Regents of the University of California. All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95";
45 #else
46 static char rcsid[] = "$NetBSD: rlogin.c,v 1.17 1997/02/17 06:14:25 mrg Exp $";
47 #endif
48 #endif /* not lint */
49
50 /*
51 * rlogin - remote login
52 */
53 #include <sys/param.h>
54 #include <sys/ioctl.h>
55 #include <sys/socket.h>
56 #include <sys/time.h>
57 #include <sys/resource.h>
58 #include <sys/wait.h>
59 #include <sys/ioctl.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <netdb.h>
68 #include <pwd.h>
69 #include <setjmp.h>
70 #include <termios.h>
71 #include <signal.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76
77 #ifdef __STDC__
78 #include <stdarg.h>
79 #else
80 #include <varargs.h>
81 #endif
82
83 #ifdef KERBEROS
84 #include <kerberosIV/des.h>
85 #include <kerberosIV/krb.h>
86 #include <kerberosIV/kstream.h>
87
88 #include "krb.h"
89
90 CREDENTIALS cred;
91 Key_schedule schedule;
92 MSG_DAT msg_data;
93 struct sockaddr_in local, foreign;
94 int use_kerberos = 1, doencrypt;
95 char dest_realm[REALM_SZ];
96 kstream krem;
97 #endif
98
99 #ifndef TIOCPKT_WINDOW
100 #define TIOCPKT_WINDOW 0x80
101 #endif
102
103 /* concession to Sun */
104 #ifndef SIGUSR1
105 #define SIGUSR1 30
106 #endif
107
108 #ifndef CCEQ
109 #define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0)
110 #endif
111
112 int eight, rem;
113 struct termios deftty;
114
115 int noescape;
116 u_char escapechar = '~';
117
118 #ifdef OLDSUN
119 struct winsize {
120 unsigned short ws_row, ws_col;
121 unsigned short ws_xpixel, ws_ypixel;
122 };
123 #else
124 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
125 #endif
126 struct winsize winsize;
127
128 void catch_child __P((int));
129 void copytochild __P((int));
130 __dead void doit __P((sigset_t *));
131 __dead void done __P((int));
132 void echo __P((char));
133 u_int getescape __P((char *));
134 void lostpeer __P((int));
135 void mode __P((int));
136 void msg __P((char *));
137 void oob __P((int));
138 int reader __P((sigset_t *));
139 void sendwindow __P((void));
140 void setsignal __P((int));
141 int speed __P((int));
142 void sigwinch __P((int));
143 void stop __P((int));
144 __dead void usage __P((void));
145 void writer __P((void));
146 void writeroob __P((int));
147
148 #ifdef KERBEROS
149 void warning __P((const char *, ...));
150 #endif
151 #ifdef OLDSUN
152 int get_window_size __P((int, struct winsize *));
153 #endif
154
155 int
156 main(argc, argv)
157 int argc;
158 char *argv[];
159 {
160 struct passwd *pw;
161 struct servent *sp;
162 struct termios tty;
163 sigset_t smask;
164 int argoff, ch, dflag, one, uid;
165 int i, len, len2;
166 char *host, *p, *user, term[1024] = "network";
167 speed_t ospeed;
168 struct sigaction sa;
169 struct rlimit rlim;
170 #ifdef KERBEROS
171 KTEXT_ST ticket;
172 char **orig_argv = argv;
173 int sock;
174 long authopts;
175 int through_once = 0;
176 char *cp = (char *) NULL;
177 extern int _kstream_des_debug_OOB;
178 #endif
179
180 argoff = dflag = 0;
181 one = 1;
182 host = user = NULL;
183
184 if (p = strrchr(argv[0], '/'))
185 ++p;
186 else
187 p = argv[0];
188
189 if (strcmp(p, "rlogin") != 0)
190 host = p;
191
192 /* handle "rlogin host flags" */
193 if (!host && argc > 2 && argv[1][0] != '-') {
194 host = argv[1];
195 argoff = 1;
196 }
197
198 #ifdef KERBEROS
199 #define OPTIONS "8EKLde:k:l:x"
200 #else
201 #define OPTIONS "8EKLde:l:"
202 #endif
203 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
204 switch(ch) {
205 case '8':
206 eight = 1;
207 break;
208 case 'E':
209 noescape = 1;
210 break;
211 case 'K':
212 #ifdef KERBEROS
213 use_kerberos = 0;
214 #endif
215 break;
216 case 'd':
217 #ifdef KERBEROS
218 _kstream_des_debug_OOB = 1;
219 #endif
220 dflag = 1;
221 break;
222 case 'e':
223 noescape = 0;
224 escapechar = getescape(optarg);
225 break;
226 #ifdef KERBEROS
227 case 'k':
228 (void)strncpy(dest_realm, optarg, REALM_SZ);
229 break;
230 #endif
231 case 'l':
232 user = optarg;
233 break;
234 #ifdef CRYPT
235 #ifdef KERBEROS
236 case 'x':
237 doencrypt = 1;
238 break;
239 #endif
240 #endif
241 case '?':
242 default:
243 usage();
244 }
245 optind += argoff;
246 argc -= optind;
247 argv += optind;
248
249 /* if haven't gotten a host yet, do so */
250 if (!host && !(host = *argv++))
251 usage();
252
253 if (*argv)
254 usage();
255
256 if (!(pw = getpwuid(uid = getuid())))
257 errx(1, "unknown user id.");
258 /* Accept user1@host format, though "-l user2" overrides user1 */
259 p = strchr(host, '@');
260 if (p) {
261 *p = '\0';
262 if (!user && p > host)
263 user = host;
264 host = p + 1;
265 if (*host == '\0')
266 usage();
267 }
268 if (!user)
269 user = pw->pw_name;
270
271 sp = NULL;
272 #ifdef KERBEROS
273 if (use_kerberos) {
274 sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
275 if (sp == NULL) {
276 use_kerberos = 0;
277 warning("can't get entry for %s/tcp service",
278 doencrypt ? "eklogin" : "klogin");
279 }
280 }
281 #endif
282 if (sp == NULL)
283 sp = getservbyname("login", "tcp");
284 if (sp == NULL)
285 errx(1, "login/tcp: unknown service.");
286
287 if (p = getenv("TERM")) {
288 (void)strncpy(term, p, sizeof(term) - 1);
289 term[sizeof(term) - 1] = '\0';
290 }
291 len = strlen(term);
292 if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
293 /* start at 2 to include the / */
294 for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
295 i /= 10;
296
297 if (len + len2 < sizeof(term))
298 (void)snprintf(term + len, len2 + 1, "/%d", ospeed);
299 }
300
301 (void)get_window_size(0, &winsize);
302
303 sigemptyset(&sa.sa_mask);
304 sa.sa_flags = SA_RESTART;
305 sa.sa_handler = lostpeer;
306 (void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
307 /* will use SIGUSR1 for window size hack, so hold it off */
308 sigemptyset(&smask);
309 sigaddset(&smask, SIGURG);
310 sigaddset(&smask, SIGUSR1);
311 (void)sigprocmask(SIG_SETMASK, &smask, &smask);
312 /*
313 * We set SIGURG and SIGUSR1 below so that an
314 * incoming signal will be held pending rather than being
315 * discarded. Note that these routines will be ready to get
316 * a signal by the time that they are unblocked below.
317 */
318 sa.sa_handler = copytochild;
319 (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
320 sa.sa_handler = writeroob;
321 (void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
322
323 /* don't dump core */
324 rlim.rlim_cur = rlim.rlim_max = 0;
325 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
326 warn("setrlimit");
327
328 #ifdef KERBEROS
329 try_connect:
330 if (use_kerberos) {
331 struct hostent *hp;
332
333 /* Fully qualify hostname (needed for krb_realmofhost). */
334 hp = gethostbyname(host);
335 if (hp != NULL && !(host = strdup(hp->h_name)))
336 errx(1, "%s", strerror(ENOMEM));
337
338 rem = KSUCCESS;
339 errno = 0;
340 #ifdef CRYPT
341 if (doencrypt)
342 authopts = KOPT_DO_MUTUAL;
343 else
344 #endif /* CRYPT */
345 authopts = 0L;
346
347 /* default this now, once. */
348 if (!(cp = krb_realmofhost (host))) {
349 warnx("Unknown realm for host %s.", host);
350 use_kerberos = 0;
351 goto try_connect;
352 }
353
354 strncpy(dest_realm, cp, REALM_SZ);
355
356 rem = kcmd(&sock, &host, sp->s_port, pw->pw_name, user,
357 term, 0, &ticket, "rcmd", dest_realm,
358 &cred, schedule, &msg_data, &local, &foreign,
359 authopts);
360
361 if (rem != KSUCCESS) {
362 switch(rem) {
363
364 case KDC_PR_UNKNOWN:
365 warnx("Host %s not registered for %s",
366 host, "Kerberos rlogin service");
367 use_kerberos = 0;
368 goto try_connect;
369 case NO_TKT_FIL:
370 if (through_once++) {
371 use_kerberos = 0;
372 goto try_connect;
373 }
374 #ifdef notyet
375 krb_get_pw_in_tkt(user, krb_realm, "krbtgt",
376 krb_realm,
377 DEFAULT_TKT_LIFE/5, 0);
378 goto try_connect;
379 #endif
380 default:
381 warnx("Kerberos rcmd failed: %s",
382 (rem == -1) ? "rcmd protocol failure" :
383 krb_err_txt[rem]);
384 use_kerberos = 0;
385 goto out;
386 }
387 }
388 rem = sock;
389 if (doencrypt)
390 krem = kstream_create_rlogin_from_fd(rem, &schedule,
391 &cred.session);
392 else
393 krem = kstream_create_from_fd(rem, 0, 0);
394 kstream_set_buffer_mode(krem, 0);
395 } else {
396 #ifdef CRYPT
397 out:
398 if (doencrypt)
399 errx(1, "the -x flag requires Kerberos authentication.");
400 #else
401 out:
402 #endif /* CRYPT */
403 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
404 if ( rem < 0 )
405 exit(1);
406 krem = kstream_create_from_fd(rem, 0, 0);
407 }
408 #else
409 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
410
411 #endif /* KERBEROS */
412
413 if (rem < 0)
414 exit(1);
415
416 if (dflag &&
417 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
418 warn("setsockopt DEBUG (ignored)");
419 one = IPTOS_LOWDELAY;
420 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
421 warn("setsockopt TOS (ignored)");
422
423 (void)setuid(uid);
424 doit(&smask);
425 /*NOTREACHED*/
426 }
427
428 #if BSD >= 198810
429 int
430 speed(fd)
431 int fd;
432 {
433 struct termios tt;
434
435 (void)tcgetattr(fd, &tt);
436
437 return ((int) cfgetispeed(&tt));
438 }
439 #else
440 int speeds[] = { /* for older systems, B0 .. EXTB */
441 0, 50, 75, 110,
442 134, 150, 200, 300,
443 600, 1200, 1800, 2400,
444 4800, 9600, 19200, 38400
445 };
446
447 int
448 speed(fd)
449 int fd;
450 {
451 struct termios tt;
452
453 (void)tcgetattr(fd, &tt);
454
455 return (speeds[(int)cfgetispeed(&tt)]);
456 }
457 #endif
458
459 pid_t child;
460 struct termios deftt;
461 struct termios nott;
462
463 void
464 doit(smask)
465 sigset_t *smask;
466 {
467 int i;
468 struct sigaction sa;
469
470 for (i = 0; i < NCCS; i++)
471 nott.c_cc[i] = _POSIX_VDISABLE;
472 tcgetattr(0, &deftt);
473 nott.c_cc[VSTART] = deftt.c_cc[VSTART];
474 nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
475 sigemptyset(&sa.sa_mask);
476 sa.sa_flags = SA_RESTART;
477 sa.sa_handler = SIG_IGN;
478 (void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
479 setsignal(SIGHUP);
480 setsignal(SIGQUIT);
481 mode(1);
482 child = fork();
483 if (child == -1) {
484 warn("fork");
485 done(1);
486 }
487 if (child == 0) {
488 mode(1);
489 if (reader(smask) == 0) {
490 msg("connection closed.");
491 exit(0);
492 }
493 sleep(1);
494 msg("\aconnection closed.");
495 exit(1);
496 }
497
498 /*
499 * We may still own the socket, and may have a pending SIGURG (or might
500 * receive one soon) that we really want to send to the reader. When
501 * one of these comes in, the trap copytochild simply copies such
502 * signals to the child. We can now unblock SIGURG and SIGUSR1
503 * that were set above.
504 */
505 (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
506 sa.sa_handler = catch_child;
507 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
508 writer();
509 msg("closed connection.");
510 done(0);
511 }
512
513 /* trap a signal, unless it is being ignored. */
514 void
515 setsignal(sig)
516 int sig;
517 {
518 struct sigaction sa;
519 sigset_t sigs;
520
521 sigemptyset(&sigs);
522 sigaddset(&sigs, sig);
523 sigprocmask(SIG_BLOCK, &sigs, &sigs);
524
525 sigemptyset(&sa.sa_mask);
526 sa.sa_handler = exit;
527 sa.sa_flags = SA_RESTART;
528 (void)sigaction(sig, &sa, &sa);
529 if (sa.sa_handler == SIG_IGN)
530 (void)sigaction(sig, &sa, (struct sigaction *) 0);
531
532 (void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
533 }
534
535 __dead void
536 done(status)
537 int status;
538 {
539 pid_t w;
540 int wstatus;
541 struct sigaction sa;
542
543 mode(0);
544 if (child > 0) {
545 /* make sure catch_child does not snap it up */
546 sigemptyset(&sa.sa_mask);
547 sa.sa_handler = SIG_DFL;
548 sa.sa_flags = 0;
549 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
550 if (kill(child, SIGKILL) >= 0)
551 while ((w = wait(&wstatus)) > 0 && w != child)
552 continue;
553 }
554 exit(status);
555 }
556
557 int dosigwinch;
558
559 /*
560 * This is called when the reader process gets the out-of-band (urgent)
561 * request to turn on the window-changing protocol.
562 */
563 void
564 writeroob(signo)
565 int signo;
566 {
567 struct sigaction sa;
568
569 if (dosigwinch == 0) {
570 sendwindow();
571 sigemptyset(&sa.sa_mask);
572 sa.sa_handler = sigwinch;
573 sa.sa_flags = SA_RESTART;
574 (void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
575 }
576 dosigwinch = 1;
577 }
578
579 void
580 catch_child(signo)
581 int signo;
582 {
583 int status;
584 pid_t pid;
585
586 for (;;) {
587 pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
588 if (pid == 0)
589 return;
590 /* if the child (reader) dies, just quit */
591 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
592 done(WEXITSTATUS(status) | WTERMSIG(status));
593 }
594 /* NOTREACHED */
595 }
596
597 /*
598 * writer: write to remote: 0 -> line.
599 * ~. terminate
600 * ~^Z suspend rlogin process.
601 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
602 */
603 void
604 writer()
605 {
606 register int bol, local, n;
607 char c;
608
609 bol = 1; /* beginning of line */
610 local = 0;
611 for (;;) {
612 n = read(STDIN_FILENO, &c, 1);
613 if (n <= 0) {
614 if (n < 0 && errno == EINTR)
615 continue;
616 break;
617 }
618 /*
619 * If we're at the beginning of the line and recognize a
620 * command character, then we echo locally. Otherwise,
621 * characters are echo'd remotely. If the command character
622 * is doubled, this acts as a force and local echo is
623 * suppressed.
624 */
625 if (bol) {
626 bol = 0;
627 if (!noescape && c == escapechar) {
628 local = 1;
629 continue;
630 }
631 } else if (local) {
632 local = 0;
633 if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
634 echo(c);
635 break;
636 }
637 if (CCEQ(deftty.c_cc[VSUSP], c)) {
638 bol = 1;
639 echo(c);
640 stop(1);
641 continue;
642 }
643 if (CCEQ(deftty.c_cc[VDSUSP], c)) {
644 bol = 1;
645 echo(c);
646 stop(0);
647 continue;
648 }
649 if (c != escapechar)
650 #ifdef KERBEROS
651 (void)kstream_write(krem,
652 (char *)&escapechar, 1);
653 else
654 #endif
655 (void)write(rem, &escapechar, 1);
656 }
657
658 #ifdef KERBEROS
659 if (kstream_write(krem, &c, 1) == 0) {
660 msg("line gone");
661 break;
662 }
663 #endif
664 if (write(rem, &c, 1) == 0) {
665 msg("line gone");
666 break;
667 }
668 bol = CCEQ(deftty.c_cc[VKILL], c) ||
669 CCEQ(deftty.c_cc[VEOF], c) ||
670 CCEQ(deftty.c_cc[VINTR], c) ||
671 CCEQ(deftty.c_cc[VSUSP], c) ||
672 c == '\r' || c == '\n';
673 }
674 }
675
676 void
677 #if __STDC__
678 echo(register char c)
679 #else
680 echo(c)
681 register char c;
682 #endif
683 {
684 register char *p;
685 char buf[8];
686
687 p = buf;
688 c &= 0177;
689 *p++ = escapechar;
690 if (c < ' ') {
691 *p++ = '^';
692 *p++ = c + '@';
693 } else if (c == 0177) {
694 *p++ = '^';
695 *p++ = '?';
696 } else
697 *p++ = c;
698 *p++ = '\r';
699 *p++ = '\n';
700 (void)write(STDOUT_FILENO, buf, p - buf);
701 }
702
703 void
704 stop(all)
705 int all;
706 {
707 struct sigaction sa;
708
709 mode(0);
710 sigemptyset(&sa.sa_mask);
711 sa.sa_handler = SIG_IGN;
712 sa.sa_flags = SA_RESTART;
713 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
714 (void)kill(all ? 0 : getpid(), SIGTSTP);
715 sa.sa_handler = catch_child;
716 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
717 mode(1);
718 sigwinch(0); /* check for size changes */
719 }
720
721 void
722 sigwinch(signo)
723 int signo;
724 {
725 struct winsize ws;
726
727 if (dosigwinch && get_window_size(0, &ws) == 0 &&
728 memcmp(&ws, &winsize, sizeof(ws))) {
729 winsize = ws;
730 sendwindow();
731 }
732 }
733
734 /*
735 * Send the window size to the server via the magic escape
736 */
737 void
738 sendwindow()
739 {
740 struct winsize *wp;
741 char obuf[4 + sizeof (struct winsize)];
742
743 wp = (struct winsize *)(obuf+4);
744 obuf[0] = 0377;
745 obuf[1] = 0377;
746 obuf[2] = 's';
747 obuf[3] = 's';
748 wp->ws_row = htons(winsize.ws_row);
749 wp->ws_col = htons(winsize.ws_col);
750 wp->ws_xpixel = htons(winsize.ws_xpixel);
751 wp->ws_ypixel = htons(winsize.ws_ypixel);
752
753 #ifdef KERBEROS
754 (void)kstream_write(krem, obuf, sizeof(obuf));
755 #endif
756 (void)write(rem, obuf, sizeof(obuf));
757 }
758
759 /*
760 * reader: read from remote: line -> 1
761 */
762 #define READING 1
763 #define WRITING 2
764
765 jmp_buf rcvtop;
766 pid_t ppid;
767 int rcvcnt, rcvstate;
768 char rcvbuf[8 * 1024];
769
770 void
771 oob(signo)
772 int signo;
773 {
774 struct termios tty;
775 int atmark, n, rcvd;
776 char waste[BUFSIZ], mark;
777
778 rcvd = 0;
779 while (recv(rem, &mark, 1, MSG_OOB) < 0) {
780 switch (errno) {
781 case EWOULDBLOCK:
782 /*
783 * Urgent data not here yet. It may not be possible
784 * to send it yet if we are blocked for output and
785 * our input buffer is full.
786 */
787 if (rcvcnt < sizeof(rcvbuf)) {
788 n = read(rem, rcvbuf + rcvcnt,
789 sizeof(rcvbuf) - rcvcnt);
790 if (n <= 0)
791 return;
792 rcvd += n;
793 } else {
794 n = read(rem, waste, sizeof(waste));
795 if (n <= 0)
796 return;
797 }
798 continue;
799 default:
800 return;
801 }
802 }
803 if (mark & TIOCPKT_WINDOW) {
804 /* Let server know about window size changes */
805 (void)kill(ppid, SIGUSR1);
806 }
807 if (!eight && (mark & TIOCPKT_NOSTOP)) {
808 (void)tcgetattr(0, &tty);
809 tty.c_iflag &= ~IXON;
810 (void)tcsetattr(0, TCSANOW, &tty);
811 }
812 if (!eight && (mark & TIOCPKT_DOSTOP)) {
813 (void)tcgetattr(0, &tty);
814 tty.c_iflag |= (deftty.c_iflag & IXON);
815 (void)tcsetattr(0, TCSANOW, &tty);
816 }
817 if (mark & TIOCPKT_FLUSHWRITE) {
818 (void)tcflush(1, TCIOFLUSH);
819 for (;;) {
820 if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
821 warn("ioctl SIOCATMARK (ignored)");
822 break;
823 }
824 if (atmark)
825 break;
826 n = read(rem, waste, sizeof (waste));
827 if (n <= 0)
828 break;
829 }
830 /*
831 * Don't want any pending data to be output, so clear the recv
832 * buffer. If we were hanging on a write when interrupted,
833 * don't want it to restart. If we were reading, restart
834 * anyway.
835 */
836 rcvcnt = 0;
837 longjmp(rcvtop, 1);
838 }
839
840 /* oob does not do FLUSHREAD (alas!) */
841
842 /*
843 * If we filled the receive buffer while a read was pending, longjmp
844 * to the top to restart appropriately. Don't abort a pending write,
845 * however, or we won't know how much was written.
846 */
847 if (rcvd && rcvstate == READING)
848 longjmp(rcvtop, 1);
849 }
850
851 /* reader: read from remote: line -> 1 */
852 int
853 reader(smask)
854 sigset_t *smask;
855 {
856 pid_t pid;
857 int n, remaining;
858 char *bufp;
859 struct sigaction sa;
860
861 #if BSD >= 43 || defined(SUNOS4)
862 pid = getpid(); /* modern systems use positives for pid */
863 #else
864 pid = -getpid(); /* old broken systems use negatives */
865 #endif
866 sigemptyset(&sa.sa_mask);
867 sa.sa_flags = SA_RESTART;
868 sa.sa_handler = SIG_IGN;
869 (void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
870 sa.sa_handler = oob;
871 (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
872 ppid = getppid();
873 (void)fcntl(rem, F_SETOWN, pid);
874 (void)setjmp(rcvtop);
875 (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
876 bufp = rcvbuf;
877 for (;;) {
878 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
879 rcvstate = WRITING;
880 n = write(STDOUT_FILENO, bufp, remaining);
881 if (n < 0) {
882 if (errno != EINTR)
883 return (-1);
884 continue;
885 }
886 bufp += n;
887 }
888 bufp = rcvbuf;
889 rcvcnt = 0;
890 rcvstate = READING;
891
892 #ifdef KERBEROS
893 rcvcnt = kstream_read(krem, rcvbuf, sizeof(rcvbuf));
894 #endif
895 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
896 if (rcvcnt == 0)
897 return (0);
898 if (rcvcnt < 0) {
899 if (errno == EINTR)
900 continue;
901 warn("read");
902 return (-1);
903 }
904 }
905 }
906
907 void
908 mode(f)
909 int f;
910 {
911 struct termios tty;
912
913 switch (f) {
914 case 0:
915 (void)tcsetattr(0, TCSANOW, &deftty);
916 break;
917 case 1:
918 (void)tcgetattr(0, &deftty);
919 tty = deftty;
920 /* This is loosely derived from sys/compat/tty_compat.c. */
921 tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
922 tty.c_iflag &= ~ICRNL;
923 tty.c_oflag &= ~OPOST;
924 tty.c_cc[VMIN] = 1;
925 tty.c_cc[VTIME] = 0;
926 if (eight) {
927 tty.c_iflag &= IXOFF;
928 tty.c_cflag &= ~(CSIZE|PARENB);
929 tty.c_cflag |= CS8;
930 }
931 (void)tcsetattr(0, TCSANOW, &tty);
932 break;
933
934 default:
935 return;
936 }
937 }
938
939 void
940 lostpeer(signo)
941 int signo;
942 {
943 struct sigaction sa;
944 sa.sa_flags = SA_RESTART;
945 sa.sa_handler = SIG_IGN;
946 (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
947 msg("\aconnection closed.");
948 done(1);
949 }
950
951 /* copy SIGURGs to the child process. */
952 void
953 copytochild(signo)
954 int signo;
955 {
956
957 (void)kill(child, SIGURG);
958 }
959
960 void
961 msg(str)
962 char *str;
963 {
964
965 (void)fprintf(stderr, "rlogin: %s\r\n", str);
966 }
967
968 #ifdef KERBEROS
969 /* VARARGS */
970 void
971 #if __STDC__
972 warning(const char *fmt, ...)
973 #else
974 warning(fmt, va_alist)
975 char *fmt;
976 va_dcl
977 #endif
978 {
979 va_list ap;
980
981 (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
982 #ifdef __STDC__
983 va_start(ap, fmt);
984 #else
985 va_start(ap);
986 #endif
987 vfprintf(stderr, fmt, ap);
988 va_end(ap);
989 (void)fprintf(stderr, ".\n");
990 }
991 #endif
992
993 __dead void
994 usage()
995 {
996 (void)fprintf(stderr,
997 "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
998 #ifdef KERBEROS
999 #ifdef CRYPT
1000 "8EKLx", " [-k realm] ");
1001 #else
1002 "8EKL", " [-k realm] ");
1003 #endif
1004 #else
1005 "8EL", " ");
1006 #endif
1007 exit(1);
1008 }
1009
1010 /*
1011 * The following routine provides compatibility (such as it is) between older
1012 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
1013 */
1014 #ifdef OLDSUN
1015 int
1016 get_window_size(fd, wp)
1017 int fd;
1018 struct winsize *wp;
1019 {
1020 struct ttysize ts;
1021 int error;
1022
1023 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
1024 return (error);
1025 wp->ws_row = ts.ts_lines;
1026 wp->ws_col = ts.ts_cols;
1027 wp->ws_xpixel = 0;
1028 wp->ws_ypixel = 0;
1029 return (0);
1030 }
1031 #endif
1032
1033 u_int
1034 getescape(p)
1035 register char *p;
1036 {
1037 long val;
1038 int len;
1039
1040 if ((len = strlen(p)) == 1) /* use any single char, including '\' */
1041 return ((u_int)*p);
1042 /* otherwise, \nnn */
1043 if (*p == '\\' && len >= 2 && len <= 4) {
1044 val = strtol(++p, NULL, 8);
1045 for (;;) {
1046 if (!*++p)
1047 return ((u_int)val);
1048 if (*p < '0' || *p > '8')
1049 break;
1050 }
1051 }
1052 msg("illegal option value -- e");
1053 usage();
1054 /* NOTREACHED */
1055 }
1056