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