1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35#pragma ident	"%Z%%M%	%I%	%E% SMI"
36
37#include <myrcmd.h>
38#include <limits.h>
39#include <stdio.h>
40#include <string.h>
41#include <pwd.h>
42#include <errno.h>
43#include <unistd.h>
44#include <sys/types.h>
45#include <sys/file.h>
46#include <signal.h>
47#include <sys/socket.h>
48
49#include <netinet/in.h>
50#include <arpa/inet.h>
51
52#include <netdb.h>
53#include <fcntl.h>
54#include <libintl.h>
55
56#include <memutils.h>
57
58#define	index(s, c)		strchr(s, c)
59char	*strchr();
60
61char	*inet_ntoa();
62
63char myrcmd_stderr[1024];
64
65int
66myrcmd(char **ahost, unsigned short rport, char *locuser, char *remuser,
67    char *cmd)
68{
69	uint_t loclen, remlen, cmdlen;
70	int s, timo, retval;
71	int tries = 0;
72	pid_t pid;
73	struct sockaddr_in sin;
74	char c;
75	int lport;
76	int saverr;
77	struct hostent *hp;
78	sigset_t oldmask;
79	sigset_t newmask;
80	struct sigaction oldaction;
81	struct sigaction newaction;
82	static struct hostent numhp;
83	static char numhostname[32];	/* big enough for "255.255.255.255" */
84	struct in_addr numaddr;
85	struct in_addr *numaddrlist[2];
86
87	myrcmd_stderr[0] = '\0';	/* empty error string */
88	pid = getpid();
89	hp = gethostbyname(*ahost);
90	if (hp == 0) {
91		char *straddr;
92
93		bzero((char *)numaddrlist, sizeof (numaddrlist));
94		if ((numaddr.s_addr = inet_addr(*ahost)) == (in_addr_t)-1) {
95			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
96			    gettext("%s: unknown host\n"), *ahost);
97			return (MYRCMD_NOHOST);
98		} else {
99			bzero((char *)&numhp, sizeof (numhp));
100			bzero(numhostname, sizeof (numhostname));
101
102			if ((straddr = inet_ntoa(numaddr)) == (char *)0) {
103				(void) snprintf(myrcmd_stderr,
104				    sizeof (myrcmd_stderr),
105				    gettext("%s: unknown host\n"), *ahost);
106				return (MYRCMD_NOHOST);
107			}
108			(void) strncpy(numhostname, straddr,
109			    sizeof (numhostname));
110			numhostname[sizeof (numhostname) - 1] = '\0';
111			numhp.h_name = numhostname;
112			numhp.h_addrtype = AF_INET;
113			numhp.h_length = sizeof (numaddr);
114			numaddrlist[0] = &numaddr;
115			numaddrlist[1] = NULL;
116			numhp.h_addr_list = (char **)numaddrlist;
117			hp = &numhp;
118		}
119	}
120	*ahost = hp->h_name;
121
122	/* This provides a bounds-test for the bcopy()s below. */
123	if ((unsigned)(hp->h_length) > sizeof (sin.sin_addr)) {
124		(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
125		    gettext("rcmd: address size: %d larger than limit %d\n"),
126		    hp->h_length, sizeof (sin.sin_addr));
127		return (MYRCMD_EBAD);
128	}
129
130	/* ignore SIGPIPE */
131	bzero((char *)&newaction, sizeof (newaction));
132	newaction.sa_handler = SIG_IGN;
133	newaction.sa_flags = SA_ONSTACK;
134	(void) sigaction(SIGPIPE, &newaction, &oldaction);
135
136	/* block SIGURG */
137	bzero((char *)&newmask, sizeof (newmask));
138	(void) sigaddset(&newmask, SIGURG);
139	(void) sigprocmask(SIG_BLOCK, &newmask, &oldmask);
140again:
141	timo = 1;
142	/*
143	 * Use 0 as lport means that rresvport() will bind to a port in
144	 * the anonymous priviledged port range.
145	 */
146	lport = 0;
147	for (;;) {
148		s = rresvport(&lport);
149		if (s < 0) {
150			int err;
151
152			if (errno == EAGAIN) {
153				(void) snprintf(myrcmd_stderr,
154				    sizeof (myrcmd_stderr),
155				    gettext("socket: All ports in use\n"));
156				err = MYRCMD_ENOPORT;
157			} else {
158				saverr = errno;
159				(void) snprintf(myrcmd_stderr,
160				    sizeof (myrcmd_stderr),
161				    gettext("rcmd: socket: %s\n"),
162				    strerror(saverr));
163				err = MYRCMD_ENOSOCK;
164			}
165			/* restore original SIGPIPE handler */
166			(void) sigaction(SIGPIPE, &oldaction,
167			    (struct sigaction *)0);
168
169			/* restore original signal mask */
170			(void) sigprocmask(SIG_SETMASK, &oldmask,
171			    (sigset_t *)0);
172			return (err);
173		}
174		/* Can't fail, according to fcntl(2) */
175		(void) fcntl(s, F_SETOWN, pid);
176		sin.sin_family = hp->h_addrtype;
177		bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length);
178		sin.sin_port = rport;
179		if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
180			break;
181		saverr = errno;
182		(void) close(s);
183		if (saverr == EADDRINUSE) {
184			continue;
185		}
186		if (saverr == ECONNREFUSED && timo <= 16) {
187			sleep(timo);
188			timo *= 2;
189			continue;
190		}
191		if (hp->h_addr_list[1] != NULL) {
192			saverr = errno;
193
194			fprintf(stderr,
195			    gettext("connect to address %s: "),
196			    inet_ntoa(sin.sin_addr));
197			errno = saverr;
198			perror(0);
199			hp->h_addr_list++;
200			bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
201			    hp->h_length);
202			fprintf(stderr, gettext("Trying %s...\n"),
203				inet_ntoa(sin.sin_addr));
204			continue;
205		}
206		(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
207		    "%s: %s\n", hp->h_name, strerror(saverr));
208		/* restore original SIGPIPE handler */
209		(void) sigaction(SIGPIPE, &oldaction,
210		    (struct sigaction *)0);
211
212		/* restore original signal mask */
213		(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
214		return (MYRCMD_ENOCONNECT);
215	}
216	if (write(s, "", 1) < 0) {
217		(void) close(s);
218		return (MYRCMD_ENOCONNECT);
219	}
220
221	loclen = strlen(locuser) + 1;
222	remlen = strlen(remuser) + 1;
223	cmdlen = strlen(cmd) + 1;
224
225	if (((retval = write(s, locuser, loclen)) != loclen) ||
226	    ((retval = write(s, remuser, remlen)) != remlen) ||
227	    ((retval = write(s, cmd, cmdlen)) != cmdlen)) {
228		if (retval == -1)
229			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
230			    "write: %s\n", strerror(errno));
231		else
232			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
233			    gettext("write unexpectedly truncated\n"));
234		goto bad;
235	}
236	retval = read(s, &c, 1);
237	if (retval != 1) {
238		if (retval == 0) {
239			/*
240			 * Solaris 2.0 bug alert.  Sometimes, if the
241			 * tapehost is a Solaris 2.0 system, the connection
242			 * will be dropped at this point.  Let's try again,
243			 * three times, before we throw in the towel.
244			 */
245			if (++tries < 3) {
246				(void) close(s);
247				goto again;
248			}
249			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
250			    gettext("Protocol error, %s closed connection\n"),
251			    *ahost);
252		} else if (retval < 0) {
253			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
254			    "%s: %s\n", *ahost, strerror(errno));
255		} else {
256			(void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr),
257			    gettext("Protocol error, %s sent %d bytes\n"),
258			    *ahost, retval);
259		}
260		goto bad;
261	}
262	if (c != 0) {
263		char *cp = myrcmd_stderr;
264		char *ecp = &myrcmd_stderr[sizeof (myrcmd_stderr) - 1];
265
266		while (read(s, &c, 1) == 1) {
267			*cp++ = c;
268			if (c == '\n' || cp >= ecp)
269				break;
270		}
271		*cp = '\0';
272		goto bad;
273	}
274	/* restore original SIGPIPE handler */
275	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
276
277	/* restore original signal mask */
278	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
279	return (s);
280bad:
281	(void) close(s);
282	/* restore original SIGPIPE handler */
283	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
284
285	/* restore original signal mask */
286	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
287	return (MYRCMD_EBAD);
288}
289