xref: /openbsd/gnu/usr.bin/cvs/windows-NT/rcmd.c (revision 404b540a)
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
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
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
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
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