9287924fdbf01dedff8e6ea9c682cf4f5e59d475
[dragonfly.git] / usr.bin / rsh / rsh.c
1 /*-
2  * Copyright (c) 1983, 1990, 1993, 1994
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. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#)rsh.c    8.3 (Berkeley) 4/6/94
37  * $FreeBSD: src/usr.bin/rsh/rsh.c,v 1.35 2005/05/21 09:55:07 ru Exp $
38  * $DragonFly: src/usr.bin/rsh/rsh.c,v 1.7 2007/05/18 17:05:12 dillon Exp $
39  */
40
41 #include <sys/param.h>
42 #include <sys/signal.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
45 #include <sys/file.h>
46 #include <sys/time.h>
47
48 #include <netinet/in.h>
49 #include <netdb.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <libutil.h>
54 #include <paths.h>
55 #include <pwd.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 /*
63  * rsh - remote shell
64  */
65 int     rfd2;
66
67 int family = PF_UNSPEC;
68 char rlogin[] = "rlogin";
69
70 void    connect_timeout(int);
71 char   *copyargs(char * const *);
72 void    sendsig(int);
73 void    talk(int, long, pid_t, int, int);
74 void    usage(void);
75
76 int
77 main(int argc, char **argv)
78 {
79         struct passwd const *pw;
80         struct servent const *sp;
81         long omask;
82         int argoff, asrsh, ch, dflag, nflag, one, rem;
83         pid_t pid = 0;
84         uid_t uid;
85         char *args, *host, *p, *user;
86         int timeout = 0;
87
88         argoff = asrsh = dflag = nflag = 0;
89         one = 1;
90         host = user = NULL;
91
92         /* if called as something other than "rsh", use it as the host name */
93         if ((p = strrchr(argv[0], '/')))
94                 ++p;
95         else
96                 p = argv[0];
97         if (strcmp(p, "rsh"))
98                 host = p;
99         else
100                 asrsh = 1;
101
102         /* handle "rsh host flags" */
103         if (!host && argc > 2 && argv[1][0] != '-') {
104                 host = argv[1];
105                 argoff = 1;
106         }
107
108 #define OPTIONS "468Lde:l:nt:w"
109         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
110                 switch(ch) {
111                 case '4':
112                         family = PF_INET;
113                         break;
114
115                 case '6':
116                         family = PF_INET6;
117                         break;
118
119                 case 'L':       /* -8Lew are ignored to allow rlogin aliases */
120                 case 'e':
121                 case 'w':
122                 case '8':
123                         break;
124                 case 'd':
125                         dflag = 1;
126                         break;
127                 case 'l':
128                         user = optarg;
129                         break;
130                 case 'n':
131                         nflag = 1;
132                         break;
133                 case 't':
134                         timeout = atoi(optarg);
135                         break;
136                 case '?':
137                 default:
138                         usage();
139                 }
140         optind += argoff;
141
142         /* if haven't gotten a host yet, do so */
143         if (!host && !(host = argv[optind++]))
144                 usage();
145
146         /* if no further arguments, must have been called as rlogin. */
147         if (!argv[optind]) {
148                 if (asrsh)
149                         *argv = rlogin;
150                 execv(_PATH_RLOGIN, argv);
151                 err(1, "can't exec %s", _PATH_RLOGIN);
152         }
153
154         argc -= optind;
155         argv += optind;
156
157         if (!(pw = getpwuid(uid = getuid())))
158                 errx(1, "unknown user id");
159         if (!user)
160                 user = pw->pw_name;
161
162         args = copyargs(argv);
163
164         sp = NULL;
165         if (sp == NULL)
166                 sp = getservbyname("shell", "tcp");
167         if (sp == NULL)
168                 errx(1, "shell/tcp: unknown service");
169
170         if (timeout) {
171                 signal(SIGALRM, connect_timeout);
172                 alarm(timeout);
173         }
174         rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2,
175                       family);
176         if (timeout) {
177                 signal(SIGALRM, SIG_DFL);
178                 alarm(0);
179         }
180
181         if (rem < 0)
182                 exit(1);
183
184         if (rfd2 < 0)
185                 errx(1, "can't establish stderr");
186         if (dflag) {
187                 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
188                     sizeof(one)) < 0)
189                         warn("setsockopt");
190                 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
191                     sizeof(one)) < 0)
192                         warn("setsockopt");
193         }
194
195         setuid(uid);
196         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
197         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
198                 signal(SIGINT, sendsig);
199         if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
200                 signal(SIGQUIT, sendsig);
201         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
202                 signal(SIGTERM, sendsig);
203
204         if (!nflag) {
205                 pid = fork();
206                 if (pid < 0)
207                         err(1, "fork");
208         }
209         else
210                 shutdown(rem, SHUT_WR);
211
212         ioctl(rfd2, FIONBIO, &one);
213         ioctl(rem, FIONBIO, &one);
214
215         talk(nflag, omask, pid, rem, timeout);
216
217         if (!nflag)
218                 kill(pid, SIGKILL);
219         exit(0);
220 }
221
222 void
223 talk(int nflag, long omask, pid_t pid, int rem, int timeout)
224 {
225         int cc, wc;
226         fd_set readfrom, ready, rembits;
227         char buf[BUFSIZ];
228         const char *bp;
229         struct timeval tvtimeout;
230         int nfds, srval;
231
232         if (!nflag && pid == 0) {
233                 close(rfd2);
234
235 reread:         errno = 0;
236                 if ((cc = read(STDIN_FILENO, buf, sizeof(buf))) <= 0)
237                         goto done;
238                 bp = buf;
239
240 rewrite:
241                 if (rem >= FD_SETSIZE)
242                         errx(1, "descriptor too big");
243                 FD_ZERO(&rembits);
244                 FD_SET(rem, &rembits);
245                 nfds = rem + 1;
246                 if (select(nfds, 0, &rembits, 0, 0) < 0) {
247                         if (errno != EINTR)
248                                 err(1, "select");
249                         goto rewrite;
250                 }
251                 if (!FD_ISSET(rem, &rembits))
252                         goto rewrite;
253                 wc = write(rem, bp, cc);
254                 if (wc < 0) {
255                         if (errno == EWOULDBLOCK)
256                                 goto rewrite;
257                         goto done;
258                 }
259                 bp += wc;
260                 cc -= wc;
261                 if (cc == 0)
262                         goto reread;
263                 goto rewrite;
264 done:
265                 shutdown(rem, SHUT_WR);
266                 exit(0);
267         }
268
269         tvtimeout.tv_sec = timeout;
270         tvtimeout.tv_usec = 0;
271
272         sigsetmask(omask);
273         if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE)
274                 errx(1, "descriptor too big");
275         FD_ZERO(&readfrom);
276         FD_SET(rfd2, &readfrom);
277         FD_SET(rem, &readfrom);
278         nfds = MAX(rfd2+1, rem+1);
279         do {
280                 ready = readfrom;
281                 if (timeout) {
282                         srval = select(nfds, &ready, 0, 0, &tvtimeout);
283                 } else {
284                         srval = select(nfds, &ready, 0, 0, 0);
285                 }
286
287                 if (srval < 0) {
288                         if (errno != EINTR)
289                                 err(1, "select");
290                         continue;
291                 }
292                 if (srval == 0)
293                         errx(1, "timeout reached (%d seconds)", timeout);
294                 if (FD_ISSET(rfd2, &ready)) {
295                         errno = 0;
296                         cc = read(rfd2, buf, sizeof(buf));
297                         if (cc <= 0) {
298                                 if (errno != EWOULDBLOCK)
299                                         FD_CLR(rfd2, &readfrom);
300                         } else
301                                 write(STDERR_FILENO, buf, cc);
302                 }
303                 if (FD_ISSET(rem, &ready)) {
304                         errno = 0;
305                         cc = read(rem, buf, sizeof(buf));
306                         if (cc <= 0) {
307                                 if (errno != EWOULDBLOCK)
308                                         FD_CLR(rem, &readfrom);
309                         } else
310                                 write(STDOUT_FILENO, buf, cc);
311                 }
312         } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
313 }
314
315 void
316 connect_timeout(int sig __unused)
317 {
318         char message[] = "timeout reached before connection completed.\n";
319
320         write(STDERR_FILENO, message, sizeof(message) - 1);
321         _exit(1);
322 }
323
324 void
325 sendsig(int sig)
326 {
327         char signo;
328
329         signo = sig;
330         write(rfd2, &signo, 1);
331 }
332
333 char *
334 copyargs(char * const *argv)
335 {
336         int cc;
337         char *args, *p;
338         char * const *ap;
339
340         cc = 0;
341         for (ap = argv; *ap; ++ap)
342                 cc += strlen(*ap) + 1;
343         if (!(args = malloc((u_int)cc)))
344                 err(1, NULL);
345         for (p = args, ap = argv; *ap; ++ap) {
346                 strcpy(p, *ap);
347                 for (p = strcpy(p, *ap); *p; ++p);
348                 if (ap[1])
349                         *p++ = ' ';
350         }
351         return (args);
352 }
353
354 void
355 usage(void)
356 {
357
358         fprintf(stderr,
359             "usage: rsh [-46dn] [-l username] [-t timeout] host [command]\n");
360         exit(1);
361 }