11e72d8d2Sderaadt /* rcmd.c --- execute a command on a remote host from Windows NT
22770ece5Stholo
32770ece5Stholo This program is free software; you can redistribute it and/or modify
42770ece5Stholo it under the terms of the GNU General Public License as published by
52770ece5Stholo the Free Software Foundation; either version 2, or (at your option)
62770ece5Stholo any later version.
72770ece5Stholo
82770ece5Stholo This program is distributed in the hope that it will be useful,
92770ece5Stholo but WITHOUT ANY WARRANTY; without even the implied warranty of
102770ece5Stholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
112770ece5Stholo GNU General Public License for more details.
122770ece5Stholo
131e72d8d2Sderaadt Jim Blandy <jimb@cyclic.com> --- August 1995 */
141e72d8d2Sderaadt
15*2286d8edStholo #include "cvs.h"
16*2286d8edStholo #include "rcmd.h"
17*2286d8edStholo
181e72d8d2Sderaadt #include <io.h>
191e72d8d2Sderaadt #include <fcntl.h>
201e72d8d2Sderaadt #include <malloc.h>
211e72d8d2Sderaadt #include <errno.h>
222770ece5Stholo
232770ece5Stholo #ifdef HAVE_WINSOCK_H
241e72d8d2Sderaadt #include <winsock.h>
252770ece5Stholo #else
262770ece5Stholo #include <sys/types.h>
272770ece5Stholo #include <sys/socket.h>
282770ece5Stholo #include <netinet/in.h>
292770ece5Stholo #include <netdb.h>
302770ece5Stholo typedef int SOCKET;
312770ece5Stholo #define closesocket close
322770ece5Stholo #define SOCK_ERRNO errno
332770ece5Stholo #define SOCK_STRERROR strerror
342770ece5Stholo /* Probably would be cleaner to just use EADDRINUSE, as NT has that too. */
352770ece5Stholo #define WSAEADDRINUSE EADDRINUSE
362770ece5Stholo /* Probably would be cleaner to just check for < 0. Might want to
372770ece5Stholo double-check that doing so would seem to work on NT. */
382770ece5Stholo #define SOCKET_ERROR -1
392770ece5Stholo #define INVALID_SOCKET -1
402770ece5Stholo #endif
412770ece5Stholo
4213571821Stholo #include <stdio.h>
431e72d8d2Sderaadt #include <assert.h>
441e72d8d2Sderaadt
45b6c02222Stholo /* The rest of this file contains the rcmd() code, which is used
46b6c02222Stholo only by START_SERVER. The idea for a long-term direction is
47b6c02222Stholo that this code can be made portable (by using SOCK_ERRNO and
48b6c02222Stholo so on), and then moved to client.c or someplace it can be
49b6c02222Stholo shared with the VMS port and any other ports which may want it. */
50b6c02222Stholo
511e72d8d2Sderaadt
521e72d8d2Sderaadt static int
resolve_address(const char ** ahost,struct sockaddr_in * sai)531e72d8d2Sderaadt resolve_address (const char **ahost, struct sockaddr_in *sai)
541e72d8d2Sderaadt {
551e72d8d2Sderaadt {
561e72d8d2Sderaadt unsigned long addr = inet_addr (*ahost);
571e72d8d2Sderaadt
581e72d8d2Sderaadt if (addr != (unsigned long) -1)
591e72d8d2Sderaadt {
601e72d8d2Sderaadt sai->sin_family = AF_INET;
611e72d8d2Sderaadt sai->sin_addr.s_addr = addr;
621e72d8d2Sderaadt return 0;
631e72d8d2Sderaadt }
641e72d8d2Sderaadt }
651e72d8d2Sderaadt
661e72d8d2Sderaadt {
671e72d8d2Sderaadt struct hostent *e = gethostbyname (*ahost);
681e72d8d2Sderaadt
691e72d8d2Sderaadt if (e)
701e72d8d2Sderaadt {
711e72d8d2Sderaadt assert (e->h_addrtype == AF_INET);
721e72d8d2Sderaadt assert (e->h_addr);
731e72d8d2Sderaadt *ahost = e->h_name;
741e72d8d2Sderaadt sai->sin_family = AF_INET;
751e72d8d2Sderaadt memcpy (&sai->sin_addr, e->h_addr, sizeof (sai->sin_addr));
761e72d8d2Sderaadt return 0;
771e72d8d2Sderaadt }
781e72d8d2Sderaadt }
791e72d8d2Sderaadt
80b6c02222Stholo error (1, 0, "no such host %s", *ahost);
812770ece5Stholo /* Shut up gcc -Wall. */
822770ece5Stholo return 1;
831e72d8d2Sderaadt }
841e72d8d2Sderaadt
851e72d8d2Sderaadt static SOCKET
bind_and_connect(struct sockaddr_in * server_sai)861e72d8d2Sderaadt bind_and_connect (struct sockaddr_in *server_sai)
871e72d8d2Sderaadt {
881e72d8d2Sderaadt SOCKET s;
891e72d8d2Sderaadt struct sockaddr_in client_sai;
901e72d8d2Sderaadt u_short client_port;
911e72d8d2Sderaadt
921e72d8d2Sderaadt client_sai.sin_family = AF_INET;
931e72d8d2Sderaadt client_sai.sin_addr.s_addr = htonl (INADDR_ANY);
941e72d8d2Sderaadt
951e72d8d2Sderaadt for (client_port = IPPORT_RESERVED - 1;
961e72d8d2Sderaadt client_port >= IPPORT_RESERVED/2;
971e72d8d2Sderaadt client_port--)
981e72d8d2Sderaadt {
99b6c02222Stholo int result, errcode;
1001e72d8d2Sderaadt client_sai.sin_port = htons (client_port);
1011e72d8d2Sderaadt
1021e72d8d2Sderaadt if ((s = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
103b6c02222Stholo error (1, 0, "cannot create socket: %s",
104b6c02222Stholo SOCK_STRERROR (SOCK_ERRNO));
1051e72d8d2Sderaadt
1061e72d8d2Sderaadt result = bind (s, (struct sockaddr *) &client_sai,
1071e72d8d2Sderaadt sizeof (client_sai));
108b6c02222Stholo errcode = SOCK_ERRNO;
1091e72d8d2Sderaadt if (result == SOCKET_ERROR)
1101e72d8d2Sderaadt {
1111e72d8d2Sderaadt closesocket (s);
112b6c02222Stholo if (errcode == WSAEADDRINUSE)
1131e72d8d2Sderaadt continue;
1141e72d8d2Sderaadt else
115b6c02222Stholo error (1, 0, "cannot bind to socket: %s",
116b6c02222Stholo SOCK_STRERROR (errcode));
1171e72d8d2Sderaadt }
1181e72d8d2Sderaadt
1191e72d8d2Sderaadt result = connect (s, (struct sockaddr *) server_sai,
1201e72d8d2Sderaadt sizeof (*server_sai));
121b6c02222Stholo errcode = SOCK_ERRNO;
1221e72d8d2Sderaadt if (result == SOCKET_ERROR)
1231e72d8d2Sderaadt {
1241e72d8d2Sderaadt closesocket (s);
125b6c02222Stholo if (errcode == WSAEADDRINUSE)
1261e72d8d2Sderaadt continue;
1271e72d8d2Sderaadt else
128b6c02222Stholo error (1, 0, "cannot connect to socket: %s",
129b6c02222Stholo SOCK_STRERROR (errcode));
1301e72d8d2Sderaadt }
1311e72d8d2Sderaadt
1321e72d8d2Sderaadt return s;
1331e72d8d2Sderaadt }
1341e72d8d2Sderaadt
135b6c02222Stholo error (1, 0, "cannot find free port");
1362770ece5Stholo /* Shut up gcc -Wall. */
1372770ece5Stholo return s;
1381e72d8d2Sderaadt }
1391e72d8d2Sderaadt
1401e72d8d2Sderaadt static int
rcmd_authenticate(int fd,char * locuser,char * remuser,char * command)1411e72d8d2Sderaadt rcmd_authenticate (int fd, char *locuser, char *remuser, char *command)
1421e72d8d2Sderaadt {
1431e72d8d2Sderaadt /* Send them a bunch of information, each terminated by '\0':
1441e72d8d2Sderaadt - secondary stream port number (we don't use this)
1451e72d8d2Sderaadt - username on local machine
1461e72d8d2Sderaadt - username on server machine
1471e72d8d2Sderaadt - command
1481e72d8d2Sderaadt Now, the Ultrix man page says you transmit the username on the
1491e72d8d2Sderaadt server first, but that doesn't seem to work. Transmitting the
1501e72d8d2Sderaadt client username first does. Go figure. The Linux man pages
1511e72d8d2Sderaadt get it right --- hee hee. */
15250bf276cStholo if ((send (fd, "0\0", 2, 0) == SOCKET_ERROR)
15350bf276cStholo || (send (fd, locuser, strlen (locuser) + 1, 0) == SOCKET_ERROR)
15450bf276cStholo || (send (fd, remuser, strlen (remuser) + 1, 0) == SOCKET_ERROR)
15550bf276cStholo || (send (fd, command, strlen (command) + 1, 0) == SOCKET_ERROR))
156b6c02222Stholo error (1, 0, "cannot send authentication info to rshd: %s",
157b6c02222Stholo SOCK_STRERROR (SOCK_ERRNO));
1581e72d8d2Sderaadt
1591e72d8d2Sderaadt /* They sniff our butt, and send us a '\0' character if they
1601e72d8d2Sderaadt like us. */
1611e72d8d2Sderaadt {
1621e72d8d2Sderaadt char c;
163b6c02222Stholo if (recv (fd, &c, 1, 0) == SOCKET_ERROR)
1641e72d8d2Sderaadt {
165b6c02222Stholo error (1, 0, "cannot receive authentication info from rshd: %s",
166b6c02222Stholo SOCK_STRERROR (SOCK_ERRNO));
167b6c02222Stholo }
168b6c02222Stholo if (c != '\0')
169b6c02222Stholo {
170*2286d8edStholo /* All the junk with USER, LOGNAME, GetUserName, &c, is so
171*2286d8edStholo confusing that we better give some clue as to what sort
172*2286d8edStholo of user name we decided on. */
173*2286d8edStholo error (0, 0, "cannot log in as local user '%s', remote user '%s'",
174*2286d8edStholo locuser, remuser);
175b6c02222Stholo error (1, 0, "Permission denied by rshd");
1761e72d8d2Sderaadt }
1771e72d8d2Sderaadt }
1781e72d8d2Sderaadt
1791e72d8d2Sderaadt return 0;
1801e72d8d2Sderaadt }
1811e72d8d2Sderaadt
1821e72d8d2Sderaadt int
rcmd(const char ** ahost,unsigned short inport,char * locuser,char * remuser,char * cmd,int * fd2p)1831e72d8d2Sderaadt rcmd (const char **ahost,
1841e72d8d2Sderaadt unsigned short inport,
1851e72d8d2Sderaadt char *locuser,
1861e72d8d2Sderaadt char *remuser,
1871e72d8d2Sderaadt char *cmd,
1881e72d8d2Sderaadt int *fd2p)
1891e72d8d2Sderaadt {
1901e72d8d2Sderaadt struct sockaddr_in sai;
1911e72d8d2Sderaadt SOCKET s;
1921e72d8d2Sderaadt
1931e72d8d2Sderaadt assert (fd2p == 0);
1941e72d8d2Sderaadt
1951e72d8d2Sderaadt if (resolve_address (ahost, &sai) < 0)
196b6c02222Stholo error (1, 0, "internal error: resolve_address < 0");
1971e72d8d2Sderaadt
1981e72d8d2Sderaadt sai.sin_port = htons (inport);
1991e72d8d2Sderaadt
2001e72d8d2Sderaadt if ((s = bind_and_connect (&sai)) == INVALID_SOCKET)
201b6c02222Stholo error (1, 0, "internal error: bind_and_connect < 0");
2021e72d8d2Sderaadt
20350bf276cStholo if (rcmd_authenticate (s, locuser, remuser, cmd) < 0)
204b6c02222Stholo error (1, 0, "internal error: rcmd_authenticate < 0");
2051e72d8d2Sderaadt
20650bf276cStholo return s;
2071e72d8d2Sderaadt }
208