1/*	$NetBSD: rexec.c,v 1.15 2002/11/11 23:43:03 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1980, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
18 *
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
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if defined(LIBC_SCCS) && !defined(lint)
34#if 0
35static char sccsid[] = "@(#)rexec.c	8.1 (Berkeley) 6/4/93";
36#else
37__RCSID("$NetBSD: rexec.c,v 1.15 2002/11/11 23:43:03 thorpej Exp $");
38#endif
39#endif /* LIBC_SCCS and not lint */
40
41#include <sys/types.h>
42#include <sys/socket.h>
43
44#include <netinet/in.h>
45
46#include <assert.h>
47#include <err.h>
48#include <errno.h>
49#include <netdb.h>
50#include <stdio.h>
51#include <string.h>
52#include <unistd.h>
53
54int	rexecoptions;
55
56int	ruserpass __P((const char *, char **, char **));
57int	rexec __P((char **, int, char *, char *, char *, int *));
58
59int
60rexec(ahost, rport, name, pass, cmd, fd2p)
61	char **ahost;
62	int rport;
63	char *name, *pass, *cmd;
64	int *fd2p;
65{
66	struct sockaddr_in rsin, from;
67	socklen_t fromlen;
68	struct hostent *hp;
69	u_short port;
70	size_t len;
71	unsigned int timo = 1;
72	int s, s3;
73	char c;
74
75	_DIAGASSERT(ahost != NULL);
76	_DIAGASSERT(name != NULL);
77	_DIAGASSERT(pass != NULL);
78	_DIAGASSERT(cmd != NULL);
79	/* fd2p may be NULL */
80
81	hp = gethostbyname(*ahost);
82	if (hp == 0) {
83		warnx("Error resolving %s (%s)", *ahost, hstrerror(h_errno));
84		return -1;
85	}
86	*ahost = hp->h_name;
87	(void)ruserpass(hp->h_name, &name, &pass);
88retry:
89	if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
90		warn("Cannot create socket");
91		return (-1);
92	}
93	rsin.sin_family = hp->h_addrtype;
94	rsin.sin_len = sizeof(rsin);
95	rsin.sin_port = rport;
96	/* Avoid data corruption from bogus DNS results */
97	if (hp->h_length > (int) sizeof(rsin.sin_addr))
98		len = sizeof(rsin.sin_addr);
99	else
100		len = hp->h_length;
101	memcpy(&rsin.sin_addr, hp->h_addr, len);
102	if (connect(s, (struct sockaddr *)(void *)&rsin, sizeof(rsin)) == -1) {
103		if (errno == ECONNREFUSED && timo <= 16) {
104			(void)close(s);
105			sleep(timo);
106			timo *= 2;
107			goto retry;
108		}
109		warn("Cannot connect to %s", hp->h_name);
110		return -1;
111	}
112	port = 0;
113	if (fd2p == 0) {
114		(void)write(s, "", 1);
115	} else {
116		char num[8];
117		int s2;
118		struct sockaddr_in sin2;
119		socklen_t sin2len;
120
121		s2 = socket(AF_INET, SOCK_STREAM, 0);
122		if (s2 == -1) {
123			warn("Error creating socket");
124			(void)close(s);
125			return -1;
126		}
127		listen(s2, 1);
128		sin2len = sizeof(sin2);
129		if (getsockname(s2, (struct sockaddr *)(void *)&sin2,
130		    &sin2len) == -1 || sin2len != sizeof(sin2)) {
131			warn("Failed to get socket name");
132			(void)close(s2);
133			goto bad;
134		}
135		port = ntohs((u_short)sin2.sin_port);
136		(void)snprintf(num, sizeof(num), "%u", port);
137		(void)write(s, num, strlen(num)+1);
138
139		fromlen = sizeof(from);
140		s3 = accept(s2, (struct sockaddr *)(void *)&from, &fromlen);
141		(void)close(s2);
142		if (s3 == -1) {
143			warn("Error accepting connection");
144			port = 0;
145			goto bad;
146		}
147		*fd2p = s3;
148	}
149	(void)write(s, name, strlen(name) + 1);
150	/* should public key encypt the password here */
151	(void)write(s, pass, strlen(pass) + 1);
152	(void)write(s, cmd, strlen(cmd) + 1);
153	if (read(s, &c, 1) != 1) {
154		warn("Error reading from %s", *ahost);
155		goto bad;
156	}
157	if (c != 0) {
158		while (read(s, &c, 1) == 1) {
159			(void) write(2, &c, 1);
160			if (c == '\n')
161				break;
162		}
163		goto bad;
164	}
165	return s;
166bad:
167	if (port)
168		(void)close(*fd2p);
169	(void)close(s);
170	return -1;
171}
172