1 /* $NetBSD: rlogin.c,v 1.39 2008/07/21 14:19:25 lukem Exp $ */
4 * Copyright (c) 1983, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95";
42 __RCSID("$NetBSD: rlogin.c,v 1.39 2008/07/21 14:19:25 lukem Exp $");
47 * rlogin - remote login
49 #include <sys/param.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
53 #include <sys/resource.h>
56 #include <netinet/in.h>
57 #include <netinet/in_systm.h>
58 #include <netinet/ip.h>
77 #ifndef TIOCPKT_WINDOW
78 #define TIOCPKT_WINDOW 0x80
81 /* concession to Sun */
87 #define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0)
91 struct termios deftty
;
94 u_char escapechar
= '~';
98 unsigned short ws_row
, ws_col
;
99 unsigned short ws_xpixel
, ws_ypixel
;
102 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
104 struct winsize winsize
;
106 void catch_child(int);
107 void copytochild(int);
108 void doit(sigset_t
*);
111 u_int
getescape(char *);
113 int main(int, char **);
115 void msg(const char *);
117 int reader(sigset_t
*);
118 void sendwindow(void);
127 int get_window_size(int, struct winsize
*);
131 main(int argc
, char *argv
[])
138 int argoff
, ch
, dflag
, one
;
140 int family
= AF_UNSPEC
;
141 char *host
, *p
, *user
, *name
, term
[1024] = "network";
144 char *service
= NULL
;
152 if (strcmp(getprogname(), "rlogin") != 0) {
153 host
= strdup(getprogname());
158 /* handle "rlogin host flags" */
159 if (!host
&& argc
> 2 && argv
[1][0] != '-') {
164 #define OPTIONS "468dEe:l:p:"
165 while ((ch
= getopt(argc
- argoff
, argv
+ argoff
, OPTIONS
)) != -1)
184 escapechar
= getescape(optarg
);
190 sp
= getport(service
= optarg
, "tcp");
200 /* if haven't gotten a host yet, do so */
201 if (!host
&& !(host
= *argv
++))
207 if (!(pw
= getpwuid(uid
= getuid())))
208 errx(1, "unknown user id.");
209 /* Accept user1@host format, though "-l user2" overrides user1 */
210 p
= strchr(host
, '@');
213 if (!user
&& p
> host
)
219 if ((name
= strdup(pw
->pw_name
)) == NULL
)
225 sp
= getservbyname("login", "tcp");
227 errx(1, "login/tcp: unknown service.");
229 if ((p
= getenv("TERM")) != NULL
)
230 (void)strlcpy(term
, p
, sizeof(term
));
232 if (len
< (int)(sizeof(term
) - 1) && tcgetattr(0, &tty
) == 0) {
233 /* start at 2 to include the / */
234 for (ospeed
= i
= cfgetospeed(&tty
), len2
= 2; i
> 9; len2
++)
237 if (len
+ len2
< (int)sizeof(term
))
238 (void)snprintf(term
+ len
, len2
+ 1, "/%d", ospeed
);
241 (void)get_window_size(0, &winsize
);
243 sigemptyset(&sa
.sa_mask
);
244 sa
.sa_flags
= SA_RESTART
;
245 sa
.sa_handler
= lostpeer
;
246 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*)0);
247 /* will use SIGUSR1 for window size hack, so hold it off */
249 sigaddset(&smask
, SIGURG
);
250 sigaddset(&smask
, SIGUSR1
);
251 (void)sigprocmask(SIG_SETMASK
, &smask
, &smask
);
253 * We set SIGURG and SIGUSR1 below so that an
254 * incoming signal will be held pending rather than being
255 * discarded. Note that these routines will be ready to get
256 * a signal by the time that they are unblocked below.
258 sa
.sa_handler
= copytochild
;
259 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
260 sa
.sa_handler
= writeroob
;
261 (void)sigaction(SIGUSR1
, &sa
, (struct sigaction
*) 0);
263 /* don't dump core */
264 rlim
.rlim_cur
= rlim
.rlim_max
= 0;
265 if (setrlimit(RLIMIT_CORE
, &rlim
) < 0)
268 rem
= rcmd_af(&host
, sp
->s_port
, name
, user
, term
, 0, family
);
275 setsockopt(rem
, SOL_SOCKET
, SO_DEBUG
, &one
, sizeof(one
)) < 0)
276 warn("setsockopt DEBUG (ignored)");
278 struct sockaddr_storage ss
;
281 if (getsockname(rem
, (struct sockaddr
*)&ss
, &sslen
) == 0
282 && ((struct sockaddr
*)&ss
)->sa_family
== AF_INET
) {
283 one
= IPTOS_LOWDELAY
;
284 if (setsockopt(rem
, IPPROTO_IP
, IP_TOS
, (char *)&one
,
286 warn("setsockopt TOS (ignored)");
300 doit(sigset_t
*smask
)
304 sigemptyset(&sa
.sa_mask
);
305 sa
.sa_flags
= SA_RESTART
;
306 sa
.sa_handler
= SIG_IGN
;
307 (void)sigaction(SIGINT
, &sa
, (struct sigaction
*) 0);
318 if (reader(smask
) == 0) {
319 msg("connection closed.");
323 msg("\aconnection closed.");
328 * We may still own the socket, and may have a pending SIGURG (or might
329 * receive one soon) that we really want to send to the reader. When
330 * one of these comes in, the trap copytochild simply copies such
331 * signals to the child. We can now unblock SIGURG and SIGUSR1
332 * that were set above.
334 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
335 sa
.sa_handler
= catch_child
;
336 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
338 msg("closed connection.");
342 /* trap a signal, unless it is being ignored. */
350 sigaddset(&sigs
, sig
);
351 sigprocmask(SIG_BLOCK
, &sigs
, &sigs
);
353 sigemptyset(&sa
.sa_mask
);
354 sa
.sa_handler
= exit
;
355 sa
.sa_flags
= SA_RESTART
;
356 (void)sigaction(sig
, &sa
, &sa
);
357 if (sa
.sa_handler
== SIG_IGN
)
358 (void)sigaction(sig
, &sa
, (struct sigaction
*) 0);
360 (void)sigprocmask(SIG_SETMASK
, &sigs
, (sigset_t
*) 0);
372 /* make sure catch_child does not snap it up */
373 sigemptyset(&sa
.sa_mask
);
374 sa
.sa_handler
= SIG_DFL
;
376 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
377 if (kill(child
, SIGKILL
) >= 0)
378 while ((w
= wait(&wstatus
)) > 0 && w
!= child
)
387 * This is called when the reader process gets the out-of-band (urgent)
388 * request to turn on the window-changing protocol.
395 if (dosigwinch
== 0) {
397 sigemptyset(&sa
.sa_mask
);
398 sa
.sa_handler
= sigwinch
;
399 sa
.sa_flags
= SA_RESTART
;
400 (void)sigaction(SIGWINCH
, &sa
, (struct sigaction
*) 0);
406 catch_child(int signo
)
412 pid
= waitpid(-1, &status
, WNOHANG
|WUNTRACED
);
415 /* if the child (reader) dies, just quit */
416 if (pid
< 0 || (pid
== child
&& !WIFSTOPPED(status
)))
417 done(WEXITSTATUS(status
) | WTERMSIG(status
));
423 * writer: write to remote: 0 -> line.
425 * ~^Z suspend rlogin process.
426 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
434 bol
= 1; /* beginning of line */
437 n
= read(STDIN_FILENO
, &c
, 1);
439 if (n
< 0 && errno
== EINTR
)
444 * If we're at the beginning of the line and recognize a
445 * command character, then we echo locally. Otherwise,
446 * characters are echo'd remotely. If the command character
447 * is doubled, this acts as a force and local echo is
452 if (!noescape
&& c
== escapechar
) {
458 if (c
== '.' || CCEQ(deftty
.c_cc
[VEOF
], c
)) {
462 if (CCEQ(deftty
.c_cc
[VSUSP
], c
)) {
468 if (CCEQ(deftty
.c_cc
[VDSUSP
], c
)) {
474 if (c
!= escapechar
) {
475 (void)write(rem
, &escapechar
, 1);
479 if (write(rem
, &c
, 1) == 0) {
484 bol
= CCEQ(deftty
.c_cc
[VKILL
], c
) ||
485 CCEQ(deftty
.c_cc
[VEOF
], c
) ||
486 CCEQ(deftty
.c_cc
[VINTR
], c
) ||
487 CCEQ(deftty
.c_cc
[VSUSP
], c
) ||
488 c
== '\r' || c
== '\n';
505 } else if (c
== 0177) {
512 (void)write(STDOUT_FILENO
, buf
, p
- buf
);
521 sigemptyset(&sa
.sa_mask
);
522 sa
.sa_handler
= SIG_IGN
;
523 sa
.sa_flags
= SA_RESTART
;
524 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
525 (void)kill(all
? 0 : getpid(), SIGTSTP
);
526 sa
.sa_handler
= catch_child
;
527 (void)sigaction(SIGCHLD
, &sa
, (struct sigaction
*) 0);
529 sigwinch(0); /* check for size changes */
537 if (dosigwinch
&& get_window_size(0, &ws
) == 0 &&
538 memcmp(&ws
, &winsize
, sizeof(ws
))) {
545 * Send the window size to the server via the magic escape
551 char obuf
[4 + sizeof (struct winsize
)];
553 wp
= (struct winsize
*)(obuf
+4);
558 wp
->ws_row
= htons(winsize
.ws_row
);
559 wp
->ws_col
= htons(winsize
.ws_col
);
560 wp
->ws_xpixel
= htons(winsize
.ws_xpixel
);
561 wp
->ws_ypixel
= htons(winsize
.ws_ypixel
);
563 (void)write(rem
, obuf
, sizeof(obuf
));
567 * reader: read from remote: line -> 1
574 int rcvcnt
, rcvstate
;
575 char rcvbuf
[8 * 1024];
582 char waste
[BUFSIZ
], mark
;
585 while (recv(rem
, &mark
, 1, MSG_OOB
) < 0) {
589 * Urgent data not here yet. It may not be possible
590 * to send it yet if we are blocked for output and
591 * our input buffer is full.
593 if (rcvcnt
< (int)sizeof(rcvbuf
)) {
594 n
= read(rem
, rcvbuf
+ rcvcnt
,
595 sizeof(rcvbuf
) - rcvcnt
);
600 n
= read(rem
, waste
, sizeof(waste
));
609 if (mark
& TIOCPKT_WINDOW
) {
610 /* Let server know about window size changes */
611 (void)kill(ppid
, SIGUSR1
);
613 if (!eight
&& (mark
& TIOCPKT_NOSTOP
)) {
614 (void)tcgetattr(0, &tty
);
615 tty
.c_iflag
&= ~IXON
;
616 (void)tcsetattr(0, TCSANOW
, &tty
);
618 if (!eight
&& (mark
& TIOCPKT_DOSTOP
)) {
619 (void)tcgetattr(0, &tty
);
620 tty
.c_iflag
|= (deftty
.c_iflag
& IXON
);
621 (void)tcsetattr(0, TCSANOW
, &tty
);
623 if (mark
& TIOCPKT_FLUSHWRITE
) {
624 (void)tcflush(1, TCIOFLUSH
);
626 if (ioctl(rem
, SIOCATMARK
, &atmark
) < 0) {
627 warn("ioctl SIOCATMARK (ignored)");
632 n
= read(rem
, waste
, sizeof (waste
));
637 * Don't want any pending data to be output, so clear the recv
638 * buffer. If we were hanging on a write when interrupted,
639 * don't want it to restart. If we were reading, restart
646 /* oob does not do FLUSHREAD (alas!) */
649 * If we filled the receive buffer while a read was pending, longjmp
650 * to the top to restart appropriately. Don't abort a pending write,
651 * however, or we won't know how much was written.
653 if (rcvd
&& rcvstate
== READING
)
657 /* reader: read from remote: line -> 1 */
659 reader(sigset_t
*smask
)
666 pid
= getpid(); /* modern systems use positives for pid */
667 sigemptyset(&sa
.sa_mask
);
668 sa
.sa_flags
= SA_RESTART
;
669 sa
.sa_handler
= SIG_IGN
;
670 (void)sigaction(SIGTTOU
, &sa
, (struct sigaction
*) 0);
672 (void)sigaction(SIGURG
, &sa
, (struct sigaction
*) 0);
674 (void)fcntl(rem
, F_SETOWN
, pid
);
675 (void)setjmp(rcvtop
);
676 (void)sigprocmask(SIG_SETMASK
, smask
, (sigset_t
*) 0);
679 while ((remaining
= rcvcnt
- (bufp
- rcvbuf
)) > 0) {
681 n
= write(STDOUT_FILENO
, bufp
, remaining
);
693 rcvcnt
= read(rem
, rcvbuf
, sizeof (rcvbuf
));
713 (void)tcsetattr(0, TCSANOW
, &deftty
);
716 (void)tcgetattr(0, &deftty
);
718 /* This is loosely derived from sys/compat/tty_compat.c. */
719 tty
.c_lflag
&= ~(ECHO
|ICANON
|ISIG
|IEXTEN
);
720 tty
.c_iflag
&= ~ICRNL
;
721 tty
.c_oflag
&= ~OPOST
;
725 tty
.c_iflag
&= IXOFF
;
726 tty
.c_cflag
&= ~(CSIZE
|PARENB
);
729 (void)tcsetattr(0, TCSANOW
, &tty
);
741 sa
.sa_flags
= SA_RESTART
;
742 sa
.sa_handler
= SIG_IGN
;
743 (void)sigaction(SIGPIPE
, &sa
, (struct sigaction
*)0);
744 msg("\aconnection closed.");
748 /* copy SIGURGs to the child process. */
750 copytochild(int signo
)
753 (void)kill(child
, SIGURG
);
760 (void)fprintf(stderr
, "rlogin: %s\r\n", str
);
767 (void)fprintf(stderr
,
768 "usage: rlogin [-%s]%s[-e char] [-l username] [-p port] [username@]host\n",
774 * The following routine provides compatibility (such as it is) between older
775 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
779 get_window_size(fd
, wp
)
786 if ((error
= ioctl(0, TIOCGSIZE
, &ts
)) != 0)
788 wp
->ws_row
= ts
.ts_lines
;
789 wp
->ws_col
= ts
.ts_cols
;
802 if ((len
= strlen(p
)) == 1) /* use any single char, including '\' */
804 /* otherwise, \nnn */
805 if (*p
== '\\' && len
>= 2 && len
<= 4) {
806 val
= strtol(++p
, NULL
, 8);
810 if (*p
< '0' || *p
> '8')
814 msg("illegal option value -- e");