#include "sniffer.h"

/// \fn Sniffer::Sniffer()
/// \~English
/// \brief Constructor, does nothing
/// \~Spanish
/// \brief Constructor, no hace nada
Sniffer::Sniffer(){
}

/// \fn Sniffer::Sniffer(QWaitCondition *pw, QMutex *mx, int *ps)
/// \~English
/// \brief Sets the mutexes for the inter communication between
/// the sniffer and the GUI.
/// \param pw used to pause the capture of packets
/// \param mx used to protect critical region
/// \param ps flag to pause the packet capture
/// \~Spanish
/// \brief Ajusta los mutexes para la inter comunicacion entre
/// el sniffer y el GUI.
/// \param pw se usa para pausar la captura de paquetes
/// \param mx usado para proteger la region critica
/// \param ps bandera para pausar la captura de paquetes
Sniffer::Sniffer(QWaitCondition *pw, QMutex *mx, int *ps){
    wait_pause = pw ;
    mutex = mx ;
    pause = ps ;
}

/// \fn Sniffer::~Sniffer()
/// \~English
/// \brief Destructor, does nothing
/// \~Spanish
/// \brief Destructor, no hace nada
Sniffer::~Sniffer(){

}
/// \fn void Sniffer::process()
/// \~English
/// \brief The sniffer is run as a separate thread and the function process
/// is the main function of the thread. It is the sniffer.
/// \~Spanish
/// \brief El sniffer se corre como un hilo por separado y la funcion process
/// es la funcion principal de el hilo.  Es el sniffer.
void Sniffer::process()  Q_DECL_OVERRIDE {

    char *dev = NULL;   /* capture device name */
                        /* nombre del dispositivo para capturar */
    char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
                                    /* buffer de error */
    pcap_t *handle;    /* packet capture handle */
                       /* manejador de captura de paquete */
    struct pcap_pkthdr header;	/* The header that pcap gives us */
                                /* El header devuelto por pcap */
    const u_char *packet;		/* The actual packet */
                                /* El paquete como tal */
    int num_packets = 100;   /* number of packets to capture */
                             /* numero de paquetes a capturar */
    unsigned long iptu_count = 0 ; /* counter of tcp|udp packets */
                                   /* contador de paquetes tcp|udp */

    /* print capture info */
    /* imprime informacion de captura */
    printf("Device: %s\n", dev);
    printf("Number of packets: %d\n", num_packets);

    /* open capture device */
    /* abre el dispositivo para capturar */
    handle = pcap_open_live(device.c_str(), SNAP_LEN, 1, 1000, errbuf);
    if (handle == NULL) {
     fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
     exit(EXIT_FAILURE);
    }

    /* make sure we're capturing on an Ethernet device */
    /* se asegura que se esta caputando en un dispositivo de Ethernet */
    if (pcap_datalink(handle) != DLT_EN10MB) {
     fprintf(stderr, "%s is not an Ethernet\n", dev);
     exit(EXIT_FAILURE);
    }

    /* Check if device can be set to monitor */
    /* Revisa si el device puede ser ajustado a modo monitoreo */
    if (pcap_can_set_rfmon(handle)){
        pcap_set_rfmon(handle, 1) ;
        pcap_activate(handle) ;
    }

    /* now we can set our callback function in an infinite loop */
    /* ahora podemos ajustar la funcion de llamada devuelta en un cyclo infinito */
    for(;;){
        mutex->lock() ;
        if(*pause){
            wait_pause->wait(mutex) ;
            // Stop capturing, next we need to restart
            // Para de capturar, next we need to restart
            pcap_close(handle) ;

            /* open capture device */
            /* abre el dispositivo de captura */
            handle = pcap_open_live(device.c_str(), SNAP_LEN, 1, 1000, errbuf);
            if (handle == NULL) {
             fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
             exit(EXIT_FAILURE);
            }

            /* make sure we're capturing on an Ethernet device */
            /* se asegura que estamos capturando en un dispositivo Ethernet */
            if (pcap_datalink(handle) != DLT_EN10MB) {
             fprintf(stderr, "%s is not an Ethernet\n", dev);
             exit(EXIT_FAILURE);
            }
        }
        mutex->unlock();

        packet = pcap_next(handle, &header) ;
        /* If a packet is received then process the packet */
        /* Si se recibe un paquete entonces se procesa */
        if(packet)
            got_packet(&header, packet);

        /* If the packet will be displayed then send singal to GUI */
        /* Si el paquete va a ser desplegado se envia senal al GUI */
        if(iptu_count < packet_list.size()){
           iptu_count = packet_list.size() ;
           emit resultReady((unsigned long) packet_list.size());
        }
    }


    /* cleanup */
    /* limpio */
    pcap_close(handle);

    /* Termino de capturar */
    printf("\nCapture complete.\n");
    printf("PACKETS IN LIST %lu\n", packet_list.size()) ;
}

/*
 * print data in rows of 16 bytes: offset   hex   ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
/// \fn void Sniffer::print_hex_ascii_line(const u_char *payload, int len, int offset)
/// \~English
/// \brief Prints to std output the a payload in ascii
/// \param payload payload of the packet
/// \param len  length of the packet
/// \param offset offset
/// \~Spanish
/// \brief Imprime la carga en ascii a std output
/// \param payload carga del paquete
/// \param len largo de la carga
/// \param offset distancia
void
Sniffer::print_hex_ascii_line(const u_char *payload, int len, int offset)
{

 int i;
 int gap;
 const u_char *ch;

 /* offset */
 printf("%05d   ", offset);

 /* hex */
 ch = payload;
 for(i = 0; i < len; i++) {
  printf("%02x ", *ch);
  ch++;
  /* print extra space after 8th byte for visual aid */
  if (i == 7)
   printf(" ");
 }
 /* print space to handle line less than 8 bytes */
 if (len < 8)
  printf(" ");

 /* fill hex gap with spaces if not full line */
 if (len < 16) {
  gap = 16 - len;
  for (i = 0; i < gap; i++) {
   printf("   ");
  }
 }
 printf("   ");

 /* ascii (if printable) */
 ch = payload;
 for(i = 0; i < len; i++) {
  if (isprint(*ch))
   printf("%c", *ch);
  else
   printf(".");
  ch++;
 }

 printf("\n");

return;
}

/// \fn string Sniffer::format_hex_ascii_line(const char *payload, int len, int offset)
/// \~English
/// \brief Return string with the bytes of a payload line in ascii
/// \param payload payload of the packet
/// \param len  length of the packet
/// \param offset offset of packet
/// \return a string with the bytes of a payload line in ascii
/// \~Spanish
/// \brief Devuelve una cadena de caracteres con los bytes de una linea
/// de la carga en ascii
/// \param payload carga del paquete
/// \param len largo del paquete
/// \param offset distancia del paquete
/// \return a string with the bytes of a payload line in ascii
string
Sniffer::format_hex_ascii_line(const char *payload, int len, int offset)
{

 int i;
 int gap;
 const char *ch;
 string line ;
 char temp[15] ;
 /* offset */
 sprintf(temp, "%05d   ", offset);
 line.append(temp) ;
 /* hex */
 ch = payload;
 for(i = 0; i < len; i++) {
  sprintf(temp, "%02x ", (unsigned char) *ch);
  line.append(temp) ;
  ch++;
  /* print extra space after 8th byte for visual aid */
  if (i == 7)
   line.append(" ") ;
 }
 /* print space to handle line less than 8 bytes */
 if (len < 8)
  line.append(" ") ;

 /* fill hex gap with spaces if not full line */
 if (len < 16) {
  gap = 16 - len;
  for (i = 0; i < gap; i++) {
   line.append("   ") ;
  }
 }
 line.append("   ");

 /* ascii (if printable) */
 ch = payload;
 for(i = 0; i < len; i++) {
  if (isprint(*ch))
   line += (unsigned char) *ch ;
  else
   line.append(".") ;
  ch++;
 }

 line.append("\n") ;
 return line ;
}
/*
 * print packet payload data (avoid printing binary data)
 */
/// \fn void Sniffer::print_payload(const u_char *payload, int len)
/// \~English
/// \brief Prints the payload in ascii
/// \param payload payload of the packet
/// \param len payload length
/// \~Spanish
/// \brief Imprime la carga en ascii
/// \param payload carga del paquete
/// \param len largo de la carga
void Sniffer::print_payload(const u_char *payload, int len)
{

 int len_rem = len;
 int line_width = 16;   /* number of bytes per line */
                        /* numero de bytes por linea */
 int line_len;
 int offset = 0;     /* zero-based offset counter */
                     /* basado a cero, contador de compensacion */
 const u_char *ch = payload;

 if (len <= 0)
  return;

 /* data fits on one line */
 /* data cabe en una linea */
 if (len <= line_width) {
  print_hex_ascii_line(ch, len, offset);
  return;
 }

 /* data spans multiple lines */
 /* data cubre multiples lineas */
 for ( ;; ) {
  /* compute current line length */
  /* computa el largo de la linea corriente */
  line_len = line_width % len_rem;
  /* print line */
  /* imprime linea */
  print_hex_ascii_line(ch, line_len, offset);
  /* compute total remaining */
  /* computa total que queda */
  len_rem = len_rem - line_len;
  /* shift pointer to remaining bytes to print */
  /* mueve el apuntado a los bytes que quedan para imprimir */
  ch = ch + line_len;
  /* add offset */
  /* anade compensacion */
  offset = offset + line_width;
  /* check if we have line width chars or less */
  /* revisa si tenemos una linea con ancho completo o menos */
  if (len_rem <= line_width) {
   /* print last line and get out */
   /* imprime la ultima linea y sale */
   print_hex_ascii_line(ch, len_rem, offset);
   break;
  }
 }

return;
}

/// \fn string Sniffer::format_payload(const char *payload, int len)
/// \~English
/// \brief Formats the payload from a byte stream into a string of ascci
/// \param payload payload of the packet
/// \param len length of the packet
/// \return the payload in a string of ascii
/// \~Spanish
/// \brief Formatea la carga de un torrente de bytes a un cadena de
/// caracteres ascii
/// \param payload carga del paquete
/// \param len largo del paquete
/// \return la carga en una cadena de caracteres ascii
string Sniffer::format_payload(const char *payload, int len)
{

 int len_rem = len;
 int line_width = 16;   /* number of bytes per line */
 int line_len;
 int offset = 0;     /* zero-based offset counter */
 const char *ch = payload;
 string formated ;

 if (len <= 0)
  return "";

 /* data fits on one line */
 if (len <= line_width) {
  formated += format_hex_ascii_line(ch, len, offset);
  return "";
 }

 /* data spans multiple lines */
 for ( ;; ) {
  /* compute current line length */
  line_len = line_width % len_rem;
  /* print line */
  formated += format_hex_ascii_line(ch, line_len, offset);
  /* compute total remaining */
  len_rem = len_rem - line_len;
  /* shift pointer to remaining bytes to print */
  ch = ch + line_len;
  /* add offset */
  offset = offset + line_width;
  /* check if we have line width chars or less */
  if (len_rem <= line_width) {
   /* print last line and get out */
   formated += format_hex_ascii_line(ch, len_rem, offset);
   break;
  }
 }

return formated;
}

/// \fn void Sniffer::got_packet(const struct pcap_pkthdr *, const u_char *packet)
/// \~English
/// \brief Disects the received packet. Takes out the info needed.
/// \param pcap_pkthdr header of the pcap packet
/// \param packet recieved packet
/// \~Spanish
/// \brief Secciona el paquete recibido. Extrae la informacion necesaria.
/// \param pcap_pkthdr encabezado del paquete de pcap
/// \param packet paquete recibido
void
Sniffer::got_packet(const struct pcap_pkthdr *, const u_char *packet)
{

 /* declare pointers to packet headers */
 /* declara apuntadores a los headers del paquete. */

 const struct sniff_ethernet *ethernet;  /* The Ethernet header */
                                         /* El encabezado de  Ethernet */
 const struct sniff_ip *ip;              /* The IP header */
                                         /* Encabezado de IP */
 const struct sniff_tcp *tcp;            /* The TCP header */
                                         /* Encabezado de TCP */
 const struct sniff_udp *udp ;           /* The UDP header */
                                         /* Encabezado de UDP */
 const u_char *payload;                  /* Packet payload */
                                         /* Carga del paquete */
 ip_packet ip_pkt ;                      /* IP Packet */
                                         /* Paquete de IP */

 int size_ip;       // to store the size of IP
                    // para guardar el tamano de IP
 int size_tcp;      // to store the size of tcp
                    // para guardar el tamano de TCP
 int size_payload;  // to store the size of payload
                    // para guardar el tamano del payload


 ethernet = (struct sniff_ethernet*)(packet);


 /* define/compute IP header offset */
 /* define/computa donde empieza el header de IP */
 ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);

 size_ip = IP_HL(ip)*4;

 if (size_ip < 20) {
  // qDebug() << "   * Invalid IP header length: " << size_ip <<" bytes\n" ;
  printf("   * Invalid IP header length: %u bytes\n", size_ip);
  return ;
 }
 /* print source and destination IP addresses */
 /* imprime las direcciones fuente y destino de IP */
 printf("       From: %s\n", inet_ntoa(ip->ip_src));
 printf("         To: %s\n", inet_ntoa(ip->ip_dst));

 ip_pkt.setEtherSHost((u_char *)ethernet->ether_shost) ;
 ip_pkt.setEtherDHost((u_char *)ethernet->ether_dhost);
 ip_pkt.setIPSAddress(inet_ntoa(ip->ip_src));
 ip_pkt.setIPDAddress(inet_ntoa(ip->ip_dst));
 ip_pkt.setIPProto(ip->ip_p);

 /* determine protocol */
 /* determina el protocolo */
 switch(ip->ip_p) {
  case IPPROTO_TCP:
     printf("   Protocol: TCP\n");
     /* define/compute tcp header offset */
     /* define/computa el donde empieza el encabezado tcp */
     tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
     size_tcp = TH_OFF(tcp)*4;
     if (size_tcp < 20) {
      printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
      return;
     }
     ip_pkt.setIPSPort(ntohs(tcp->th_sport)) ;
     ip_pkt.setIPDPort(ntohs(tcp->th_dport)) ;

     printf("   Src port: %d\n", ntohs(tcp->th_sport));
     printf("   Dst port: %d\n", ntohs(tcp->th_dport));

     /* define/compute tcp payload (segment) offset */
     /* define/computa donde empieza la carga tcp */
     payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
     /* compute tcp payload (segment) size */
     /* computa el tamano de la carga tcp */
     size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);

     ip_pkt.setPayload((char *) payload, size_payload);

     packet_list.push_back(ip_pkt);
     break ;
  case IPPROTO_UDP:

   printf("   Protocol: UDP\n");
   udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + size_ip);
   ip_pkt.setIPSPort(ntohs(udp->th_sport)) ;
   ip_pkt.setIPDPort(ntohs(udp->th_dport)) ;
   printf("   Src port: %d\n", ntohs(udp->th_sport));
   printf("   Dst port: %d\n", ntohs(udp->th_dport));

   /* define/compute udp payload (segment) offset */
   /* define/computa donde comienza UDP */
   payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + 8);

   /* compute udp payload (segment) size */
   /* computa donde comienza la carga del UDP */
   size_payload = ntohs(ip->ip_len) - (size_ip + 8);
   ip_pkt.setPayload((char *)payload, size_payload);

   packet_list.push_back(ip_pkt);

   break;
  case IPPROTO_ICMP:
   printf("   Protocol: ICMP\n");
   break;
  case IPPROTO_IP:
   printf("   Protocol: IP\n");
   break;
  default:
   printf("   Protocol: unknown\n");
   break;
 }

return;
}

/// \fn vector<ip_packet> *Sniffer::getPacketList()
/// \~English
/// \brief Returns the packet list that contains the packets that are
/// Ethernet -> IP -> (TCP|UDP)
/// \return vector of packets
/// \~Spanish
/// \brief Devuelve la lista de paquetes que contiene los paquetes que son
/// Ethernet -> IP -> (TCP|UDP)
/// \return vector de paquetes
vector<ip_packet> *Sniffer::getPacketList(){
    return &packet_list ;

}

/// \fn void Sniffer::find_devices(vector<string> &devs)
/// \~English
/// \brief Find the network devices in the computer, and store them in vector devs.
/// \param devs vector of string for device names
/// \~Spanish
/// \brief Encuentra los dispositivos de red en la computadora, y los almacena en
/// el vector devs
/// \param devs vector de cadenas de caracteres para los nombres
void Sniffer::find_devices(vector<string> &devs){

    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_if_t *devices ;

    if(pcap_findalldevs(&devices, errbuf) == 0 && devices != NULL){

        while(devices){
            string s(devices->name) ;
            devs.push_back(s) ;
            devices = devices->next ;
        }
        pcap_freealldevs(devices);
    }
    else{
        devs.push_back("None") ;
    }

return ;
}

/// \fn void Sniffer::setDevice(string dev)
/// \~English
/// \brief Sets the device to capture packets to dev
/// \param dev name of the device
/// \~Spanish
/// \brief Ajusta el dispositivo para caputurar paquetes a dev
/// \param dev nombre del dispositivo
void Sniffer::setDevice(string dev){
    device = dev ;
}

