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