/*****************************************************************************
* File:   knock.c
* Date:   2006-Apr-29 3:14:27 PM EST
* Original Authors: Jason Galens and Pat Wilbur
* Last Modified:2006-Apr-30 11:23:05 PM EST
*
* Description:
*
* Port knocking daemon that authenticates clients and adds rules to iptables.
*
*
* Notice:
*
* To be used with knock.c.  Copying and distributing this file is permitted as
* long as it is packaged with knock.c, and all this information here at the top
* of the file, including authors' names, authors' e-mail addresses, the 
* description, the notice, and the thank you messages are all intact.  In other
* words, do not touch the area between the stars.  If you wish to contribute,
* please e-mail one of us!
*
* libpcap implementation based on the BEST EVER libpcap packet capture tutorial
* by Martin Casado.  That tutorial may be found at:
*   http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
*
* Thank you, Mr. Casado.
*
******************************************************************************/


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>

#define     KEY_LEN     16
#define     BUF_LEN     256
#define     N_PACKETS 16
#define N_PORTS     65536

/* globals */
int sock, length, n;
struct sockaddr_in server, from;
struct hostent *hp;

/* functions */
void error(char *);
void send_packet(unsigned short port);
void crypt(const unsigned char* buffer, int buf_size, const unsigned char* key, int key_len, unsigned char* ret);

int main(int argc, char **argv)
{
     if (argc != 4) {
         printf("Usage: %s server port username\n", argv[0]);
         return 0;
     }

     srand(time(NULL));

     unsigned char knock_buffer[BUF_LEN];
     int knock_len, i;
     bzero(knock_buffer,BUF_LEN);

     char hostn[BUF_LEN];
     struct hostent *hostIP;

     unsigned short port = (unsigned short)atoi(argv[2]);

     unsigned char key[KEY_LEN];

     // create socket
     sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock < 0)
         error("socket");
    
     // set destination
     server.sin_family = AF_INET;
     hp = gethostbyname(argv[1]);
     if (hp == 0)
         error("Unknown host");
     bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length);
     length=sizeof(struct sockaddr_in);
    
     //send_packet(atoi(argv[2]));

     if((gethostname(hostn, sizeof(hostn))) != 0) {
         error("No IP foind");
      }

     hostIP = gethostbyname(hostn);
    
     /* fill the buffer with the information in the right order */

     knock_buffer[0] = hostIP->h_addr[0]; // this machines ip
     knock_buffer[1] = hostIP->h_addr[1];
     knock_buffer[2] = hostIP->h_addr[2];
     knock_buffer[3] = hostIP->h_addr[3];

     char* temp = (char*)(&port);     // add the port we want to connect to
     knock_buffer[4] = temp[0];
     knock_buffer[5] = temp[1];

     int timestamp = time(NULL) + 60; // give us a minute to open the port
     temp = (char*)(&temp);
     knock_buffer[6] = temp[0];
     knock_buffer[7] = temp[1];
     knock_buffer[8] = temp[2];
     knock_buffer[9] = temp[3];

     int length = strlen(argv[3]);     // add the username
     for (i = 10; i < length + 10; i++) {
         knock_buffer[i] = argv[3][i];
     }    
     knock_buffer[length + 10] = '\0';

     /* open server's public key file and obtain key */
     int handle = open("server.key", O_RDONLY);
     if (handle == -1) {
         printf("Cannot find server key.");
         return 0;
     }

     read(handle, key, KEY_LEN);
     close(handle);

     /* take user supplied info and encrypt it */
     unsigned char crypted_knock[BUF_LEN];

     crypt(knock_buffer, BUF_LEN, key, KEY_LEN, crypted_knock);

     /* divide info into chunks */
     /* send packets within range of ports */
/*     unsigned short x, y;
     int j;
     for (i = 0, j = 0; i < N_PACKETS; i++) {
         y = x = 0;
         if (i % 2) {
             x = (unsigned short)crypted_knock[j];
             y = (( (unsigned short)(crypted_knock[++j]) ) & 0x0F) << 8;
             y += x;
             printf("y= %i\n", y);
             j++;
         }
         else {
             y = ( (unsigned short)(crypted_knock[j]) ) << 4;
             x = (( (unsigned short)(crypted_knock[++j]) ) & 0xF0) >> 4;
             y += x;
             printf("y= %i\n", y);
         }
        
         port = y + i*(N_PORTS/N_PACKETS);
         send_packet(port);
     } */
     
     
     for (i=0; i<N_PACKETS; i++) {
     	port = (i*256) + crypted_knock[i];
     	send_packet(port);
     }
     
    
     //printf("sizeof(time_t) = %i\n", sizeof(time_t));     
     // close socket
     close(sock);  
}

void crypt(const unsigned char* buffer, int buf_size, const unsigned char* key, int key_len, unsigned char* ret)
{
     int i, j;
     for (i = 0, j = 0; i < buf_size; i++, j++) {
         if (j >= key_len) j = 0;
    
         ret[i] = buffer[i] ^ key[j];
     }
}

void send_packet(unsigned short port)
{
     char buffer[BUF_LEN];

     server.sin_port = htons(port);
    
     int i;    
     for (i = 0; i < BUF_LEN; i++) {
         buffer[i] = (char)(rand() % 256);
     }
     
     // send packet
     n=sendto(sock, buffer, BUF_LEN, 0, &server, length);
     if (n < 0) error("Sendto");
}

void error(char *msg)
{
   perror(msg);
   exit(0);
}
