]>
git.saurik.com Git - apple/network_cmds.git/blob - rlogin.tproj/rlogin.c
1767ea2c11ad3977ceacf209ee41ddfeafec80c5
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1983, 1990, 1993
25 * The Regents of the University of California. All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * rlogin - remote login
60 #include <sys/param.h>
61 #include <sys/socket.h>
63 #include <sys/resource.h>
65 #include <sys/ioctl.h>
67 #include <netinet/in.h>
68 #include <netinet/in_systm.h>
69 #include <netinet/ip.h>
90 #include <kerberosIV/des.h>
91 #include <kerberosIV/krb.h>
96 Key_schedule schedule
;
97 int use_kerberos
= 1, doencrypt
;
98 char dst_realm_buf
[REALM_SZ
], *dest_realm
= NULL
;
101 #ifndef TIOCPKT_WINDOW
102 #define TIOCPKT_WINDOW 0x80
105 /* concession to Sun */
110 int eight
, litout
, rem
;
113 u_char escapechar
= '~';
117 unsigned short ws_row
, ws_col
;
118 unsigned short ws_xpixel
, ws_ypixel
;
121 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
123 struct winsize winsize
;
125 void catch_child
__P((int));
126 void copytochild
__P((int));
127 __dead
void doit
__P((sigset_t
*));
128 __dead
void done
__P((int));
129 void echo
__P((char));
130 u_int getescape
__P((char *));
131 static void do_exit
__P((int));
132 void lostpeer
__P((int));
133 void mode
__P((int));
134 void msg
__P((char *));
136 int reader
__P((sigset_t
*));
137 void sendwindow
__P((void));
138 void setsignal
__P((int));
139 int speed
__P((int));
140 void sigwinch
__P((int));
141 void stop
__P((char));
142 __dead
void usage
__P((void));
143 void writer
__P((void));
144 void writeroob
__P((int));
147 void warning
__P((const char *, ...));
150 int get_window_size
__P((int, struct winsize
*));
162 int argoff
, ch
, dflag
, one
;
163 char *host
, *p
, *user
, term
[1024];
170 if (p
= strrchr(argv
[0], '/'))
175 if (strcmp(p
, "rlogin") != 0)
178 /* handle "rlogin host flags" */
179 if (!host
&& argc
> 2 && argv
[1][0] != '-') {
185 #define OPTIONS "8EKLde:k:l:x"
187 #define OPTIONS "8EKLde:l:"
189 while ((ch
= getopt(argc
- argoff
, argv
+ argoff
, OPTIONS
)) != EOF
)
210 escapechar
= getescape(optarg
);
214 dest_realm
= dst_realm_buf
;
215 (void)strncpy(dest_realm
, optarg
, REALM_SZ
);
225 des_set_key(cred
.session
, schedule
);
237 /* if haven't gotten a host yet, do so */
238 if (!host
&& !(host
= *argv
++))
244 if (!(pw
= getpwuid(uid
= getuid())))
245 errx(1, "unknown user id.");
246 /* Accept user1@host format, though "-l user2" overrides user1 */
247 p
= strchr(host
, '@');
250 if (!user
&& p
> host
)
262 sp
= getservbyname((doencrypt
? "eklogin" : "klogin"), "tcp");
265 warning("can't get entry for %s/tcp service",
266 doencrypt
? "eklogin" : "klogin");
271 sp
= getservbyname("login", "tcp");
273 errx(1, "login/tcp: unknown service.");
275 (void)snprintf(term
, sizeof(term
), "%s/%d",
276 ((p
= getenv("TERM")) ? p
: "network"),
279 (void)get_window_size(0, &winsize
);
281 sigemptyset(&sa
.sa_mask
);
282 sa
.sa_flags
= SA_RESTART
;
283 sa
.sa_handler
= lostpeer
;
284 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
285 /* will use SIGUSR1 for window size hack, so hold it off */
287 sigaddset(&smask
, SIGURG
);
288 sigaddset(&smask
, SIGUSR1
);
289 (void)sigprocmask(SIG_SETMASK
, &smask
, &smask
);
291 * We set SIGURG and SIGUSR1 below so that an
292 * incoming signal will be held pending rather than being
293 * discarded. Note that these routines will be ready to get
294 * a signal by the time that they are unblocked below.
296 sa
.sa_handler
= copytochild
;
297 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
298 sa
.sa_handler
= writeroob
;
299 (void)sigaction(SIGUSR1
, &sa
, (struct sigaction
*) 0);
306 /* Fully qualify hostname (needed for krb_realmofhost). */
307 hp
= gethostbyname(host
);
308 if (hp
!= NULL
&& !(host
= strdup(hp
->h_name
)))
309 errx(1, "%s", strerror(ENOMEM
));
313 if (dest_realm
== NULL
)
314 dest_realm
= krb_realmofhost(host
);
318 rem
= krcmd_mutual(&host
, sp
->s_port
, user
, term
, 0,
319 dest_realm
, &cred
, schedule
);
322 rem
= krcmd(&host
, sp
->s_port
, user
, term
, 0,
326 sp
= getservbyname("login", "tcp");
328 errx(1, "unknown service login/tcp.");
329 if (errno
== ECONNREFUSED
)
330 warning("remote host doesn't support Kerberos");
332 warning("can't provide Kerberos auth data");
338 errx(1, "the -x flag requires Kerberos authentication.");
340 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
343 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
344 #endif /* KERBEROS */
350 setsockopt(rem
, SOL_SOCKET
, SO_DEBUG
, &one
, sizeof(one
)) < 0)
351 warn("setsockopt DEBUG (ignored)");
352 one
= IPTOS_LOWDELAY
;
353 if (setsockopt(rem
, IPPROTO_IP
, IP_TOS
, (char *)&one
, sizeof(int)) < 0)
354 warn("setsockopt TOS (ignored)");
368 (void)tcgetattr(fd
, &tt
);
370 return ((int) cfgetispeed(&tt
));
373 int speeds
[] = { /* for older systems, B0 .. EXTB */
376 600, 1200, 1800, 2400,
377 4800, 9600, 19200, 38400
386 (void)tcgetattr(fd
, &tt
);
388 return (speeds
[(int)cfgetispeed(&tt
)]);
393 struct termios deftt
;
403 for (i
= 0; i
< NCCS
; i
++)
404 nott
.c_cc
[i
] = _POSIX_VDISABLE
;
405 tcgetattr(0, &deftt
);
406 nott
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
407 nott
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
408 sigemptyset(&sa
.sa_mask
);
409 sa
.sa_flags
= SA_RESTART
;
410 sa
.sa_handler
= SIG_IGN
;
411 (void)sigaction(SIGINT
, &sa
, (struct sigaction
*) 0);
421 if (reader(smask
) == 0) {
422 msg("connection closed.");
426 msg("\007connection closed.");
431 * We may still own the socket, and may have a pending SIGURG (or might
432 * receive one soon) that we really want to send to the reader. When
433 * one of these comes in, the trap copytochild simply copies such
434 * signals to the child. We can now unblock SIGURG and SIGUSR1
435 * that were set above.
437 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
438 sa
.sa_handler
= catch_child
;
439 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
441 msg("closed connection.");
445 /* trap a signal, unless it is being ignored. */
454 sigaddset(&sigs
, sig
);
455 sigprocmask(SIG_BLOCK
, &sigs
, &sigs
);
457 sigemptyset(&sa
.sa_mask
);
458 sa
.sa_handler
= do_exit
;
459 sa
.sa_flags
= SA_RESTART
;
460 (void)sigaction(sig
, &sa
, &sa
);
461 if (sa
.sa_handler
== SIG_IGN
)
462 (void)sigaction(sig
, &sa
, (struct sigaction
*) 0);
464 (void)sigprocmask(SIG_SETMASK
, &sigs
, (sigset_t
*) 0);
477 /* make sure catch_child does not snap it up */
478 sigemptyset(&sa
.sa_mask
);
479 sa
.sa_handler
= SIG_DFL
;
481 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
482 if (kill(child
, SIGKILL
) >= 0)
483 while ((w
= wait(&wstatus
)) > 0 && w
!= child
)
492 * This is called when the reader process gets the out-of-band (urgent)
493 * request to turn on the window-changing protocol.
501 if (dosigwinch
== 0) {
503 sigemptyset(&sa
.sa_mask
);
504 sa
.sa_handler
= sigwinch
;
505 sa
.sa_flags
= SA_RESTART
;
506 (void)sigaction(SIGWINCH
, &sa
, (struct sigaction
*) 0);
519 pid
= waitpid(-1, &status
, WNOHANG
|WUNTRACED
);
522 /* if the child (reader) dies, just quit */
523 if (pid
< 0 || (pid
== child
&& !WIFSTOPPED(status
)))
524 done(WEXITSTATUS(status
) | WTERMSIG(status
));
530 * writer: write to remote: 0 -> line.
532 * ~^Z suspend rlogin process.
533 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
538 register int bol
, local
, n
;
541 bol
= 1; /* beginning of line */
544 n
= read(STDIN_FILENO
, &c
, 1);
546 if (n
< 0 && errno
== EINTR
)
551 * If we're at the beginning of the line and recognize a
552 * command character, then we echo locally. Otherwise,
553 * characters are echo'd remotely. If the command character
554 * is doubled, this acts as a force and local echo is
559 if (!noescape
&& c
== escapechar
) {
565 if (c
== '.' || c
== deftt
.c_cc
[VEOF
]) {
569 if (c
== deftt
.c_cc
[VSUSP
] || c
== deftt
.c_cc
[VDSUSP
]) {
580 (char *)&escapechar
, 1);
584 (void)write(rem
, &escapechar
, 1);
590 if (des_write(rem
, &c
, 1) == 0) {
597 if (write(rem
, &c
, 1) == 0) {
601 bol
= c
== deftt
.c_cc
[VKILL
] || c
== deftt
.c_cc
[VEOF
] ||
602 c
== deftt
.c_cc
[VINTR
] || c
== deftt
.c_cc
[VSUSP
] ||
603 c
== '\r' || c
== '\n';
609 echo(register char c
)
624 } else if (c
== 0177) {
631 (void)write(STDOUT_FILENO
, buf
, p
- buf
);
645 sigemptyset(&sa
.sa_mask
);
646 sa
.sa_handler
= SIG_IGN
;
647 sa
.sa_flags
= SA_RESTART
;
648 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
649 (void)kill(cmdc
== deftt
.c_cc
[VSUSP
] ? 0 : getpid(), SIGTSTP
);
650 sa
.sa_handler
= catch_child
;
651 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
653 sigwinch(0); /* check for size changes */
662 if (dosigwinch
&& get_window_size(0, &ws
) == 0 &&
663 memcmp(&ws
, &winsize
, sizeof(ws
))) {
670 * Send the window size to the server via the magic escape
676 char obuf
[4 + sizeof (struct winsize
)];
678 wp
= (struct winsize
*)(obuf
+4);
683 wp
->ws_row
= htons(winsize
.ws_row
);
684 wp
->ws_col
= htons(winsize
.ws_col
);
685 wp
->ws_xpixel
= htons(winsize
.ws_xpixel
);
686 wp
->ws_ypixel
= htons(winsize
.ws_ypixel
);
691 (void)des_write(rem
, obuf
, sizeof(obuf
));
695 (void)write(rem
, obuf
, sizeof(obuf
));
699 * reader: read from remote: line -> 1
706 int rcvcnt
, rcvstate
;
707 char rcvbuf
[8 * 1024];
714 int atmark
, n
, out
, rcvd
;
715 char waste
[BUFSIZ
], mark
;
719 while (recv(rem
, &mark
, 1, MSG_OOB
) < 0) {
723 * Urgent data not here yet. It may not be possible
724 * to send it yet if we are blocked for output and
725 * our input buffer is full.
727 if (rcvcnt
< sizeof(rcvbuf
)) {
728 n
= read(rem
, rcvbuf
+ rcvcnt
,
729 sizeof(rcvbuf
) - rcvcnt
);
734 n
= read(rem
, waste
, sizeof(waste
));
743 if (mark
& TIOCPKT_WINDOW
) {
744 /* Let server know about window size changes */
745 (void)kill(ppid
, SIGUSR1
);
747 if (!eight
&& (mark
& TIOCPKT_NOSTOP
)) {
749 tt
.c_iflag
&= ~(IXON
| IXOFF
);
750 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
751 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
752 tcsetattr(0, TCSANOW
, &tt
);
754 if (!eight
&& (mark
& TIOCPKT_DOSTOP
)) {
756 tt
.c_iflag
|= (IXON
|IXOFF
);
757 tt
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
758 tt
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
759 tcsetattr(0, TCSANOW
, &tt
);
761 if (mark
& TIOCPKT_FLUSHWRITE
) {
762 (void)ioctl(1, TIOCFLUSH
, (char *)&out
);
764 if (ioctl(rem
, SIOCATMARK
, &atmark
) < 0) {
765 warn("ioctl SIOCATMARK (ignored)");
770 n
= read(rem
, waste
, sizeof (waste
));
775 * Don't want any pending data to be output, so clear the recv
776 * buffer. If we were hanging on a write when interrupted,
777 * don't want it to restart. If we were reading, restart
784 /* oob does not do FLUSHREAD (alas!) */
787 * If we filled the receive buffer while a read was pending, longjmp
788 * to the top to restart appropriately. Don't abort a pending write,
789 * however, or we won't know how much was written.
791 if (rcvd
&& rcvstate
== READING
)
795 /* reader: read from remote: line -> 1 */
805 #if BSD >= 43 || defined(SUNOS4)
806 pid
= getpid(); /* modern systems use positives for pid */
808 pid
= -getpid(); /* old broken systems use negatives */
810 sigemptyset(&sa
.sa_mask
);
811 sa
.sa_flags
= SA_RESTART
;
812 sa
.sa_handler
= SIG_IGN
;
813 (void)sigaction(SIGTTOU
, &sa
, (struct sigaction
*) 0);
815 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
817 (void)fcntl(rem
, F_SETOWN
, pid
);
818 (void)setjmp(rcvtop
);
819 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
822 while ((remaining
= rcvcnt
- (bufp
- rcvbuf
)) > 0) {
824 n
= write(STDOUT_FILENO
, bufp
, remaining
);
839 rcvcnt
= des_read(rem
, rcvbuf
, sizeof(rcvbuf
));
843 rcvcnt
= read(rem
, rcvbuf
, sizeof (rcvbuf
));
863 tcsetattr(0, TCSADRAIN
, &deftt
);
867 tt
.c_oflag
&= ~(OPOST
);
868 tt
.c_lflag
&= ~(ECHO
| ICANON
| IEXTEN
| ISIG
);
869 tt
.c_iflag
&= ~(ICRNL
);
873 tt
.c_iflag
&= ~(IXON
| IXOFF
| ISTRIP
);
874 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
875 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
879 tcsetattr(0, TCSADRAIN
, &tt
);
893 sigemptyset(&sa
.sa_mask
);
894 sa
.sa_flags
= SA_RESTART
;
895 sa
.sa_handler
= SIG_IGN
;
896 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
897 msg("\007connection closed.");
901 /* copy SIGURGs to the child process. */
907 (void)kill(child
, SIGURG
);
910 static void do_exit(int signo
)
920 (void)fprintf(stderr
, "rlogin: %s\r\n", str
);
927 warning(const char *fmt
, ...)
929 warning(fmt
, va_alist
)
936 (void)fprintf(stderr
, "rlogin: warning, using standard rlogin: ");
942 vfprintf(stderr
, fmt
, ap
);
944 (void)fprintf(stderr
, ".\n");
951 (void)fprintf(stderr
,
952 "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
955 "8EKLx", " [-k realm] ");
957 "8EKL", " [-k realm] ");
966 * The following routine provides compatibility (such as it is) between older
967 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
971 get_window_size(fd
, wp
)
978 if ((error
= ioctl(0, TIOCGSIZE
, &ts
)) != 0)
980 wp
->ws_row
= ts
.ts_lines
;
981 wp
->ws_col
= ts
.ts_cols
;
995 if ((len
= strlen(p
)) == 1) /* use any single char, including '\' */
997 /* otherwise, \nnn */
998 if (*p
== '\\' && len
>= 2 && len
<= 4) {
999 val
= strtol(++p
, NULL
, 8);
1002 return ((u_int
)val
);
1003 if (*p
< '0' || *p
> '8')
1007 msg("illegal option value -- e");