Merge branch 'mirbsd'
[alioth/cvs.git] / windows-NT / rcmd.c
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 #include <sys/socket.h>
24
25 #include <stdio.h>
26 #include <assert.h>
27
28 /* The rest of this file contains the rcmd() code, which is used
29    only by START_SERVER.  The idea for a long-term direction is
30    that this code can be made portable (by using SOCK_ERRNO and
31    so on), and then moved to client.c or someplace it can be
32    shared with the VMS port and any other ports which may want it.  */
33
34
35 static int
36 resolve_address (const char **ahost, struct sockaddr_in *sai)
37 {
38     {
39         unsigned long addr = inet_addr (*ahost);
40
41         if (addr != (unsigned long) -1)
42         {
43             sai->sin_family = AF_INET;
44             sai->sin_addr.s_addr = addr;
45             return 0;
46         }
47     }
48
49     {
50         struct hostent *e = gethostbyname (*ahost);
51
52         if (e)
53         {
54             assert (e->h_addrtype == AF_INET);
55             assert (e->h_addr);
56             *ahost = e->h_name;
57             sai->sin_family = AF_INET;
58             memcpy (&sai->sin_addr, e->h_addr, sizeof (sai->sin_addr));
59             return 0;
60         }
61     }
62
63     error (1, 0, "no such host %s", *ahost);
64     /* Shut up gcc -Wall.  */
65     return 1;
66 }
67
68 static SOCKET
69 bind_and_connect (struct sockaddr_in *server_sai)
70 {
71     SOCKET s;
72     struct sockaddr_in client_sai;
73     u_short client_port;
74
75     client_sai.sin_family = AF_INET;
76     client_sai.sin_addr.s_addr = htonl (INADDR_ANY);
77
78     for (client_port = IPPORT_RESERVED - 1;
79          client_port >= IPPORT_RESERVED/2;
80          client_port--)
81     {
82         int result, errcode;
83         client_sai.sin_port = htons (client_port);
84
85         if ((s = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
86             error (1, 0, "cannot create socket: %s",
87                    SOCK_STRERROR (SOCK_ERRNO));
88
89         result = bind (s, (struct sockaddr *) &client_sai,
90                        sizeof (client_sai));
91         errcode = SOCK_ERRNO;
92         if (result == SOCKET_ERROR)
93         {
94             closesocket (s);
95             if (errcode == WSAEADDRINUSE)
96                 continue;
97             else
98                 error (1, 0, "cannot bind to socket: %s",
99                        SOCK_STRERROR (errcode));
100         }
101
102         result = connect (s, (struct sockaddr *) server_sai,
103                           sizeof (*server_sai));
104         errcode = SOCK_ERRNO;
105         if (result == SOCKET_ERROR)
106         {
107             closesocket (s);
108             if (errcode == WSAEADDRINUSE)
109                 continue;
110             else
111                 error (1, 0, "cannot connect to socket: %s",
112                        SOCK_STRERROR (errcode));
113         }
114
115         return s;
116     }
117
118     error (1, 0, "cannot find free port");
119     /* Shut up gcc -Wall.  */
120         return s;
121 }
122
123 static int
124 rcmd_authenticate (int fd, char *locuser, char *remuser, char *command)
125 {
126     /* Send them a bunch of information, each terminated by '\0':
127        - secondary stream port number (we don't use this)
128        - username on local machine
129        - username on server machine
130        - command
131        Now, the Ultrix man page says you transmit the username on the
132        server first, but that doesn't seem to work.  Transmitting the
133        client username first does.  Go figure.  The Linux man pages
134        get it right --- hee hee.  */
135     if ((send (fd, "0\0", 2, 0) == SOCKET_ERROR)
136         || (send (fd, locuser, strlen (locuser) + 1, 0) == SOCKET_ERROR)
137         || (send (fd, remuser, strlen (remuser) + 1, 0) == SOCKET_ERROR)
138         || (send (fd, command, strlen (command) + 1, 0) == SOCKET_ERROR))
139         error (1, 0, "cannot send authentication info to rshd: %s",
140                SOCK_STRERROR (SOCK_ERRNO));
141
142     /* They sniff our butt, and send us a '\0' character if they
143        like us.  */
144     {
145         char c;
146         if (recv (fd, &c, 1, 0) == SOCKET_ERROR)
147         {
148             error (1, 0, "cannot receive authentication info from rshd: %s",
149                    SOCK_STRERROR (SOCK_ERRNO));
150         }
151         if (c != '\0')
152         {
153             /* All the junk with USER, LOGNAME, GetUserName, &c, is so
154                confusing that we better give some clue as to what sort
155                of user name we decided on.  */
156             error (0, 0, "cannot log in as local user '%s', remote user '%s'",
157                    locuser, remuser);
158             error (1, 0, "Permission denied by rshd");
159         }
160     }
161
162     return 0;
163 }
164
165 int
166 rcmd (const char **ahost,
167       unsigned short inport,
168       char *locuser,
169       char *remuser,
170       char *cmd,
171       int *fd2p)
172 {
173     struct sockaddr_in sai;
174     SOCKET s;
175
176     assert (fd2p == 0);
177
178     if (resolve_address (ahost, &sai) < 0)
179         error (1, 0, "internal error: resolve_address < 0");
180
181     sai.sin_port = htons (inport);
182
183     if ((s = bind_and_connect (&sai)) == INVALID_SOCKET)
184         error (1, 0, "internal error: bind_and_connect < 0");
185
186     if (rcmd_authenticate (s, locuser, remuser, cmd) < 0)
187         error (1, 0, "internal error: rcmd_authenticate < 0");
188
189     return s;
190 }