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