1 /*
2  * hwrcon.c
3  * $Id: hwrcon.c 5787 2017-01-03 22:22:34Z sezero $
4  *
5  * HWRCON 1.2 HexenWorld Remote CONsole
6  * Idea based on RCon 1.1 by Michael Dwyer/N0ZAP (18-May-1998).
7  * Made to work with HexenWorld using code from the HexenWorld
8  * engine (C) Raven Software and ID Software.
9  * Copyright (C) 1998 Michael Dwyer <mdwyer@holly.colostate.edu>
10  * Copyright (C) 2006-2011 O. Sezer <sezero@users.sourceforge.net>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or (at
15  * your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20  *
21  * See the GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  */
27 
28 
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdarg.h>
34 
35 #include "arch_def.h"
36 #include "compiler.h"
37 
38 #define	COMPILE_TIME_ASSERT(name, x)	\
39 	typedef int dummy_ ## name[(x) * 2 - 1]
40 
41 #include "net_sys.h"
42 #include "qsnprint.h"
43 
44 /*****************************************************************************/
45 
46 typedef struct
47 {
48 	unsigned char	ip[4];
49 	unsigned short	port;
50 	unsigned short	pad;
51 } netadr_t;
52 
53 #if defined(PLATFORM_AMIGA)
54 struct Library	*SocketBase;
55 #endif
56 #if defined(PLATFORM_WINDOWS)
57 #include "wsaerror.h"
58 static WSADATA	winsockdata;
59 #endif
60 static sys_socket_t	socketfd = INVALID_SOCKET;
61 
62 FUNC_NORETURN void Sys_Error (const char *error, ...) FUNC_PRINTF(1,2);
63 #ifdef __WATCOMC__
64 #pragma aux Sys_Error aborts;
65 #endif
66 
67 /*****************************************************************************/
68 
NetadrToSockadr(const netadr_t * a,struct sockaddr_in * s)69 static void NetadrToSockadr (const netadr_t *a, struct sockaddr_in *s)
70 {
71 	memset (s, 0, sizeof(*s));
72 	s->sin_family = AF_INET;
73 
74 	memcpy (&s->sin_addr, a->ip, 4);
75 	s->sin_port = a->port;
76 }
77 
SockadrToNetadr(const struct sockaddr_in * s,netadr_t * a)78 static void SockadrToNetadr (const struct sockaddr_in *s, netadr_t *a)
79 {
80 	memcpy (a->ip, &s->sin_addr, 4);
81 	a->port = s->sin_port;
82 }
83 
NET_AdrToString(const netadr_t * a)84 const char *NET_AdrToString (const netadr_t *a)
85 {
86 	static	char	s[64];
87 
88 	sprintf (s, "%i.%i.%i.%i:%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3],
89 							ntohs(a->port));
90 
91 	return s;
92 }
93 
NET_StringToAdr(const char * s,netadr_t * a)94 static int NET_StringToAdr (const char *s, netadr_t *a)
95 {
96 	struct hostent		*h;
97 	struct sockaddr_in	sadr;
98 	char	*colon;
99 	char	copy[128];
100 
101 	memset (&sadr, 0, sizeof(sadr));
102 	sadr.sin_family = AF_INET;
103 	sadr.sin_port = 0;
104 
105 	strncpy (copy, s, sizeof(copy) - 1);
106 	copy[sizeof(copy) - 1] = '\0';
107 	/* strip off a trailing :port if present */
108 	for (colon = copy; *colon; colon++)
109 	{
110 		if (*colon == ':')
111 		{
112 			*colon = 0;
113 			sadr.sin_port = htons((short)atoi(colon+1));
114 		}
115 	}
116 
117 	if (copy[0] >= '0' && copy[0] <= '9')
118 	{
119 		sadr.sin_addr.s_addr = inet_addr(copy);
120 	}
121 	else
122 	{
123 		h = gethostbyname (copy);
124 		if (!h)
125 			return 0;
126 		sadr.sin_addr.s_addr = *(in_addr_t *)h->h_addr_list[0];
127 	}
128 
129 	SockadrToNetadr (&sadr, a);
130 
131 	return 1;
132 }
133 
NET_Init(void)134 static void NET_Init (void)
135 {
136 #if defined(PLATFORM_WINDOWS)
137 	int err = WSAStartup(MAKEWORD(1,1), &winsockdata);
138 	if (err != 0)
139 		Sys_Error ("Winsock initialization failed (%s)", socketerror(err));
140 #endif	/* PLATFORM_WINDOWS */
141 #if defined(PLATFORM_OS2) && !defined(__EMX__)
142 	if (sock_init() < 0)
143 		Sys_Error ("Can't initialize IBM OS/2 sockets");
144 #endif	/* OS/2 */
145 #ifdef PLATFORM_AMIGA
146 	SocketBase = OpenLibrary("bsdsocket.library", 0);
147 	if (!SocketBase)
148 		Sys_Error ("Can't open bsdsocket.library.");
149 #endif	/* PLATFORM_AMIGA */
150 #if defined(PLATFORM_DOS) && defined(USE_WATT32)
151 	int i, err;
152 
153 /*	dbug_init();*/
154 	i = _watt_do_exit;
155 	_watt_do_exit = 0;
156 	err = sock_init();
157 	_watt_do_exit = i;
158 	if (err != 0)
159 		Sys_Error ("WATTCP initialization failed (%s)", sock_init_err(err));
160 #endif	/* WatTCP  */
161 }
162 
NET_Shutdown(void)163 static void NET_Shutdown (void)
164 {
165 	if (socketfd != INVALID_SOCKET)
166 	{
167 		closesocket (socketfd);
168 		socketfd = INVALID_SOCKET;
169 	}
170 #if defined(PLATFORM_WINDOWS)
171 	WSACleanup ();
172 #endif
173 #ifdef PLATFORM_AMIGA
174 	if (SocketBase)
175 	{
176 		CloseLibrary(SocketBase);
177 		SocketBase = NULL;
178 	}
179 #endif
180 }
181 
182 /*****************************************************************************/
183 
Sys_Error(const char * error,...)184 void Sys_Error (const char *error, ...)
185 {
186 	va_list		argptr;
187 	char		text[1024];
188 
189 	va_start (argptr,error);
190 	q_vsnprintf (text, sizeof (text), error,argptr);
191 	va_end (argptr);
192 
193 	NET_Shutdown ();
194 
195 	printf ("\nERROR: %s\n\n", text);
196 
197 	exit (1);
198 }
199 
200 /*****************************************************************************/
201 
202 #define	VER_HWRCON_MAJ		1
203 #define	VER_HWRCON_MID		2
204 #define	VER_HWRCON_MIN		7
205 
206 #define	PORT_SERVER		26950
207 
208 #define	MAX_RCON_PACKET		256
209 
210 static const unsigned char rcon_hdr[10] =
211 	{ 255, 255, 255, 255,
212 	  'r', 'c', 'o', 'n', ' ', '\0' };
213 
214 static unsigned char	packet[MAX_RCON_PACKET];
215 
main(int argc,char * argv[])216 int main (int argc, char *argv[])
217 {
218 	int		i;
219 	int		len, size;
220 	netadr_t		ipaddress;
221 	struct sockaddr_in	hostaddress;
222 	int		err;
223 	unsigned char	*p;
224 
225 	printf ("HWRCON %d.%d.%d\n", VER_HWRCON_MAJ, VER_HWRCON_MID, VER_HWRCON_MIN);
226 
227 /* command line sanity checking */
228 	if (argc < 3)
229 	{
230 		printf ("Usage: %s <address>[:port] <password> commands ...\n", argv[0]);
231 		exit (1);
232 	}
233 
234 /* init OS-specific network stuff */
235 	NET_Init ();
236 
237 /* decode the address and port */
238 	if (!NET_StringToAdr(argv[1], &ipaddress))
239 		Sys_Error ("Unable to resolve address %s", argv[1]);
240 	if (ipaddress.port == 0)
241 		ipaddress.port = htons(PORT_SERVER);
242 	NetadrToSockadr(&ipaddress, &hostaddress);
243 	printf ("Using address %s\n", NET_AdrToString(&ipaddress));
244 
245 /* prepare the header: \377\377\377\377rcon<space> */
246 	p = &packet[0];
247 
248 /* we need not use HuffEncode, therefore we must put an additional
249  * 0xff at the beginning for our message to be read by server.  */
250 	*p++ = 0xff;
251 
252 	memcpy (p, rcon_hdr, sizeof(rcon_hdr));
253 	p += sizeof(rcon_hdr) - 1;
254 
255 /* concat all the excess command line stuff into a single string */
256 	for (i = 2; i < argc; i++)
257 	{
258 		len = strlen(argv[i]);
259 		if (p - &packet[0] + len + 1 > MAX_RCON_PACKET - 1)
260 			Sys_Error ("Command too long");
261 		memcpy (p, argv[i], len);
262 		p += len;
263 		*p++ = ' ';
264 	}
265 /* kill the trailing space */
266 	p[-1] = '\0';
267 
268 	len = p - &packet[0];
269 
270 /* open the socket */
271 	socketfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
272 	if (socketfd == INVALID_SOCKET)
273 	{
274 		err = SOCKETERRNO;
275 		Sys_Error ("Couldn't open socket: %s", socketerror(err));
276 	}
277 
278 /* send the packet */
279 	size = sendto(socketfd, (char *)packet, len, 0,
280 			(struct sockaddr *)&hostaddress, sizeof(hostaddress));
281 
282 /* see if it worked */
283 	if (size != len)
284 	{
285 		err = SOCKETERRNO;
286 		Sys_Error ("Sendto failed: %s", socketerror(err));
287 	}
288 
289 /* clean up */
290 	NET_Shutdown ();
291 	return 0;
292 }
293 
294