next   previous   contents

Esecuzione dei comandi

 Per l'esecuzione delle azioni previste si fa riferimento a due liste di comandi associate alla sequenza di knock riconosciuta, e puntate da attempt->knock->start_command_list ed attempt->knock->stop_command_list. Nel caso la sequenza riconosciuta utilizzi una sola serie di comandi, il secondo puntatore ovviamente deve essere posto a NULL.
 Come gia' descritto nella sezione riguardante le strutture dati adottate, start_command_list e stop_command_list sono due liste costituite da elementi del tipo PMList, i quali a loro volta contengono una struttura dati command_t puntata da data, un puntatore di tipo void. Per accedere a tale struct e' quindi necessario eseguire un'operazione di typecasting sull'indirizzo puntato da data, effettuando una conversione al tipo command_t.
 A questo punto e' possibile verificare che il comando associato alla struttura (execute->bin) esista e non sia una stringa vuota.
 Se il controllo dovesse avere esito positivo allora, viene fatta una chiamata alla funzione fork, per creare un figlio a cui possano essere fatti eseguire i comandi previsti. In caso di successo essa ritorna il pid del processo figlio al padre e zero al figlio; ritorna invece -1 al padre in presenza di un eventuale errore, senza creare alcun figlio.

PMList *temp;
command_t *execute;

/*knock is complete*/
#ifdef DEBUG
  [...]/*debug info*/
#endif

execute=(command_t*)attempt->knock->start_command_list->data;
/*if exist a command in command_t struct execute it*/
if(execute->bin && strlen(execute->bin))
{//exist at least 1 command
  /*fork the process*/
  if (fork()==0)
{//fork
/* child */
setsid();
/* execute the commands in command_list */
for(temp=attempt->knock->start_command_list; temp; temp=temp-next)
{
  execute=(command_t*)temp->data;
  #ifdef DEBUG
    [...]/*debug info*/
  #endif
  /*exec*/
  exec_command(parse_commad(execute->bin, attempt->src), attempt->knock->name, pkt_date, pkt_time);
}

 Dunque, se il valore ritornato e' zero si puo' scandire la lista start_command_list ed affidare l'esecuzione dei singoli comandi al processo figlio, chiamando la funzione exec_command, la quale ha come argomenti tre stringhe, rappresentanti rispettivamente: il comando da eseguire, data e ora del riconoscimento della sequenza di knock.
 Come si puo' notare dal codice riportato, la stringa che definisce il comando, puntata da execute->bin, deve prima essere passata a parse_command. Compito di tale funzione e' costruire il primo argomento utilizzato da exec_command, partendo dal comando memorizzato nella struttura command_t, ed includendovi l'indirizzo IP del richiedente quando necessario.
 Se ad esempio il comando in questione fosse iptables -D INPUT -s $IP -p tcp --dport 22 -j ACCEPT, e l'indirizzo IP del richiedente 192.168.0.4, parse_command deve ritornare un puntatore alla stringa “iptables -D INPUT -s 192.168.0.1 -p tcp --dport 22 -j ACCEPT”, nella quale sono state sostituite le occorrenze di $IP con l'effettivo indirizzo IP.
 Scopo di exec_command, e' invece l'esecuzione del comando passatole come primo argomento, per mezzo di una chiamata alla funzione della C standard library system, e la registrazione della rispettiva entry nel file di log di module knock.

void exec_command(char* command, char* name, char* date, char* time)
{
  int ret;
  ret = system(command);

  /*error*/
  if(ret == -1)
  {
    fprintf(LogFilePtr, "[%s][ERROR] %s-%s command fork failed for %s\n" , name, date, time, command );
  }

  /*exit status !=0, WARNING */
  else if(ret != 0)
  {
    fprintf(LogFilePtr, "[%s][WARNING] %s-%s : command %s returned non-sero status code (%d)\n", name, date, time, command, ret);
  }

  /*OK*/
  else if(!ret)
  {
    fprintf(LogFilePtr, "[%s] %s-%s command : %s\t[OK]\n", name, date, time, command );
  }

  return;
}

 Inoltre, se alla sequenza di knock riconosciuta fosse associata anche la seconda serie di comandi da eseguire trascorso un certo timeout, il processo figlio precedentemente creato deve controllare anche la lista attempt->knock->stop_command_list per verificare che almeno il primo comando (puntato da execute->bin) esista e sia diverso dalla stringa vuota.
 Se il controllo dovesse avere esito positivo allora, viene fatta una chiamata alla funzione sleep, mettendo in pausa processo chiamante per un numero di secondi pari al timeout associato alla sequenza e specificato da attempt->knock->cmd_timeout.
 Trascorso l'intervallo cmd_timeout il processo torna operativo e puo' provvede ad eseguire i vari comandi della lista stop_command_list, tramite la funzione exec_command, come gia' visto in precedenza.

/*if exist stop commands execute them*/
if( (execute=(command_t*)attempt->knock->stop_command_list->data) )
{
  if(execute->bin && strlen(execute->bin))
  {
    /*wait cmd_timeout*/
    sleep(attempt->knock->cmd_timeout);
    fprintf(LogFilePtr, "[%s] %s-%s Command timeout : %d \n", attempt->knock->name, pkt_date, pkt_time, (int) attempt->knock->cmd_timeout );

    /*scan command list*/
    for(temp=attempt->knock->stop_command_list ; temp ; temp=temp->next)
    {       execute=(command_t*)temp->data;
      #ifdef DEBUG
        [...] /*debug info*/
      #endif

      /*run command*/
      exec_command(parse_commad(execute->bin, attempt->src), attempt->knock->name, pkt_date, pkt_time);
    }
  }
}

 Infine, eseguiti tutti comandi previsti per la sequenza di knock, viene fatto terminare il processo figlio con una chiamata ad exit, che causa la normale terminazione del programma, ritornando l'exit status al processo padre.

  /*exit child*/
  exit(EXIT_SUCCESS);
  }
}

next   previous   contents