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