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