Merge from vendor branch NTPD:
[dragonfly.git] / usr.bin / rlogin / rlogin.c
1 /*
2  * Copyright (c) 1983, 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technology, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  * @(#) Copyright (c) 1983, 1990, 1993 The Regents of the University of California.  All rights reserved.
41  * @(#)rlogin.c 8.1 (Berkeley) 6/6/93
42  * $FreeBSD: src/usr.bin/rlogin/rlogin.c,v 1.24.2.2 2002/07/19 18:03:41 ru Exp $
43  * $DragonFly: src/usr.bin/rlogin/rlogin.c,v 1.4 2004/08/30 18:06:50 eirikn Exp $
44  */
45
46 /*
47  * rlogin - remote login
48  */
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <sys/resource.h>
53 #include <sys/wait.h>
54
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 #include <netinet/tcp.h>
59
60 #include <err.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <libutil.h>
64 #include <netdb.h>
65 #include <paths.h>
66 #include <pwd.h>
67 #include <setjmp.h>
68 #include <sgtty.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <err.h>
75
76 #ifdef KERBEROS
77 #include <openssl/des.h>
78 #include <krb.h>
79
80 #include "krb.h"
81
82 CREDENTIALS cred;
83 Key_schedule schedule;
84 int use_kerberos = 1, doencrypt;
85 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
86 #endif
87
88 #ifndef TIOCPKT_WINDOW
89 #define TIOCPKT_WINDOW  0x80
90 #endif
91
92 /* concession to Sun */
93 #ifndef SIGUSR1
94 #define SIGUSR1 30
95 #endif
96
97 int eight, litout, rem;
98 int family = PF_UNSPEC;
99
100 int noescape;
101 u_char escapechar = '~';
102
103 char *speeds[] = {
104         "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
105         "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
106 #define MAX_SPEED_LENGTH        (sizeof("115200") - 1)
107 };
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(long) __dead2;
122 void            done(int) __dead2;
123 void            echo(char);
124 u_int           getescape(char *);
125 void            lostpeer(int);
126 void            mode(int);
127 void            msg(char *);
128 void            oob(int);
129 int             reader(int);
130 void            sendwindow(void);
131 void            setsignal(int);
132 void            sigwinch(int);
133 void            stop(char);
134 void            usage(void) __dead2;
135 void            writer(void);
136 void            writeroob(int);
137
138 #ifdef OLDSUN
139 int             get_window_size(int, struct winsize *);
140 #endif
141
142 int
143 main(argc, argv)
144         int argc;
145         char *argv[];
146 {
147         struct passwd *pw;
148         struct servent *sp;
149         struct sgttyb ttyb;
150         long omask;
151         int argoff, ch, dflag, Dflag, one, uid;
152         char *host, *localname, *p, *user, term[1024];
153 #ifdef KERBEROS
154         char *k;
155 #endif
156         struct sockaddr_storage ss;
157         int sslen;
158
159         argoff = dflag = Dflag = 0;
160         one = 1;
161         host = localname = user = NULL;
162
163         if ((p = strrchr(argv[0], '/')))
164                 ++p;
165         else
166                 p = argv[0];
167
168         if (strcmp(p, "rlogin"))
169                 host = p;
170
171         /* handle "rlogin host flags" */
172         if (!host && argc > 2 && argv[1][0] != '-') {
173                 host = argv[1];
174                 argoff = 1;
175         }
176
177 #ifdef KERBEROS
178 #define OPTIONS "468DEKLde:i:k:l:x"
179 #else
180 #define OPTIONS "468DEKLde:i:l:"
181 #endif
182         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
183                 switch(ch) {
184                 case '4':
185                         family = PF_INET;
186                         break;
187
188                 case '6':
189                         family = PF_INET6;
190                         break;
191
192                 case '8':
193                         eight = 1;
194                         break;
195                 case 'D':
196                         Dflag = 1;
197                         break;
198                 case 'E':
199                         noescape = 1;
200                         break;
201                 case 'K':
202 #ifdef KERBEROS
203                         use_kerberos = 0;
204 #endif
205                         break;
206                 case 'L':
207                         litout = 1;
208                         break;
209                 case 'd':
210                         dflag = 1;
211                         break;
212                 case 'e':
213                         noescape = 0;
214                         escapechar = getescape(optarg);
215                         break;
216                 case 'i':
217                         if (getuid() != 0)
218                                 errx(1, "-i user: permission denied");
219                         localname = optarg;
220                         break;
221 #ifdef KERBEROS
222                 case 'k':
223                         dest_realm = dst_realm_buf;
224                         (void)strncpy(dest_realm, optarg, REALM_SZ);
225                         break;
226 #endif
227                 case 'l':
228                         user = optarg;
229                         break;
230 #ifdef CRYPT
231 #ifdef KERBEROS
232                 case 'x':
233                         doencrypt = 1;
234                         break;
235 #endif
236 #endif
237                 case '?':
238                 default:
239                         usage();
240                 }
241         optind += argoff;
242
243         /* if haven't gotten a host yet, do so */
244         if (!host && !(host = argv[optind++]))
245                 usage();
246
247         if (argv[optind])
248                 usage();
249
250         if (!(pw = getpwuid(uid = getuid())))
251                 errx(1, "unknown user id");
252         if (!user)
253                 user = pw->pw_name;
254         if (!localname)
255                 localname = pw->pw_name;
256
257         sp = NULL;
258 #ifdef KERBEROS
259         k = auth_getval("auth_list");
260         if (k && !strstr(k, "kerberos"))
261             use_kerberos = 0;
262         if (use_kerberos) {
263                 sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
264                 if (sp == NULL) {
265                         use_kerberos = 0;
266                         warn("can't get entry for %s/tcp service",
267                             doencrypt ? "eklogin" : "klogin");
268                 }
269         }
270 #endif
271         if (sp == NULL)
272                 sp = getservbyname("login", "tcp");
273         if (sp == NULL)
274                 errx(1, "login/tcp: unknown service");
275
276 #define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
277
278         (void)strncpy(term, (p = getenv("TERM")) ? p : "network",
279                       MAX_TERM_LENGTH);
280         term[MAX_TERM_LENGTH] = '\0';
281         if (ioctl(0, TIOCGETP, &ttyb) == 0) {
282                 (void)strcat(term, "/");
283                 (void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
284         }
285
286         (void)get_window_size(0, &winsize);
287
288         (void)signal(SIGPIPE, lostpeer);
289         /* will use SIGUSR1 for window size hack, so hold it off */
290         omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
291         /*
292          * We set SIGURG and SIGUSR1 below so that an
293          * incoming signal will be held pending rather than being
294          * discarded. Note that these routines will be ready to get
295          * a signal by the time that they are unblocked below.
296          */
297         (void)signal(SIGURG, copytochild);
298         (void)signal(SIGUSR1, writeroob);
299
300 #ifdef KERBEROS
301         if (use_kerberos) {
302                 setuid(getuid());
303                 rem = KSUCCESS;
304                 errno = 0;
305                 if (dest_realm == NULL)
306                         dest_realm = krb_realmofhost(host);
307
308 #ifdef CRYPT
309                 if (doencrypt) {
310                         rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
311                             dest_realm, &cred, schedule);
312                         des_set_key(&cred.session, schedule);
313                 } else
314 #endif /* CRYPT */
315                         rem = krcmd(&host, sp->s_port, user, term, 0,
316                             dest_realm);
317                 if (rem < 0) {
318                         int i;
319                         char **newargv;
320
321                         sp = getservbyname("login", "tcp");
322                         if (sp == NULL)
323                                 errx(1, "unknown service login/tcp");
324                         if (errno == ECONNREFUSED)
325                                 warn("remote host doesn't support Kerberos");
326                         if (errno == ENOENT)
327                                 warn("can't provide Kerberos auth data");
328                         newargv = malloc((argc + 2) * sizeof(*newargv));
329                         if (newargv == NULL)
330                                 err(1, "malloc");
331                         newargv[0] = argv[0];
332                         newargv[1] = "-K";
333                         for(i = 1; i < argc; ++i)
334                         newargv[i + 1] = argv[i];
335                         newargv[argc + 1] = NULL;
336                         execv(_PATH_RLOGIN, newargv);
337                 }
338         } else {
339 #ifdef CRYPT
340                 if (doencrypt)
341                         errx(1, "the -x flag requires Kerberos authentication");
342 #endif /* CRYPT */
343                 rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
344                               family);
345         }
346 #else
347         rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
348 #endif /* KERBEROS */
349
350         if (rem < 0)
351                 exit(1);
352
353         if (dflag &&
354             setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
355                 warn("setsockopt");
356         if (Dflag &&
357             setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
358                 warn("setsockopt NODELAY (ignored)");
359
360         sslen = sizeof(ss);
361         one = IPTOS_LOWDELAY;
362         if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
363             ss.ss_family == AF_INET) {
364                 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
365                                sizeof(int)) < 0)
366                         warn("setsockopt TOS (ignored)");
367         } else
368                 if (ss.ss_family == AF_INET)
369                         warn("setsockopt getsockname failed");
370
371         (void)setuid(uid);
372         doit(omask);
373         /*NOTREACHED*/
374 }
375
376 int child, defflags, deflflags, tabflag;
377 char deferase, defkill;
378 struct tchars deftc;
379 struct ltchars defltc;
380 struct tchars notc = { -1, -1, -1, -1, -1, -1 };
381 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
382
383 void
384 doit(omask)
385         long omask;
386 {
387         struct sgttyb sb;
388
389         (void)ioctl(0, TIOCGETP, (char *)&sb);
390         defflags = sb.sg_flags;
391         tabflag = defflags & TBDELAY;
392         defflags &= ECHO | CRMOD;
393         deferase = sb.sg_erase;
394         defkill = sb.sg_kill;
395         (void)ioctl(0, TIOCLGET, &deflflags);
396         (void)ioctl(0, TIOCGETC, &deftc);
397         notc.t_startc = deftc.t_startc;
398         notc.t_stopc = deftc.t_stopc;
399         (void)ioctl(0, TIOCGLTC, &defltc);
400         (void)signal(SIGINT, SIG_IGN);
401         setsignal(SIGHUP);
402         setsignal(SIGQUIT);
403         child = fork();
404         if (child == -1) {
405                 warn("fork");
406                 done(1);
407         }
408         if (child == 0) {
409                 mode(1);
410                 if (reader(omask) == 0) {
411                         msg("connection closed.");
412                         exit(0);
413                 }
414                 sleep(1);
415                 msg("\007connection closed.");
416                 exit(1);
417         }
418
419         /*
420          * We may still own the socket, and may have a pending SIGURG (or might
421          * receive one soon) that we really want to send to the reader.  When
422          * one of these comes in, the trap copytochild simply copies such
423          * signals to the child. We can now unblock SIGURG and SIGUSR1
424          * that were set above.
425          */
426         (void)sigsetmask(omask);
427         (void)signal(SIGCHLD, catch_child);
428         writer();
429         msg("closed connection.");
430         done(0);
431 }
432
433 /* trap a signal, unless it is being ignored. */
434 void
435 setsignal(sig)
436         int sig;
437 {
438         int omask = sigblock(sigmask(sig));
439
440         if (signal(sig, exit) == SIG_IGN)
441                 (void)signal(sig, SIG_IGN);
442         (void)sigsetmask(omask);
443 }
444
445 void
446 done(status)
447         int status;
448 {
449         int w, wstatus;
450
451         mode(0);
452         if (child > 0) {
453                 /* make sure catch_child does not snap it up */
454                 (void)signal(SIGCHLD, SIG_DFL);
455                 if (kill(child, SIGKILL) >= 0)
456                         while ((w = wait(&wstatus)) > 0 && w != child);
457         }
458         exit(status);
459 }
460
461 int dosigwinch;
462
463 /*
464  * This is called when the reader process gets the out-of-band (urgent)
465  * request to turn on the window-changing protocol.
466  */
467 void
468 writeroob(signo)
469         int signo;
470 {
471         if (dosigwinch == 0) {
472                 sendwindow();
473                 (void)signal(SIGWINCH, sigwinch);
474         }
475         dosigwinch = 1;
476 }
477
478 void
479 catch_child(signo)
480         int signo;
481 {
482         union wait status;
483         int pid;
484
485         for (;;) {
486                 pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
487                 if (pid == 0)
488                         return;
489                 /* if the child (reader) dies, just quit */
490                 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
491                         done((int)(status.w_termsig | status.w_retcode));
492         }
493         /* NOTREACHED */
494 }
495
496 /*
497  * writer: write to remote: 0 -> line.
498  * ~.                           terminate
499  * ~^Z                          suspend rlogin process.
500  * ~<delayed-suspend char>      suspend rlogin process, but leave reader alone.
501  */
502 void
503 writer()
504 {
505         register int bol, local, n;
506         char c;
507
508         bol = 1;                        /* beginning of line */
509         local = 0;
510         for (;;) {
511                 n = read(STDIN_FILENO, &c, 1);
512                 if (n <= 0) {
513                         if (n < 0 && errno == EINTR)
514                                 continue;
515                         break;
516                 }
517                 /*
518                  * If we're at the beginning of the line and recognize a
519                  * command character, then we echo locally.  Otherwise,
520                  * characters are echo'd remotely.  If the command character
521                  * is doubled, this acts as a force and local echo is
522                  * suppressed.
523                  */
524                 if (bol) {
525                         bol = 0;
526                         if (!noescape && c == escapechar) {
527                                 local = 1;
528                                 continue;
529                         }
530                 } else if (local) {
531                         local = 0;
532                         if (c == '.' || c == deftc.t_eofc) {
533                                 echo(c);
534                                 break;
535                         }
536                         if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
537                                 bol = 1;
538                                 echo(c);
539                                 stop(c);
540                                 continue;
541                         }
542                         if (c != escapechar)
543 #ifdef CRYPT
544 #ifdef KERBEROS
545                                 if (doencrypt)
546                                         (void)des_enc_write(rem,
547                                             (char *)&escapechar, 1,
548                                                 schedule, &cred.session);
549                                 else
550 #endif
551 #endif
552                                         (void)write(rem, &escapechar, 1);
553                 }
554
555 #ifdef CRYPT
556 #ifdef KERBEROS
557                 if (doencrypt) {
558                         if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
559                                 msg("line gone");
560                                 break;
561                         }
562                 } else
563 #endif
564 #endif
565                         if (write(rem, &c, 1) == 0) {
566                                 msg("line gone");
567                                 break;
568                         }
569                 bol = c == defkill || c == deftc.t_eofc ||
570                     c == deftc.t_intrc || c == defltc.t_suspc ||
571                     c == '\r' || c == '\n';
572         }
573 }
574
575 void
576 #if __STDC__
577 echo(register char c)
578 #else
579 echo(c)
580         register char c;
581 #endif
582 {
583         register char *p;
584         char buf[8];
585
586         p = buf;
587         c &= 0177;
588         *p++ = escapechar;
589         if (c < ' ') {
590                 *p++ = '^';
591                 *p++ = c + '@';
592         } else if (c == 0177) {
593                 *p++ = '^';
594                 *p++ = '?';
595         } else
596                 *p++ = c;
597         *p++ = '\r';
598         *p++ = '\n';
599         (void)write(STDOUT_FILENO, buf, p - buf);
600 }
601
602 void
603 #if __STDC__
604 stop(char cmdc)
605 #else
606 stop(cmdc)
607         char cmdc;
608 #endif
609 {
610         mode(0);
611         (void)signal(SIGCHLD, SIG_IGN);
612         (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
613         (void)signal(SIGCHLD, catch_child);
614         mode(1);
615         sigwinch(0);                    /* check for size changes */
616 }
617
618 void
619 sigwinch(signo)
620         int signo;
621 {
622         struct winsize ws;
623
624         if (dosigwinch && get_window_size(0, &ws) == 0 &&
625             bcmp(&ws, &winsize, sizeof(ws))) {
626                 winsize = ws;
627                 sendwindow();
628         }
629 }
630
631 /*
632  * Send the window size to the server via the magic escape
633  */
634 void
635 sendwindow()
636 {
637         struct winsize *wp;
638         char obuf[4 + sizeof (struct winsize)];
639
640         wp = (struct winsize *)(obuf+4);
641         obuf[0] = 0377;
642         obuf[1] = 0377;
643         obuf[2] = 's';
644         obuf[3] = 's';
645         wp->ws_row = htons(winsize.ws_row);
646         wp->ws_col = htons(winsize.ws_col);
647         wp->ws_xpixel = htons(winsize.ws_xpixel);
648         wp->ws_ypixel = htons(winsize.ws_ypixel);
649
650 #ifdef CRYPT
651 #ifdef KERBEROS
652         if(doencrypt)
653                 (void)des_enc_write(rem, obuf, sizeof(obuf),
654                         schedule, &cred.session);
655         else
656 #endif
657 #endif
658                 (void)write(rem, obuf, sizeof(obuf));
659 }
660
661 /*
662  * reader: read from remote: line -> 1
663  */
664 #define READING 1
665 #define WRITING 2
666
667 jmp_buf rcvtop;
668 int ppid, rcvcnt, rcvstate;
669 char rcvbuf[8 * 1024];
670
671 void
672 oob(signo)
673         int signo;
674 {
675         struct sgttyb sb;
676         int atmark, n, out, rcvd;
677         char waste[BUFSIZ], mark;
678
679         out = O_RDWR;
680         rcvd = 0;
681         while (recv(rem, &mark, 1, MSG_OOB) < 0) {
682                 switch (errno) {
683                 case EWOULDBLOCK:
684                         /*
685                          * Urgent data not here yet.  It may not be possible
686                          * to send it yet if we are blocked for output and
687                          * our input buffer is full.
688                          */
689                         if (rcvcnt < sizeof(rcvbuf)) {
690                                 n = read(rem, rcvbuf + rcvcnt,
691                                     sizeof(rcvbuf) - rcvcnt);
692                                 if (n <= 0)
693                                         return;
694                                 rcvd += n;
695                         } else {
696                                 n = read(rem, waste, sizeof(waste));
697                                 if (n <= 0)
698                                         return;
699                         }
700                         continue;
701                 default:
702                         return;
703                 }
704         }
705         if (mark & TIOCPKT_WINDOW) {
706                 /* Let server know about window size changes */
707                 (void)kill(ppid, SIGUSR1);
708         }
709         if (!eight && (mark & TIOCPKT_NOSTOP)) {
710                 (void)ioctl(0, TIOCGETP, (char *)&sb);
711                 sb.sg_flags &= ~CBREAK;
712                 sb.sg_flags |= RAW;
713                 (void)ioctl(0, TIOCSETN, (char *)&sb);
714                 notc.t_stopc = -1;
715                 notc.t_startc = -1;
716                 (void)ioctl(0, TIOCSETC, (char *)&notc);
717         }
718         if (!eight && (mark & TIOCPKT_DOSTOP)) {
719                 (void)ioctl(0, TIOCGETP, (char *)&sb);
720                 sb.sg_flags &= ~RAW;
721                 sb.sg_flags |= CBREAK;
722                 (void)ioctl(0, TIOCSETN, (char *)&sb);
723                 notc.t_stopc = deftc.t_stopc;
724                 notc.t_startc = deftc.t_startc;
725                 (void)ioctl(0, TIOCSETC, (char *)&notc);
726         }
727         if (mark & TIOCPKT_FLUSHWRITE) {
728                 (void)ioctl(1, TIOCFLUSH, (char *)&out);
729                 for (;;) {
730                         if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
731                                 warn("ioctl");
732                                 break;
733                         }
734                         if (atmark)
735                                 break;
736                         n = read(rem, waste, sizeof (waste));
737                         if (n <= 0)
738                                 break;
739                 }
740                 /*
741                  * Don't want any pending data to be output, so clear the recv
742                  * buffer.  If we were hanging on a write when interrupted,
743                  * don't want it to restart.  If we were reading, restart
744                  * anyway.
745                  */
746                 rcvcnt = 0;
747                 longjmp(rcvtop, 1);
748         }
749
750         /* oob does not do FLUSHREAD (alas!) */
751
752         /*
753          * If we filled the receive buffer while a read was pending, longjmp
754          * to the top to restart appropriately.  Don't abort a pending write,
755          * however, or we won't know how much was written.
756          */
757         if (rcvd && rcvstate == READING)
758                 longjmp(rcvtop, 1);
759 }
760
761 /* reader: read from remote: line -> 1 */
762 int
763 reader(omask)
764         int omask;
765 {
766         int pid, n, remaining;
767         char *bufp;
768
769 #if BSD >= 43 || defined(SUNOS4)
770         pid = getpid();         /* modern systems use positives for pid */
771 #else
772         pid = -getpid();        /* old broken systems use negatives */
773 #endif
774         (void)signal(SIGTTOU, SIG_IGN);
775         (void)signal(SIGURG, oob);
776         ppid = getppid();
777         (void)fcntl(rem, F_SETOWN, pid);
778         (void)setjmp(rcvtop);
779         (void)sigsetmask(omask);
780         bufp = rcvbuf;
781         for (;;) {
782                 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
783                         rcvstate = WRITING;
784                         n = write(STDOUT_FILENO, bufp, remaining);
785                         if (n < 0) {
786                                 if (errno != EINTR)
787                                         return (-1);
788                                 continue;
789                         }
790                         bufp += n;
791                 }
792                 bufp = rcvbuf;
793                 rcvcnt = 0;
794                 rcvstate = READING;
795
796 #ifdef CRYPT
797 #ifdef KERBEROS
798                 if (doencrypt)
799                         rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
800                                 schedule, &cred.session);
801                 else
802 #endif
803 #endif
804                         rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
805                 if (rcvcnt == 0)
806                         return (0);
807                 if (rcvcnt < 0) {
808                         if (errno == EINTR)
809                                 continue;
810                         warn("read");
811                         return (-1);
812                 }
813         }
814 }
815
816 void
817 mode(f)
818         int f;
819 {
820         struct ltchars *ltc;
821         struct sgttyb sb;
822         struct tchars *tc;
823         int lflags;
824
825         (void)ioctl(0, TIOCGETP, (char *)&sb);
826         (void)ioctl(0, TIOCLGET, (char *)&lflags);
827         switch(f) {
828         case 0:
829                 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
830                 sb.sg_flags |= defflags|tabflag;
831                 tc = &deftc;
832                 ltc = &defltc;
833                 sb.sg_kill = defkill;
834                 sb.sg_erase = deferase;
835                 lflags = deflflags;
836                 break;
837         case 1:
838                 sb.sg_flags |= (eight ? RAW : CBREAK);
839                 sb.sg_flags &= ~defflags;
840                 /* preserve tab delays, but turn off XTABS */
841                 if ((sb.sg_flags & TBDELAY) == XTABS)
842                         sb.sg_flags &= ~TBDELAY;
843                 tc = &notc;
844                 ltc = &noltc;
845                 sb.sg_kill = sb.sg_erase = -1;
846                 if (litout)
847                         lflags |= LLITOUT;
848                 break;
849         default:
850                 return;
851         }
852         (void)ioctl(0, TIOCSLTC, (char *)ltc);
853         (void)ioctl(0, TIOCSETC, (char *)tc);
854         (void)ioctl(0, TIOCSETN, (char *)&sb);
855         (void)ioctl(0, TIOCLSET, (char *)&lflags);
856 }
857
858 void
859 lostpeer(signo)
860         int signo;
861 {
862         (void)signal(SIGPIPE, SIG_IGN);
863         msg("\007connection closed.");
864         done(1);
865 }
866
867 /* copy SIGURGs to the child process. */
868 void
869 copytochild(signo)
870         int signo;
871 {
872         (void)kill(child, SIGURG);
873 }
874
875 void
876 msg(str)
877         char *str;
878 {
879         (void)fprintf(stderr, "rlogin: %s\r\n", str);
880 }
881
882 void
883 usage()
884 {
885         (void)fprintf(stderr,
886         "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
887 #ifdef KERBEROS
888 #ifdef CRYPT
889             "8DEKLdx", " [-k realm] ");
890 #else
891             "8DEKLd", " [-k realm] ");
892 #endif
893 #else
894             "8DEKLd", " ");
895 #endif
896         exit(1);
897 }
898
899 /*
900  * The following routine provides compatibility (such as it is) between older
901  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
902  */
903 #ifdef OLDSUN
904 int
905 get_window_size(fd, wp)
906         int fd;
907         struct winsize *wp;
908 {
909         struct ttysize ts;
910         int error;
911
912         if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
913                 return (error);
914         wp->ws_row = ts.ts_lines;
915         wp->ws_col = ts.ts_cols;
916         wp->ws_xpixel = 0;
917         wp->ws_ypixel = 0;
918         return (0);
919 }
920 #endif
921
922 u_int
923 getescape(p)
924         register char *p;
925 {
926         long val;
927         int len;
928
929         if ((len = strlen(p)) == 1)     /* use any single char, including '\' */
930                 return ((u_int)*p);
931                                         /* otherwise, \nnn */
932         if (*p == '\\' && len >= 2 && len <= 4) {
933                 val = strtol(++p, NULL, 8);
934                 for (;;) {
935                         if (!*++p)
936                                 return ((u_int)val);
937                         if (*p < '0' || *p > '8')
938                                 break;
939                 }
940         }
941         msg("illegal option value -- e");
942         usage();
943         /* NOTREACHED */
944 }