1 /* rcmd.c --- execute a command on a remote host from Windows NT
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 Jim Blandy <jimb@cyclic.com> --- August 1995 */
14
15 #include "cvs.h"
16 #include "rcmd.h"
17
18 #include <io.h>
19 #include <fcntl.h>
20 #include <malloc.h>
21 #include <errno.h>
22
23 #ifdef HAVE_WINSOCK_H
24 #include <winsock.h>
25 #else
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <netdb.h>
30 typedef int SOCKET;
31 #define closesocket close
32 #define SOCK_ERRNO errno
33 #define SOCK_STRERROR strerror
34 /* Probably would be cleaner to just use EADDRINUSE, as NT has that too. */
35 #define WSAEADDRINUSE EADDRINUSE
36 /* Probably would be cleaner to just check for < 0. Might want to
37 double-check that doing so would seem to work on NT. */
38 #define SOCKET_ERROR -1
39 #define INVALID_SOCKET -1
40 #endif
41
42 #include <stdio.h>
43 #include <assert.h>
44
45 /* The rest of this file contains the rcmd() code, which is used
46 only by START_SERVER. The idea for a long-term direction is
47 that this code can be made portable (by using SOCK_ERRNO and
48 so on), and then moved to client.c or someplace it can be
49 shared with the VMS port and any other ports which may want it. */
50
51
52 static int
resolve_address(const char ** ahost,struct sockaddr_in * sai)53 resolve_address (const char **ahost, struct sockaddr_in *sai)
54 {
55 {
56 unsigned long addr = inet_addr (*ahost);
57
58 if (addr != (unsigned long) -1)
59 {
60 sai->sin_family = AF_INET;
61 sai->sin_addr.s_addr = addr;
62 return 0;
63 }
64 }
65
66 {
67 struct hostent *e = gethostbyname (*ahost);
68
69 if (e)
70 {
71 assert (e->h_addrtype == AF_INET);
72 assert (e->h_addr);
73 *ahost = e->h_name;
74 sai->sin_family = AF_INET;
75 memcpy (&sai->sin_addr, e->h_addr, sizeof (sai->sin_addr));
76 return 0;
77 }
78 }
79
80 error (1, 0, "no such host %s", *ahost);
81 /* Shut up gcc -Wall. */
82 return 1;
83 }
84
85 static SOCKET
bind_and_connect(struct sockaddr_in * server_sai)86 bind_and_connect (struct sockaddr_in *server_sai)
87 {
88 SOCKET s;
89 struct sockaddr_in client_sai;
90 u_short client_port;
91
92 client_sai.sin_family = AF_INET;
93 client_sai.sin_addr.s_addr = htonl (INADDR_ANY);
94
95 for (client_port = IPPORT_RESERVED - 1;
96 client_port >= IPPORT_RESERVED/2;
97 client_port--)
98 {
99 int result, errcode;
100 client_sai.sin_port = htons (client_port);
101
102 if ((s = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
103 error (1, 0, "cannot create socket: %s",
104 SOCK_STRERROR (SOCK_ERRNO));
105
106 result = bind (s, (struct sockaddr *) &client_sai,
107 sizeof (client_sai));
108 errcode = SOCK_ERRNO;
109 if (result == SOCKET_ERROR)
110 {
111 closesocket (s);
112 if (errcode == WSAEADDRINUSE)
113 continue;
114 else
115 error (1, 0, "cannot bind to socket: %s",
116 SOCK_STRERROR (errcode));
117 }
118
119 result = connect (s, (struct sockaddr *) server_sai,
120 sizeof (*server_sai));
121 errcode = SOCK_ERRNO;
122 if (result == SOCKET_ERROR)
123 {
124 closesocket (s);
125 if (errcode == WSAEADDRINUSE)
126 continue;
127 else
128 error (1, 0, "cannot connect to socket: %s",
129 SOCK_STRERROR (errcode));
130 }
131
132 return s;
133 }
134
135 error (1, 0, "cannot find free port");
136 /* Shut up gcc -Wall. */
137 return s;
138 }
139
140 static int
rcmd_authenticate(int fd,char * locuser,char * remuser,char * command)141 rcmd_authenticate (int fd, char *locuser, char *remuser, char *command)
142 {
143 /* Send them a bunch of information, each terminated by '\0':
144 - secondary stream port number (we don't use this)
145 - username on local machine
146 - username on server machine
147 - command
148 Now, the Ultrix man page says you transmit the username on the
149 server first, but that doesn't seem to work. Transmitting the
150 client username first does. Go figure. The Linux man pages
151 get it right --- hee hee. */
152 if ((send (fd, "0\0", 2, 0) == SOCKET_ERROR)
153 || (send (fd, locuser, strlen (locuser) + 1, 0) == SOCKET_ERROR)
154 || (send (fd, remuser, strlen (remuser) + 1, 0) == SOCKET_ERROR)
155 || (send (fd, command, strlen (command) + 1, 0) == SOCKET_ERROR))
156 error (1, 0, "cannot send authentication info to rshd: %s",
157 SOCK_STRERROR (SOCK_ERRNO));
158
159 /* They sniff our butt, and send us a '\0' character if they
160 like us. */
161 {
162 char c;
163 if (recv (fd, &c, 1, 0) == SOCKET_ERROR)
164 {
165 error (1, 0, "cannot receive authentication info from rshd: %s",
166 SOCK_STRERROR (SOCK_ERRNO));
167 }
168 if (c != '\0')
169 {
170 /* All the junk with USER, LOGNAME, GetUserName, &c, is so
171 confusing that we better give some clue as to what sort
172 of user name we decided on. */
173 error (0, 0, "cannot log in as local user '%s', remote user '%s'",
174 locuser, remuser);
175 error (1, 0, "Permission denied by rshd");
176 }
177 }
178
179 return 0;
180 }
181
182 int
rcmd(const char ** ahost,unsigned short inport,char * locuser,char * remuser,char * cmd,int * fd2p)183 rcmd (const char **ahost,
184 unsigned short inport,
185 char *locuser,
186 char *remuser,
187 char *cmd,
188 int *fd2p)
189 {
190 struct sockaddr_in sai;
191 SOCKET s;
192
193 assert (fd2p == 0);
194
195 if (resolve_address (ahost, &sai) < 0)
196 error (1, 0, "internal error: resolve_address < 0");
197
198 sai.sin_port = htons (inport);
199
200 if ((s = bind_and_connect (&sai)) == INVALID_SOCKET)
201 error (1, 0, "internal error: bind_and_connect < 0");
202
203 if (rcmd_authenticate (s, locuser, remuser, cmd) < 0)
204 error (1, 0, "internal error: rcmd_authenticate < 0");
205
206 return s;
207 }
208