]>
git.saurik.com Git - apple/network_cmds.git/blob - rlogin.tproj/rlogin.c
c03229f42b064c74bef140fe24275ffcb0fbdd01
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1983, 1990, 1993
27 * The Regents of the University of California. All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * rlogin - remote login
62 #include <sys/param.h>
63 #include <sys/socket.h>
65 #include <sys/resource.h>
67 #include <sys/ioctl.h>
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/ip.h>
92 #include <kerberosIV/des.h>
93 #include <kerberosIV/krb.h>
98 Key_schedule schedule
;
99 int use_kerberos
= 1, doencrypt
;
100 char dst_realm_buf
[REALM_SZ
], *dest_realm
= NULL
;
103 #ifndef TIOCPKT_WINDOW
104 #define TIOCPKT_WINDOW 0x80
107 /* concession to Sun */
112 int eight
, litout
, rem
;
115 u_char escapechar
= '~';
119 unsigned short ws_row
, ws_col
;
120 unsigned short ws_xpixel
, ws_ypixel
;
123 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
125 struct winsize winsize
;
127 void catch_child
__P((int));
128 void copytochild
__P((int));
129 __dead
void doit
__P((sigset_t
*));
130 __dead
void done
__P((int));
131 void echo
__P((char));
132 u_int getescape
__P((char *));
133 static void do_exit
__P((int));
134 void lostpeer
__P((int));
135 void mode
__P((int));
136 void msg
__P((char *));
138 int reader
__P((sigset_t
*));
139 void sendwindow
__P((void));
140 void setsignal
__P((int));
141 int speed
__P((int));
142 void sigwinch
__P((int));
143 void stop
__P((char));
144 __dead
void usage
__P((void));
145 void writer
__P((void));
146 void writeroob
__P((int));
149 void warning
__P((const char *, ...));
152 int get_window_size
__P((int, struct winsize
*));
164 int argoff
, ch
, dflag
, one
;
165 char *host
, *p
, *user
, term
[1024];
172 if (p
= strrchr(argv
[0], '/'))
177 if (strcmp(p
, "rlogin") != 0)
180 /* handle "rlogin host flags" */
181 if (!host
&& argc
> 2 && argv
[1][0] != '-') {
187 #define OPTIONS "8EKLde:k:l:x"
189 #define OPTIONS "8EKLde:l:"
191 while ((ch
= getopt(argc
- argoff
, argv
+ argoff
, OPTIONS
)) != EOF
)
212 escapechar
= getescape(optarg
);
216 dest_realm
= dst_realm_buf
;
217 (void)strncpy(dest_realm
, optarg
, REALM_SZ
);
227 des_set_key(cred
.session
, schedule
);
239 /* if haven't gotten a host yet, do so */
240 if (!host
&& !(host
= *argv
++))
246 if (!(pw
= getpwuid(uid
= getuid())))
247 errx(1, "unknown user id.");
248 /* Accept user1@host format, though "-l user2" overrides user1 */
249 p
= strchr(host
, '@');
252 if (!user
&& p
> host
)
264 sp
= getservbyname((doencrypt
? "eklogin" : "klogin"), "tcp");
267 warning("can't get entry for %s/tcp service",
268 doencrypt
? "eklogin" : "klogin");
273 sp
= getservbyname("login", "tcp");
275 errx(1, "login/tcp: unknown service.");
277 (void)snprintf(term
, sizeof(term
), "%s/%d",
278 ((p
= getenv("TERM")) ? p
: "network"),
281 (void)get_window_size(0, &winsize
);
283 sigemptyset(&sa
.sa_mask
);
284 sa
.sa_flags
= SA_RESTART
;
285 sa
.sa_handler
= lostpeer
;
286 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
287 /* will use SIGUSR1 for window size hack, so hold it off */
289 sigaddset(&smask
, SIGURG
);
290 sigaddset(&smask
, SIGUSR1
);
291 (void)sigprocmask(SIG_SETMASK
, &smask
, &smask
);
293 * We set SIGURG and SIGUSR1 below so that an
294 * incoming signal will be held pending rather than being
295 * discarded. Note that these routines will be ready to get
296 * a signal by the time that they are unblocked below.
298 sa
.sa_handler
= copytochild
;
299 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
300 sa
.sa_handler
= writeroob
;
301 (void)sigaction(SIGUSR1
, &sa
, (struct sigaction
*) 0);
308 /* Fully qualify hostname (needed for krb_realmofhost). */
309 hp
= gethostbyname(host
);
310 if (hp
!= NULL
&& !(host
= strdup(hp
->h_name
)))
311 errx(1, "%s", strerror(ENOMEM
));
315 if (dest_realm
== NULL
)
316 dest_realm
= krb_realmofhost(host
);
320 rem
= krcmd_mutual(&host
, sp
->s_port
, user
, term
, 0,
321 dest_realm
, &cred
, schedule
);
324 rem
= krcmd(&host
, sp
->s_port
, user
, term
, 0,
328 sp
= getservbyname("login", "tcp");
330 errx(1, "unknown service login/tcp.");
331 if (errno
== ECONNREFUSED
)
332 warning("remote host doesn't support Kerberos");
334 warning("can't provide Kerberos auth data");
340 errx(1, "the -x flag requires Kerberos authentication.");
342 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
345 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
346 #endif /* KERBEROS */
352 setsockopt(rem
, SOL_SOCKET
, SO_DEBUG
, &one
, sizeof(one
)) < 0)
353 warn("setsockopt DEBUG (ignored)");
354 one
= IPTOS_LOWDELAY
;
355 if (setsockopt(rem
, IPPROTO_IP
, IP_TOS
, (char *)&one
, sizeof(int)) < 0)
356 warn("setsockopt TOS (ignored)");
370 (void)tcgetattr(fd
, &tt
);
372 return ((int) cfgetispeed(&tt
));
375 int speeds
[] = { /* for older systems, B0 .. EXTB */
378 600, 1200, 1800, 2400,
379 4800, 9600, 19200, 38400
388 (void)tcgetattr(fd
, &tt
);
390 return (speeds
[(int)cfgetispeed(&tt
)]);
395 struct termios deftt
;
405 for (i
= 0; i
< NCCS
; i
++)
406 nott
.c_cc
[i
] = _POSIX_VDISABLE
;
407 tcgetattr(0, &deftt
);
408 nott
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
409 nott
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
410 sigemptyset(&sa
.sa_mask
);
411 sa
.sa_flags
= SA_RESTART
;
412 sa
.sa_handler
= SIG_IGN
;
413 (void)sigaction(SIGINT
, &sa
, (struct sigaction
*) 0);
423 if (reader(smask
) == 0) {
424 msg("connection closed.");
428 msg("\007connection closed.");
433 * We may still own the socket, and may have a pending SIGURG (or might
434 * receive one soon) that we really want to send to the reader. When
435 * one of these comes in, the trap copytochild simply copies such
436 * signals to the child. We can now unblock SIGURG and SIGUSR1
437 * that were set above.
439 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
440 sa
.sa_handler
= catch_child
;
441 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
443 msg("closed connection.");
447 /* trap a signal, unless it is being ignored. */
456 sigaddset(&sigs
, sig
);
457 sigprocmask(SIG_BLOCK
, &sigs
, &sigs
);
459 sigemptyset(&sa
.sa_mask
);
460 sa
.sa_handler
= do_exit
;
461 sa
.sa_flags
= SA_RESTART
;
462 (void)sigaction(sig
, &sa
, &sa
);
463 if (sa
.sa_handler
== SIG_IGN
)
464 (void)sigaction(sig
, &sa
, (struct sigaction
*) 0);
466 (void)sigprocmask(SIG_SETMASK
, &sigs
, (sigset_t
*) 0);
479 /* make sure catch_child does not snap it up */
480 sigemptyset(&sa
.sa_mask
);
481 sa
.sa_handler
= SIG_DFL
;
483 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
484 if (kill(child
, SIGKILL
) >= 0)
485 while ((w
= wait(&wstatus
)) > 0 && w
!= child
)
494 * This is called when the reader process gets the out-of-band (urgent)
495 * request to turn on the window-changing protocol.
503 if (dosigwinch
== 0) {
505 sigemptyset(&sa
.sa_mask
);
506 sa
.sa_handler
= sigwinch
;
507 sa
.sa_flags
= SA_RESTART
;
508 (void)sigaction(SIGWINCH
, &sa
, (struct sigaction
*) 0);
521 pid
= waitpid(-1, &status
, WNOHANG
|WUNTRACED
);
524 /* if the child (reader) dies, just quit */
525 if (pid
< 0 || (pid
== child
&& !WIFSTOPPED(status
)))
526 done(WEXITSTATUS(status
) | WTERMSIG(status
));
532 * writer: write to remote: 0 -> line.
534 * ~^Z suspend rlogin process.
535 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
540 register int bol
, local
, n
;
543 bol
= 1; /* beginning of line */
546 n
= read(STDIN_FILENO
, &c
, 1);
548 if (n
< 0 && errno
== EINTR
)
553 * If we're at the beginning of the line and recognize a
554 * command character, then we echo locally. Otherwise,
555 * characters are echo'd remotely. If the command character
556 * is doubled, this acts as a force and local echo is
561 if (!noescape
&& c
== escapechar
) {
567 if (c
== '.' || c
== deftt
.c_cc
[VEOF
]) {
571 if (c
== deftt
.c_cc
[VSUSP
] || c
== deftt
.c_cc
[VDSUSP
]) {
582 (char *)&escapechar
, 1);
586 (void)write(rem
, &escapechar
, 1);
592 if (des_write(rem
, &c
, 1) == 0) {
599 if (write(rem
, &c
, 1) == 0) {
603 bol
= c
== deftt
.c_cc
[VKILL
] || c
== deftt
.c_cc
[VEOF
] ||
604 c
== deftt
.c_cc
[VINTR
] || c
== deftt
.c_cc
[VSUSP
] ||
605 c
== '\r' || c
== '\n';
611 echo(register char c
)
626 } else if (c
== 0177) {
633 (void)write(STDOUT_FILENO
, buf
, p
- buf
);
647 sigemptyset(&sa
.sa_mask
);
648 sa
.sa_handler
= SIG_IGN
;
649 sa
.sa_flags
= SA_RESTART
;
650 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
651 (void)kill(cmdc
== deftt
.c_cc
[VSUSP
] ? 0 : getpid(), SIGTSTP
);
652 sa
.sa_handler
= catch_child
;
653 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
655 sigwinch(0); /* check for size changes */
664 if (dosigwinch
&& get_window_size(0, &ws
) == 0 &&
665 memcmp(&ws
, &winsize
, sizeof(ws
))) {
672 * Send the window size to the server via the magic escape
678 char obuf
[4 + sizeof (struct winsize
)];
680 wp
= (struct winsize
*)(obuf
+4);
685 wp
->ws_row
= htons(winsize
.ws_row
);
686 wp
->ws_col
= htons(winsize
.ws_col
);
687 wp
->ws_xpixel
= htons(winsize
.ws_xpixel
);
688 wp
->ws_ypixel
= htons(winsize
.ws_ypixel
);
693 (void)des_write(rem
, obuf
, sizeof(obuf
));
697 (void)write(rem
, obuf
, sizeof(obuf
));
701 * reader: read from remote: line -> 1
708 int rcvcnt
, rcvstate
;
709 char rcvbuf
[8 * 1024];
716 int atmark
, n
, out
, rcvd
;
717 char waste
[BUFSIZ
], mark
;
721 while (recv(rem
, &mark
, 1, MSG_OOB
) < 0) {
725 * Urgent data not here yet. It may not be possible
726 * to send it yet if we are blocked for output and
727 * our input buffer is full.
729 if (rcvcnt
< sizeof(rcvbuf
)) {
730 n
= read(rem
, rcvbuf
+ rcvcnt
,
731 sizeof(rcvbuf
) - rcvcnt
);
736 n
= read(rem
, waste
, sizeof(waste
));
745 if (mark
& TIOCPKT_WINDOW
) {
746 /* Let server know about window size changes */
747 (void)kill(ppid
, SIGUSR1
);
749 if (!eight
&& (mark
& TIOCPKT_NOSTOP
)) {
751 tt
.c_iflag
&= ~(IXON
| IXOFF
);
752 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
753 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
754 tcsetattr(0, TCSANOW
, &tt
);
756 if (!eight
&& (mark
& TIOCPKT_DOSTOP
)) {
758 tt
.c_iflag
|= (IXON
|IXOFF
);
759 tt
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
760 tt
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
761 tcsetattr(0, TCSANOW
, &tt
);
763 if (mark
& TIOCPKT_FLUSHWRITE
) {
764 (void)ioctl(1, TIOCFLUSH
, (char *)&out
);
766 if (ioctl(rem
, SIOCATMARK
, &atmark
) < 0) {
767 warn("ioctl SIOCATMARK (ignored)");
772 n
= read(rem
, waste
, sizeof (waste
));
777 * Don't want any pending data to be output, so clear the recv
778 * buffer. If we were hanging on a write when interrupted,
779 * don't want it to restart. If we were reading, restart
786 /* oob does not do FLUSHREAD (alas!) */
789 * If we filled the receive buffer while a read was pending, longjmp
790 * to the top to restart appropriately. Don't abort a pending write,
791 * however, or we won't know how much was written.
793 if (rcvd
&& rcvstate
== READING
)
797 /* reader: read from remote: line -> 1 */
807 #if BSD >= 43 || defined(SUNOS4)
808 pid
= getpid(); /* modern systems use positives for pid */
810 pid
= -getpid(); /* old broken systems use negatives */
812 sigemptyset(&sa
.sa_mask
);
813 sa
.sa_flags
= SA_RESTART
;
814 sa
.sa_handler
= SIG_IGN
;
815 (void)sigaction(SIGTTOU
, &sa
, (struct sigaction
*) 0);
817 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
819 (void)fcntl(rem
, F_SETOWN
, pid
);
820 (void)setjmp(rcvtop
);
821 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
824 while ((remaining
= rcvcnt
- (bufp
- rcvbuf
)) > 0) {
826 n
= write(STDOUT_FILENO
, bufp
, remaining
);
841 rcvcnt
= des_read(rem
, rcvbuf
, sizeof(rcvbuf
));
845 rcvcnt
= read(rem
, rcvbuf
, sizeof (rcvbuf
));
865 tcsetattr(0, TCSADRAIN
, &deftt
);
869 tt
.c_oflag
&= ~(OPOST
);
870 tt
.c_lflag
&= ~(ECHO
| ICANON
| IEXTEN
| ISIG
);
871 tt
.c_iflag
&= ~(ICRNL
);
875 tt
.c_iflag
&= ~(IXON
| IXOFF
| ISTRIP
);
876 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
877 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
881 tcsetattr(0, TCSADRAIN
, &tt
);
895 sigemptyset(&sa
.sa_mask
);
896 sa
.sa_flags
= SA_RESTART
;
897 sa
.sa_handler
= SIG_IGN
;
898 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
899 msg("\007connection closed.");
903 /* copy SIGURGs to the child process. */
909 (void)kill(child
, SIGURG
);
912 static void do_exit(int signo
)
922 (void)fprintf(stderr
, "rlogin: %s\r\n", str
);
929 warning(const char *fmt
, ...)
931 warning(fmt
, va_alist
)
938 (void)fprintf(stderr
, "rlogin: warning, using standard rlogin: ");
944 vfprintf(stderr
, fmt
, ap
);
946 (void)fprintf(stderr
, ".\n");
953 (void)fprintf(stderr
,
954 "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
957 "8EKLx", " [-k realm] ");
959 "8EKL", " [-k realm] ");
968 * The following routine provides compatibility (such as it is) between older
969 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
973 get_window_size(fd
, wp
)
980 if ((error
= ioctl(0, TIOCGSIZE
, &ts
)) != 0)
982 wp
->ws_row
= ts
.ts_lines
;
983 wp
->ws_col
= ts
.ts_cols
;
997 if ((len
= strlen(p
)) == 1) /* use any single char, including '\' */
999 /* otherwise, \nnn */
1000 if (*p
== '\\' && len
>= 2 && len
<= 4) {
1001 val
= strtol(++p
, NULL
, 8);
1004 return ((u_int
)val
);
1005 if (*p
< '0' || *p
> '8')
1009 msg("illegal option value -- e");