]>
git.saurik.com Git - apple/network_cmds.git/blob - rlogin.tproj/rlogin.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1983, 1990, 1993
26 * The Regents of the University of California. All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * rlogin - remote login
61 #include <sys/param.h>
62 #include <sys/socket.h>
64 #include <sys/resource.h>
66 #include <sys/ioctl.h>
68 #include <netinet/in.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/ip.h>
91 #include <kerberosIV/des.h>
92 #include <kerberosIV/krb.h>
97 Key_schedule schedule
;
98 int use_kerberos
= 1, doencrypt
;
99 char dst_realm_buf
[REALM_SZ
], *dest_realm
= NULL
;
102 #ifndef TIOCPKT_WINDOW
103 #define TIOCPKT_WINDOW 0x80
106 /* concession to Sun */
111 int eight
, litout
, rem
;
114 u_char escapechar
= '~';
118 unsigned short ws_row
, ws_col
;
119 unsigned short ws_xpixel
, ws_ypixel
;
122 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
124 struct winsize winsize
;
126 void catch_child
__P((int));
127 void copytochild
__P((int));
128 __dead
void doit
__P((sigset_t
*));
129 __dead
void done
__P((int));
130 void echo
__P((char));
131 u_int getescape
__P((char *));
132 static void do_exit
__P((int));
133 void lostpeer
__P((int));
134 void mode
__P((int));
135 void msg
__P((char *));
137 int reader
__P((sigset_t
*));
138 void sendwindow
__P((void));
139 void setsignal
__P((int));
140 int speed
__P((int));
141 void sigwinch
__P((int));
142 void stop
__P((char));
143 __dead
void usage
__P((void));
144 void writer
__P((void));
145 void writeroob
__P((int));
148 void warning
__P((const char *, ...));
151 int get_window_size
__P((int, struct winsize
*));
163 int argoff
, ch
, dflag
, one
;
164 char *host
, *p
, *user
, term
[1024];
171 if (p
= strrchr(argv
[0], '/'))
176 if (strcmp(p
, "rlogin") != 0)
179 /* handle "rlogin host flags" */
180 if (!host
&& argc
> 2 && argv
[1][0] != '-') {
186 #define OPTIONS "8EKLde:k:l:x"
188 #define OPTIONS "8EKLde:l:"
190 while ((ch
= getopt(argc
- argoff
, argv
+ argoff
, OPTIONS
)) != EOF
)
211 escapechar
= getescape(optarg
);
215 dest_realm
= dst_realm_buf
;
216 (void)strncpy(dest_realm
, optarg
, REALM_SZ
);
226 des_set_key(cred
.session
, schedule
);
238 /* if haven't gotten a host yet, do so */
239 if (!host
&& !(host
= *argv
++))
245 if (!(pw
= getpwuid(uid
= getuid())))
246 errx(1, "unknown user id.");
247 /* Accept user1@host format, though "-l user2" overrides user1 */
248 p
= strchr(host
, '@');
251 if (!user
&& p
> host
)
263 sp
= getservbyname((doencrypt
? "eklogin" : "klogin"), "tcp");
266 warning("can't get entry for %s/tcp service",
267 doencrypt
? "eklogin" : "klogin");
272 sp
= getservbyname("login", "tcp");
274 errx(1, "login/tcp: unknown service.");
276 (void)snprintf(term
, sizeof(term
), "%s/%d",
277 ((p
= getenv("TERM")) ? p
: "network"),
280 (void)get_window_size(0, &winsize
);
282 sigemptyset(&sa
.sa_mask
);
283 sa
.sa_flags
= SA_RESTART
;
284 sa
.sa_handler
= lostpeer
;
285 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
286 /* will use SIGUSR1 for window size hack, so hold it off */
288 sigaddset(&smask
, SIGURG
);
289 sigaddset(&smask
, SIGUSR1
);
290 (void)sigprocmask(SIG_SETMASK
, &smask
, &smask
);
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.
297 sa
.sa_handler
= copytochild
;
298 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
299 sa
.sa_handler
= writeroob
;
300 (void)sigaction(SIGUSR1
, &sa
, (struct sigaction
*) 0);
307 /* Fully qualify hostname (needed for krb_realmofhost). */
308 hp
= gethostbyname(host
);
309 if (hp
!= NULL
&& !(host
= strdup(hp
->h_name
)))
310 errx(1, "%s", strerror(ENOMEM
));
314 if (dest_realm
== NULL
)
315 dest_realm
= krb_realmofhost(host
);
319 rem
= krcmd_mutual(&host
, sp
->s_port
, user
, term
, 0,
320 dest_realm
, &cred
, schedule
);
323 rem
= krcmd(&host
, sp
->s_port
, user
, term
, 0,
327 sp
= getservbyname("login", "tcp");
329 errx(1, "unknown service login/tcp.");
330 if (errno
== ECONNREFUSED
)
331 warning("remote host doesn't support Kerberos");
333 warning("can't provide Kerberos auth data");
339 errx(1, "the -x flag requires Kerberos authentication.");
341 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
344 rem
= rcmd(&host
, sp
->s_port
, pw
->pw_name
, user
, term
, 0);
345 #endif /* KERBEROS */
351 setsockopt(rem
, SOL_SOCKET
, SO_DEBUG
, &one
, sizeof(one
)) < 0)
352 warn("setsockopt DEBUG (ignored)");
353 one
= IPTOS_LOWDELAY
;
354 if (setsockopt(rem
, IPPROTO_IP
, IP_TOS
, (char *)&one
, sizeof(int)) < 0)
355 warn("setsockopt TOS (ignored)");
369 (void)tcgetattr(fd
, &tt
);
371 return ((int) cfgetispeed(&tt
));
374 int speeds
[] = { /* for older systems, B0 .. EXTB */
377 600, 1200, 1800, 2400,
378 4800, 9600, 19200, 38400
387 (void)tcgetattr(fd
, &tt
);
389 return (speeds
[(int)cfgetispeed(&tt
)]);
394 struct termios deftt
;
404 for (i
= 0; i
< NCCS
; i
++)
405 nott
.c_cc
[i
] = _POSIX_VDISABLE
;
406 tcgetattr(0, &deftt
);
407 nott
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
408 nott
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
409 sigemptyset(&sa
.sa_mask
);
410 sa
.sa_flags
= SA_RESTART
;
411 sa
.sa_handler
= SIG_IGN
;
412 (void)sigaction(SIGINT
, &sa
, (struct sigaction
*) 0);
422 if (reader(smask
) == 0) {
423 msg("connection closed.");
427 msg("\007connection closed.");
432 * We may still own the socket, and may have a pending SIGURG (or might
433 * receive one soon) that we really want to send to the reader. When
434 * one of these comes in, the trap copytochild simply copies such
435 * signals to the child. We can now unblock SIGURG and SIGUSR1
436 * that were set above.
438 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
439 sa
.sa_handler
= catch_child
;
440 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
442 msg("closed connection.");
446 /* trap a signal, unless it is being ignored. */
455 sigaddset(&sigs
, sig
);
456 sigprocmask(SIG_BLOCK
, &sigs
, &sigs
);
458 sigemptyset(&sa
.sa_mask
);
459 sa
.sa_handler
= do_exit
;
460 sa
.sa_flags
= SA_RESTART
;
461 (void)sigaction(sig
, &sa
, &sa
);
462 if (sa
.sa_handler
== SIG_IGN
)
463 (void)sigaction(sig
, &sa
, (struct sigaction
*) 0);
465 (void)sigprocmask(SIG_SETMASK
, &sigs
, (sigset_t
*) 0);
478 /* make sure catch_child does not snap it up */
479 sigemptyset(&sa
.sa_mask
);
480 sa
.sa_handler
= SIG_DFL
;
482 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
483 if (kill(child
, SIGKILL
) >= 0)
484 while ((w
= wait(&wstatus
)) > 0 && w
!= child
)
493 * This is called when the reader process gets the out-of-band (urgent)
494 * request to turn on the window-changing protocol.
502 if (dosigwinch
== 0) {
504 sigemptyset(&sa
.sa_mask
);
505 sa
.sa_handler
= sigwinch
;
506 sa
.sa_flags
= SA_RESTART
;
507 (void)sigaction(SIGWINCH
, &sa
, (struct sigaction
*) 0);
520 pid
= waitpid(-1, &status
, WNOHANG
|WUNTRACED
);
523 /* if the child (reader) dies, just quit */
524 if (pid
< 0 || (pid
== child
&& !WIFSTOPPED(status
)))
525 done(WEXITSTATUS(status
) | WTERMSIG(status
));
531 * writer: write to remote: 0 -> line.
533 * ~^Z suspend rlogin process.
534 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
539 register int bol
, local
, n
;
542 bol
= 1; /* beginning of line */
545 n
= read(STDIN_FILENO
, &c
, 1);
547 if (n
< 0 && errno
== EINTR
)
552 * If we're at the beginning of the line and recognize a
553 * command character, then we echo locally. Otherwise,
554 * characters are echo'd remotely. If the command character
555 * is doubled, this acts as a force and local echo is
560 if (!noescape
&& c
== escapechar
) {
566 if (c
== '.' || c
== deftt
.c_cc
[VEOF
]) {
570 if (c
== deftt
.c_cc
[VSUSP
] || c
== deftt
.c_cc
[VDSUSP
]) {
581 (char *)&escapechar
, 1);
585 (void)write(rem
, &escapechar
, 1);
591 if (des_write(rem
, &c
, 1) == 0) {
598 if (write(rem
, &c
, 1) == 0) {
602 bol
= c
== deftt
.c_cc
[VKILL
] || c
== deftt
.c_cc
[VEOF
] ||
603 c
== deftt
.c_cc
[VINTR
] || c
== deftt
.c_cc
[VSUSP
] ||
604 c
== '\r' || c
== '\n';
610 echo(register char c
)
625 } else if (c
== 0177) {
632 (void)write(STDOUT_FILENO
, buf
, p
- buf
);
646 sigemptyset(&sa
.sa_mask
);
647 sa
.sa_handler
= SIG_IGN
;
648 sa
.sa_flags
= SA_RESTART
;
649 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
650 (void)kill(cmdc
== deftt
.c_cc
[VSUSP
] ? 0 : getpid(), SIGTSTP
);
651 sa
.sa_handler
= catch_child
;
652 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
654 sigwinch(0); /* check for size changes */
663 if (dosigwinch
&& get_window_size(0, &ws
) == 0 &&
664 memcmp(&ws
, &winsize
, sizeof(ws
))) {
671 * Send the window size to the server via the magic escape
677 char obuf
[4 + sizeof (struct winsize
)];
679 wp
= (struct winsize
*)(obuf
+4);
684 wp
->ws_row
= htons(winsize
.ws_row
);
685 wp
->ws_col
= htons(winsize
.ws_col
);
686 wp
->ws_xpixel
= htons(winsize
.ws_xpixel
);
687 wp
->ws_ypixel
= htons(winsize
.ws_ypixel
);
692 (void)des_write(rem
, obuf
, sizeof(obuf
));
696 (void)write(rem
, obuf
, sizeof(obuf
));
700 * reader: read from remote: line -> 1
707 int rcvcnt
, rcvstate
;
708 char rcvbuf
[8 * 1024];
715 int atmark
, n
, out
, rcvd
;
716 char waste
[BUFSIZ
], mark
;
720 while (recv(rem
, &mark
, 1, MSG_OOB
) < 0) {
724 * Urgent data not here yet. It may not be possible
725 * to send it yet if we are blocked for output and
726 * our input buffer is full.
728 if (rcvcnt
< sizeof(rcvbuf
)) {
729 n
= read(rem
, rcvbuf
+ rcvcnt
,
730 sizeof(rcvbuf
) - rcvcnt
);
735 n
= read(rem
, waste
, sizeof(waste
));
744 if (mark
& TIOCPKT_WINDOW
) {
745 /* Let server know about window size changes */
746 (void)kill(ppid
, SIGUSR1
);
748 if (!eight
&& (mark
& TIOCPKT_NOSTOP
)) {
750 tt
.c_iflag
&= ~(IXON
| IXOFF
);
751 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
752 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
753 tcsetattr(0, TCSANOW
, &tt
);
755 if (!eight
&& (mark
& TIOCPKT_DOSTOP
)) {
757 tt
.c_iflag
|= (IXON
|IXOFF
);
758 tt
.c_cc
[VSTOP
] = deftt
.c_cc
[VSTOP
];
759 tt
.c_cc
[VSTART
] = deftt
.c_cc
[VSTART
];
760 tcsetattr(0, TCSANOW
, &tt
);
762 if (mark
& TIOCPKT_FLUSHWRITE
) {
763 (void)ioctl(1, TIOCFLUSH
, (char *)&out
);
765 if (ioctl(rem
, SIOCATMARK
, &atmark
) < 0) {
766 warn("ioctl SIOCATMARK (ignored)");
771 n
= read(rem
, waste
, sizeof (waste
));
776 * Don't want any pending data to be output, so clear the recv
777 * buffer. If we were hanging on a write when interrupted,
778 * don't want it to restart. If we were reading, restart
785 /* oob does not do FLUSHREAD (alas!) */
788 * If we filled the receive buffer while a read was pending, longjmp
789 * to the top to restart appropriately. Don't abort a pending write,
790 * however, or we won't know how much was written.
792 if (rcvd
&& rcvstate
== READING
)
796 /* reader: read from remote: line -> 1 */
806 #if BSD >= 43 || defined(SUNOS4)
807 pid
= getpid(); /* modern systems use positives for pid */
809 pid
= -getpid(); /* old broken systems use negatives */
811 sigemptyset(&sa
.sa_mask
);
812 sa
.sa_flags
= SA_RESTART
;
813 sa
.sa_handler
= SIG_IGN
;
814 (void)sigaction(SIGTTOU
, &sa
, (struct sigaction
*) 0);
816 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
818 (void)fcntl(rem
, F_SETOWN
, pid
);
819 (void)setjmp(rcvtop
);
820 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
823 while ((remaining
= rcvcnt
- (bufp
- rcvbuf
)) > 0) {
825 n
= write(STDOUT_FILENO
, bufp
, remaining
);
840 rcvcnt
= des_read(rem
, rcvbuf
, sizeof(rcvbuf
));
844 rcvcnt
= read(rem
, rcvbuf
, sizeof (rcvbuf
));
864 tcsetattr(0, TCSADRAIN
, &deftt
);
868 tt
.c_oflag
&= ~(OPOST
);
869 tt
.c_lflag
&= ~(ECHO
| ICANON
| IEXTEN
| ISIG
);
870 tt
.c_iflag
&= ~(ICRNL
);
874 tt
.c_iflag
&= ~(IXON
| IXOFF
| ISTRIP
);
875 tt
.c_cc
[VSTOP
] = _POSIX_VDISABLE
;
876 tt
.c_cc
[VSTART
] = _POSIX_VDISABLE
;
880 tcsetattr(0, TCSADRAIN
, &tt
);
894 sigemptyset(&sa
.sa_mask
);
895 sa
.sa_flags
= SA_RESTART
;
896 sa
.sa_handler
= SIG_IGN
;
897 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*) 0);
898 msg("\007connection closed.");
902 /* copy SIGURGs to the child process. */
908 (void)kill(child
, SIGURG
);
911 static void do_exit(int signo
)
921 (void)fprintf(stderr
, "rlogin: %s\r\n", str
);
928 warning(const char *fmt
, ...)
930 warning(fmt
, va_alist
)
937 (void)fprintf(stderr
, "rlogin: warning, using standard rlogin: ");
943 vfprintf(stderr
, fmt
, ap
);
945 (void)fprintf(stderr
, ".\n");
952 (void)fprintf(stderr
,
953 "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
956 "8EKLx", " [-k realm] ");
958 "8EKL", " [-k realm] ");
967 * The following routine provides compatibility (such as it is) between older
968 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
972 get_window_size(fd
, wp
)
979 if ((error
= ioctl(0, TIOCGSIZE
, &ts
)) != 0)
981 wp
->ws_row
= ts
.ts_lines
;
982 wp
->ws_col
= ts
.ts_cols
;
996 if ((len
= strlen(p
)) == 1) /* use any single char, including '\' */
998 /* otherwise, \nnn */
999 if (*p
== '\\' && len
>= 2 && len
<= 4) {
1000 val
= strtol(++p
, NULL
, 8);
1003 return ((u_int
)val
);
1004 if (*p
< '0' || *p
> '8')
1008 msg("illegal option value -- e");