a9f35503e092735122186ae72cd923f47369d3e0
[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.30 2002/04/28 11:16:43 markm Exp $
43  * $DragonFly: src/usr.bin/rlogin/rlogin.c,v 1.7 2005/02/15 01:24:49 cpressey 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
75 #ifdef KERBEROS
76 #include <openssl/des.h>
77 #include <krb.h>
78
79 #include "krb.h"
80
81 CREDENTIALS cred;
82 Key_schedule schedule;
83 int use_kerberos = 1, doencrypt;
84 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
85 #endif
86
87 #ifndef TIOCPKT_WINDOW
88 #define TIOCPKT_WINDOW  0x80
89 #endif
90
91 /* concession to Sun */
92 #ifndef SIGUSR1
93 #define SIGUSR1 30
94 #endif
95
96 int eight, litout, rem;
97 int family = PF_UNSPEC;
98
99 int noescape;
100 u_char escapechar = '~';
101
102 const char *speeds[] = {
103         "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
104         "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
105 #define MAX_SPEED_LENGTH        (sizeof("115200") - 1)
106 };
107
108 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
109 struct  winsize winsize;
110
111 void            catch_child(int);
112 void            copytochild(int);
113 void            doit(long) __dead2;
114 void            done(int) __dead2;
115 void            echo(char);
116 u_int           getescape(char *);
117 void            lostpeer(int);
118 void            mode(int);
119 void            msg(const char *);
120 void            oob(int);
121 int             reader(int);
122 void            sendwindow(void);
123 void            setsignal(int);
124 void            sigwinch(int);
125 void            stop(char);
126 void            usage(void) __dead2;
127 void            writer(void);
128 void            writeroob(int);
129
130 int
131 main(int argc, char *argv[])
132 {
133         struct passwd *pw;
134         struct servent *sp;
135         struct sgttyb ttyb;
136         long omask;
137         int argoff, ch, dflag, Dflag, one, uid;
138         char *host, *localname, *p, *user, term[1024];
139 #ifdef KERBEROS
140         char *k;
141 #endif
142         struct sockaddr_storage ss;
143         int sslen;
144
145         argoff = dflag = Dflag = 0;
146         one = 1;
147         host = localname = user = NULL;
148
149         if ((p = strrchr(argv[0], '/')))
150                 ++p;
151         else
152                 p = argv[0];
153
154         if (strcmp(p, "rlogin"))
155                 host = p;
156
157         /* handle "rlogin host flags" */
158         if (!host && argc > 2 && argv[1][0] != '-') {
159                 host = argv[1];
160                 argoff = 1;
161         }
162
163 #ifdef KERBEROS
164 #define OPTIONS "468DEKLde:i:k:l:x"
165 #else
166 #define OPTIONS "468DEKLde:i:l:"
167 #endif
168         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
169                 switch(ch) {
170                 case '4':
171                         family = PF_INET;
172                         break;
173
174                 case '6':
175                         family = PF_INET6;
176                         break;
177
178                 case '8':
179                         eight = 1;
180                         break;
181                 case 'D':
182                         Dflag = 1;
183                         break;
184                 case 'E':
185                         noescape = 1;
186                         break;
187                 case 'K':
188 #ifdef KERBEROS
189                         use_kerberos = 0;
190 #endif
191                         break;
192                 case 'L':
193                         litout = 1;
194                         break;
195                 case 'd':
196                         dflag = 1;
197                         break;
198                 case 'e':
199                         noescape = 0;
200                         escapechar = getescape(optarg);
201                         break;
202                 case 'i':
203                         if (getuid() != 0)
204                                 errx(1, "-i user: permission denied");
205                         localname = optarg;
206                         break;
207 #ifdef KERBEROS
208                 case 'k':
209                         dest_realm = dst_realm_buf;
210                         (void)strncpy(dest_realm, optarg, REALM_SZ);
211                         break;
212 #endif
213                 case 'l':
214                         user = optarg;
215                         break;
216 #ifdef CRYPT
217 #ifdef KERBEROS
218                 case 'x':
219                         doencrypt = 1;
220                         break;
221 #endif
222 #endif
223                 case '?':
224                 default:
225                         usage();
226                 }
227         optind += argoff;
228
229         /* if haven't gotten a host yet, do so */
230         if (!host && !(host = argv[optind++]))
231                 usage();
232
233         if (argv[optind])
234                 usage();
235
236         if (!(pw = getpwuid(uid = getuid())))
237                 errx(1, "unknown user id");
238         if (!user)
239                 user = pw->pw_name;
240         if (!localname)
241                 localname = pw->pw_name;
242
243         sp = NULL;
244 #ifdef KERBEROS
245         k = auth_getval("auth_list");
246         if (k && !strstr(k, "kerberos"))
247             use_kerberos = 0;
248         if (use_kerberos) {
249                 sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
250                 if (sp == NULL) {
251                         use_kerberos = 0;
252                         warn("can't get entry for %s/tcp service",
253                             doencrypt ? "eklogin" : "klogin");
254                 }
255         }
256 #endif
257         if (sp == NULL)
258                 sp = getservbyname("login", "tcp");
259         if (sp == NULL)
260                 errx(1, "login/tcp: unknown service");
261
262 #define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
263
264         (void)strncpy(term, (p = getenv("TERM")) ? p : "network",
265                       MAX_TERM_LENGTH);
266         term[MAX_TERM_LENGTH] = '\0';
267         if (ioctl(0, TIOCGETP, &ttyb) == 0) {
268                 (void)strcat(term, "/");
269                 (void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
270         }
271
272         (void)get_window_size(0, &winsize);
273
274         (void)signal(SIGPIPE, lostpeer);
275         /* will use SIGUSR1 for window size hack, so hold it off */
276         omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
277         /*
278          * We set SIGURG and SIGUSR1 below so that an
279          * incoming signal will be held pending rather than being
280          * discarded. Note that these routines will be ready to get
281          * a signal by the time that they are unblocked below.
282          */
283         (void)signal(SIGURG, copytochild);
284         (void)signal(SIGUSR1, writeroob);
285
286 #ifdef KERBEROS
287         if (use_kerberos) {
288                 setuid(getuid());
289                 rem = KSUCCESS;
290                 errno = 0;
291                 if (dest_realm == NULL)
292                         dest_realm = krb_realmofhost(host);
293
294 #ifdef CRYPT
295                 if (doencrypt) {
296                         rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
297                             dest_realm, &cred, schedule);
298                         des_set_key(&cred.session, schedule);
299                 } else
300 #endif /* CRYPT */
301                         rem = krcmd(&host, sp->s_port, user, term, 0,
302                             dest_realm);
303                 if (rem < 0) {
304                         int i;
305                         char **newargv;
306
307                         sp = getservbyname("login", "tcp");
308                         if (sp == NULL)
309                                 errx(1, "unknown service login/tcp");
310                         if (errno == ECONNREFUSED)
311                                 warn("remote host doesn't support Kerberos");
312                         if (errno == ENOENT)
313                                 warn("can't provide Kerberos auth data");
314                         newargv = malloc((argc + 2) * sizeof(*newargv));
315                         if (newargv == NULL)
316                                 err(1, "malloc");
317                         newargv[0] = argv[0];
318                         newargv[1] = "-K";
319                         for(i = 1; i < argc; ++i)
320                         newargv[i + 1] = argv[i];
321                         newargv[argc + 1] = NULL;
322                         execv(_PATH_RLOGIN, newargv);
323                 }
324         } else {
325 #ifdef CRYPT
326                 if (doencrypt)
327                         errx(1, "the -x flag requires Kerberos authentication");
328 #endif /* CRYPT */
329                 rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
330                               family);
331         }
332 #else
333         rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
334 #endif /* KERBEROS */
335
336         if (rem < 0)
337                 exit(1);
338
339         if (dflag &&
340             setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
341                 warn("setsockopt");
342         if (Dflag &&
343             setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
344                 warn("setsockopt NODELAY (ignored)");
345
346         sslen = sizeof(ss);
347         one = IPTOS_LOWDELAY;
348         if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
349             ss.ss_family == AF_INET) {
350                 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
351                                sizeof(int)) < 0)
352                         warn("setsockopt TOS (ignored)");
353         } else
354                 if (ss.ss_family == AF_INET)
355                         warn("setsockopt getsockname failed");
356
357         (void)setuid(uid);
358         doit(omask);
359         /*NOTREACHED*/
360 }
361
362 int child, defflags, deflflags, tabflag;
363 char deferase, defkill;
364 struct tchars deftc;
365 struct ltchars defltc;
366 struct tchars notc = { -1, -1, -1, -1, -1, -1 };
367 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
368
369 void
370 doit(long omask)
371 {
372         struct sgttyb sb;
373
374         (void)ioctl(0, TIOCGETP, (char *)&sb);
375         defflags = sb.sg_flags;
376         tabflag = defflags & TBDELAY;
377         defflags &= ECHO | CRMOD;
378         deferase = sb.sg_erase;
379         defkill = sb.sg_kill;
380         (void)ioctl(0, TIOCLGET, &deflflags);
381         (void)ioctl(0, TIOCGETC, &deftc);
382         notc.t_startc = deftc.t_startc;
383         notc.t_stopc = deftc.t_stopc;
384         (void)ioctl(0, TIOCGLTC, &defltc);
385         (void)signal(SIGINT, SIG_IGN);
386         setsignal(SIGHUP);
387         setsignal(SIGQUIT);
388         child = fork();
389         if (child == -1) {
390                 warn("fork");
391                 done(1);
392         }
393         if (child == 0) {
394                 mode(1);
395                 if (reader(omask) == 0) {
396                         msg("connection closed.");
397                         exit(0);
398                 }
399                 sleep(1);
400                 msg("\007connection closed.");
401                 exit(1);
402         }
403
404         /*
405          * We may still own the socket, and may have a pending SIGURG (or might
406          * receive one soon) that we really want to send to the reader.  When
407          * one of these comes in, the trap copytochild simply copies such
408          * signals to the child. We can now unblock SIGURG and SIGUSR1
409          * that were set above.
410          */
411         (void)sigsetmask(omask);
412         (void)signal(SIGCHLD, catch_child);
413         writer();
414         msg("closed connection.");
415         done(0);
416 }
417
418 /* trap a signal, unless it is being ignored. */
419 void
420 setsignal(int sig)
421 {
422         int omask = sigblock(sigmask(sig));
423
424         if (signal(sig, exit) == SIG_IGN)
425                 (void)signal(sig, SIG_IGN);
426         (void)sigsetmask(omask);
427 }
428
429 void
430 done(int status)
431 {
432         int w, wstatus;
433
434         mode(0);
435         if (child > 0) {
436                 /* make sure catch_child does not snap it up */
437                 (void)signal(SIGCHLD, SIG_DFL);
438                 if (kill(child, SIGKILL) >= 0)
439                         while ((w = wait(&wstatus)) > 0 && w != child);
440         }
441         exit(status);
442 }
443
444 int dosigwinch;
445
446 /*
447  * This is called when the reader process gets the out-of-band (urgent)
448  * request to turn on the window-changing protocol.
449  */
450 void
451 writeroob(int signo __unused)
452 {
453         if (dosigwinch == 0) {
454                 sendwindow();
455                 (void)signal(SIGWINCH, sigwinch);
456         }
457         dosigwinch = 1;
458 }
459
460 void
461 catch_child(int signo __unused)
462 {
463         union wait status;
464         int pid;
465
466         for (;;) {
467                 pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
468                 if (pid == 0)
469                         return;
470                 /* if the child (reader) dies, just quit */
471                 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
472                         done((int)(status.w_termsig | status.w_retcode));
473         }
474         /* NOTREACHED */
475 }
476
477 /*
478  * writer: write to remote: 0 -> line.
479  * ~.                           terminate
480  * ~^Z                          suspend rlogin process.
481  * ~<delayed-suspend char>      suspend rlogin process, but leave reader alone.
482  */
483 void
484 writer(void)
485 {
486         int bol, local, n;
487         char c;
488
489         bol = 1;                        /* beginning of line */
490         local = 0;
491         for (;;) {
492                 n = read(STDIN_FILENO, &c, 1);
493                 if (n <= 0) {
494                         if (n < 0 && errno == EINTR)
495                                 continue;
496                         break;
497                 }
498                 /*
499                  * If we're at the beginning of the line and recognize a
500                  * command character, then we echo locally.  Otherwise,
501                  * characters are echo'd remotely.  If the command character
502                  * is doubled, this acts as a force and local echo is
503                  * suppressed.
504                  */
505                 if (bol) {
506                         bol = 0;
507                         if (!noescape && c == escapechar) {
508                                 local = 1;
509                                 continue;
510                         }
511                 } else if (local) {
512                         local = 0;
513                         if (c == '.' || c == deftc.t_eofc) {
514                                 echo(c);
515                                 break;
516                         }
517                         if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
518                                 bol = 1;
519                                 echo(c);
520                                 stop(c);
521                                 continue;
522                         }
523                         if (c != escapechar)
524 #ifdef CRYPT
525 #ifdef KERBEROS
526                                 if (doencrypt)
527                                         (void)des_enc_write(rem,
528                                             (char *)&escapechar, 1,
529                                                 schedule, &cred.session);
530                                 else
531 #endif
532 #endif
533                                         (void)write(rem, &escapechar, 1);
534                 }
535
536 #ifdef CRYPT
537 #ifdef KERBEROS
538                 if (doencrypt) {
539                         if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
540                                 msg("line gone");
541                                 break;
542                         }
543                 } else
544 #endif
545 #endif
546                         if (write(rem, &c, 1) == 0) {
547                                 msg("line gone");
548                                 break;
549                         }
550                 bol = c == defkill || c == deftc.t_eofc ||
551                     c == deftc.t_intrc || c == defltc.t_suspc ||
552                     c == '\r' || c == '\n';
553         }
554 }
555
556 void
557 echo(char c)
558 {
559         char *p;
560         char buf[8];
561
562         p = buf;
563         c &= 0177;
564         *p++ = escapechar;
565         if (c < ' ') {
566                 *p++ = '^';
567                 *p++ = c + '@';
568         } else if (c == 0177) {
569                 *p++ = '^';
570                 *p++ = '?';
571         } else
572                 *p++ = c;
573         *p++ = '\r';
574         *p++ = '\n';
575         (void)write(STDOUT_FILENO, buf, p - buf);
576 }
577
578 void
579 stop(char cmdc)
580 {
581         mode(0);
582         (void)signal(SIGCHLD, SIG_IGN);
583         (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
584         (void)signal(SIGCHLD, catch_child);
585         mode(1);
586         sigwinch(0);                    /* check for size changes */
587 }
588
589 void
590 sigwinch(int signo __unused)
591 {
592         struct winsize ws;
593
594         if (dosigwinch && get_window_size(0, &ws) == 0 &&
595             bcmp(&ws, &winsize, sizeof(ws))) {
596                 winsize = ws;
597                 sendwindow();
598         }
599 }
600
601 /*
602  * Send the window size to the server via the magic escape
603  */
604 void
605 sendwindow(void)
606 {
607         struct winsize *wp;
608         char obuf[4 + sizeof (struct winsize)];
609
610         wp = (struct winsize *)(obuf+4);
611         obuf[0] = 0377;
612         obuf[1] = 0377;
613         obuf[2] = 's';
614         obuf[3] = 's';
615         wp->ws_row = htons(winsize.ws_row);
616         wp->ws_col = htons(winsize.ws_col);
617         wp->ws_xpixel = htons(winsize.ws_xpixel);
618         wp->ws_ypixel = htons(winsize.ws_ypixel);
619
620 #ifdef CRYPT
621 #ifdef KERBEROS
622         if(doencrypt)
623                 (void)des_enc_write(rem, obuf, sizeof(obuf),
624                         schedule, &cred.session);
625         else
626 #endif
627 #endif
628                 (void)write(rem, obuf, sizeof(obuf));
629 }
630
631 /*
632  * reader: read from remote: line -> 1
633  */
634 #define READING 1
635 #define WRITING 2
636
637 jmp_buf rcvtop;
638 int ppid, rcvcnt, rcvstate;
639 char rcvbuf[8 * 1024];
640
641 void
642 oob(int signo __unused)
643 {
644         struct sgttyb sb;
645         int atmark, n, out, rcvd;
646         char waste[BUFSIZ], mark;
647
648         out = O_RDWR;
649         rcvd = 0;
650         while (recv(rem, &mark, 1, MSG_OOB) < 0) {
651                 switch (errno) {
652                 case EWOULDBLOCK:
653                         /*
654                          * Urgent data not here yet.  It may not be possible
655                          * to send it yet if we are blocked for output and
656                          * our input buffer is full.
657                          */
658                         if (rcvcnt < (int)sizeof(rcvbuf)) {
659                                 n = read(rem, rcvbuf + rcvcnt,
660                                     sizeof(rcvbuf) - rcvcnt);
661                                 if (n <= 0)
662                                         return;
663                                 rcvd += n;
664                         } else {
665                                 n = read(rem, waste, sizeof(waste));
666                                 if (n <= 0)
667                                         return;
668                         }
669                         continue;
670                 default:
671                         return;
672                 }
673         }
674         if (mark & TIOCPKT_WINDOW) {
675                 /* Let server know about window size changes */
676                 (void)kill(ppid, SIGUSR1);
677         }
678         if (!eight && (mark & TIOCPKT_NOSTOP)) {
679                 (void)ioctl(0, TIOCGETP, (char *)&sb);
680                 sb.sg_flags &= ~CBREAK;
681                 sb.sg_flags |= RAW;
682                 (void)ioctl(0, TIOCSETN, (char *)&sb);
683                 notc.t_stopc = -1;
684                 notc.t_startc = -1;
685                 (void)ioctl(0, TIOCSETC, (char *)&notc);
686         }
687         if (!eight && (mark & TIOCPKT_DOSTOP)) {
688                 (void)ioctl(0, TIOCGETP, (char *)&sb);
689                 sb.sg_flags &= ~RAW;
690                 sb.sg_flags |= CBREAK;
691                 (void)ioctl(0, TIOCSETN, (char *)&sb);
692                 notc.t_stopc = deftc.t_stopc;
693                 notc.t_startc = deftc.t_startc;
694                 (void)ioctl(0, TIOCSETC, (char *)&notc);
695         }
696         if (mark & TIOCPKT_FLUSHWRITE) {
697                 (void)ioctl(1, TIOCFLUSH, (char *)&out);
698                 for (;;) {
699                         if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
700                                 warn("ioctl");
701                                 break;
702                         }
703                         if (atmark)
704                                 break;
705                         n = read(rem, waste, sizeof (waste));
706                         if (n <= 0)
707                                 break;
708                 }
709                 /*
710                  * Don't want any pending data to be output, so clear the recv
711                  * buffer.  If we were hanging on a write when interrupted,
712                  * don't want it to restart.  If we were reading, restart
713                  * anyway.
714                  */
715                 rcvcnt = 0;
716                 longjmp(rcvtop, 1);
717         }
718
719         /* oob does not do FLUSHREAD (alas!) */
720
721         /*
722          * If we filled the receive buffer while a read was pending, longjmp
723          * to the top to restart appropriately.  Don't abort a pending write,
724          * however, or we won't know how much was written.
725          */
726         if (rcvd && rcvstate == READING)
727                 longjmp(rcvtop, 1);
728 }
729
730 /* reader: read from remote: line -> 1 */
731 int
732 reader(int omask)
733 {
734         int pid, n, remaining;
735         char *bufp;
736
737 #if BSD >= 43 || defined(SUNOS4)
738         pid = getpid();         /* modern systems use positives for pid */
739 #else
740         pid = -getpid();        /* old broken systems use negatives */
741 #endif
742         (void)signal(SIGTTOU, SIG_IGN);
743         (void)signal(SIGURG, oob);
744         (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */
745         ppid = getppid();
746         (void)fcntl(rem, F_SETOWN, pid);
747         (void)setjmp(rcvtop);
748         (void)sigsetmask(omask);
749         bufp = rcvbuf;
750         for (;;) {
751                 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
752                         rcvstate = WRITING;
753                         n = write(STDOUT_FILENO, bufp, remaining);
754                         if (n < 0) {
755                                 if (errno != EINTR)
756                                         return (-1);
757                                 continue;
758                         }
759                         bufp += n;
760                 }
761                 bufp = rcvbuf;
762                 rcvcnt = 0;
763                 rcvstate = READING;
764
765 #ifdef CRYPT
766 #ifdef KERBEROS
767                 if (doencrypt)
768                         rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
769                                 schedule, &cred.session);
770                 else
771 #endif
772 #endif
773                         rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
774                 if (rcvcnt == 0)
775                         return (0);
776                 if (rcvcnt < 0) {
777                         if (errno == EINTR)
778                                 continue;
779                         warn("read");
780                         return (-1);
781                 }
782         }
783 }
784
785 void
786 mode(int f)
787 {
788         struct ltchars *ltc;
789         struct sgttyb sb;
790         struct tchars *tc;
791         int lflags;
792
793         (void)ioctl(0, TIOCGETP, (char *)&sb);
794         (void)ioctl(0, TIOCLGET, (char *)&lflags);
795         switch(f) {
796         case 0:
797                 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
798                 sb.sg_flags |= defflags|tabflag;
799                 tc = &deftc;
800                 ltc = &defltc;
801                 sb.sg_kill = defkill;
802                 sb.sg_erase = deferase;
803                 lflags = deflflags;
804                 break;
805         case 1:
806                 sb.sg_flags |= (eight ? RAW : CBREAK);
807                 sb.sg_flags &= ~defflags;
808                 /* preserve tab delays, but turn off XTABS */
809                 if ((sb.sg_flags & TBDELAY) == XTABS)
810                         sb.sg_flags &= ~TBDELAY;
811                 tc = &notc;
812                 ltc = &noltc;
813                 sb.sg_kill = sb.sg_erase = -1;
814                 if (litout)
815                         lflags |= LLITOUT;
816                 break;
817         default:
818                 return;
819         }
820         (void)ioctl(0, TIOCSLTC, (char *)ltc);
821         (void)ioctl(0, TIOCSETC, (char *)tc);
822         (void)ioctl(0, TIOCSETN, (char *)&sb);
823         (void)ioctl(0, TIOCLSET, (char *)&lflags);
824 }
825
826 void
827 lostpeer(int signo __unused)
828 {
829         (void)signal(SIGPIPE, SIG_IGN);
830         msg("\007connection closed.");
831         done(1);
832 }
833
834 /* copy SIGURGs to the child process via SIGUSR1. */
835 void
836 copytochild(int signo __unused)
837 {
838         (void)kill(child, SIGUSR1);
839 }
840
841 void
842 msg(const char *str)
843 {
844         (void)fprintf(stderr, "rlogin: %s\r\n", str);
845 }
846
847 void
848 usage(void)
849 {
850         (void)fprintf(stderr,
851         "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
852 #ifdef KERBEROS
853 #ifdef CRYPT
854             "8DEKLdx", " [-k realm] ");
855 #else
856             "8DEKLd", " [-k realm] ");
857 #endif
858 #else
859             "8DEKLd", " ");
860 #endif
861         exit(1);
862 }
863
864 u_int
865 getescape(char *p)
866 {
867         long val;
868         int len;
869
870         if ((len = strlen(p)) == 1)     /* use any single char, including '\' */
871                 return ((u_int)*p);
872                                         /* otherwise, \nnn */
873         if (*p == '\\' && len >= 2 && len <= 4) {
874                 val = strtol(++p, NULL, 8);
875                 for (;;) {
876                         if (!*++p)
877                                 return ((u_int)val);
878                         if (*p < '0' || *p > '8')
879                                 break;
880                 }
881         }
882         msg("illegal option value -- e");
883         usage();
884         /* NOTREACHED */
885 }