
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <glib.h>
#include <string.h>
#include "g_snmp.h"

#include "snmp_lib.h"
#include "ping.h"
#include "turtledefs.h"

#include "rfc1213.h"

typedef struct _req_data
{
  host_snmp     host;      /* Host snmp structure for the route requests */
  gpointer      attable;   /* The SNMP request */
  gpointer      iftable;   /* The SNMP request */
  guchar        atused;    /* The SNMP request */
  guchar        ifused;    /* The SNMP request */
} req_data;


void readsline(gpointer data, gpointer user_data);
void checkout_host(gulong address);
		
static GHashTable *hostlist;
static GHashTable *netlist;

static gulong ipaddr[]     = { IPADENTADDR };
static gulong ipnetmask[]  = { IPADENTNETMASK };
static gulong atnetaddr[]  = { ATNETADDRESS };
static gulong atphysaddr[] = { ATPHYSADDRESS };
static gulong atifindex[]  = { ATIFINDEX };

#define oidlen(val) (sizeof(val)/sizeof(gulong))

void myghf(gpointer key, gpointer value, gpointer user_data)
{
    struct gxnlkey *keya;

    keya = (struct gxnlkey*)key;

    g_print("Key is %s", inet_ntoa(keya->address));
    g_print("nm %s.\n", inet_ntoa(keya->netmask));
}

gint gxhl_compare(gconstpointer a, gconstpointer b)
{
    if (a == b)
        return TRUE;
    else
        return FALSE;
}

gint gxnl_compare(gconstpointer a, gconstpointer b)
{
    struct gxnlkey *keya;
    struct gxnlkey *keyb;

    keya = (struct gxnlkey*)a;
    keyb = (struct gxnlkey*)b;

    g_print("Comparing %s", inet_ntoa(keya->address));
    g_print(" with %s.\n", inet_ntoa(keyb->address));
    if (keya->address.s_addr != keyb->address.s_addr)
        return FALSE;
    if (keya->netmask.s_addr != keyb->netmask.s_addr)
        return FALSE;
    g_print("We found it\n");
    return TRUE;
}

gboolean
find_host(gulong address)
{
    if (g_hash_table_lookup(hostlist, (gpointer)address) != NULL)
        return TRUE;
    
    return FALSE;
}

void
cb_register(guint skt, void (*receiveMessage)())
{
    /*gdk_input_add(socket, GDK_INPUT_READ, receiveMessage, NULL);*/
    turtle_input_read_add(skt, receiveMessage);
}
void
snmpinit()
{
	if (!g_snmp_init(FALSE))
		g_error("Initialisation of SNMP library failed.");
}


/*
 * This function is called once we have sent a ping to a device and it has
 * replied back.  Now we want to SNMP it and find out more!
 */
void
got_ping(gulong address, time_t sent_time, gpointer user_data)
{
    /*g_print("Received ping from %s\n", inet_ntoa(*(struct in_addr*)&address));*/
    checkout_host(address);
}
int
main(int argc, char *argv[])
{
    gulong seed;
    GMainLoop *loop;
    
    hostlist = g_hash_table_new(NULL,gxhl_compare);
    netlist = g_hash_table_new(g_str_hash,g_str_equal);

#ifdef __bsdi__
    seed = inet_addr("127.0.0.1");
#else
    seed = ntohl(INADDR_LOOPBACK);
#endif

    snmpinit();
	
    init_ping();

    turtleio_init();

    sendping(seed, got_ping, NULL, NULL);

    /*
     * Now comes the scary glib loop stuff 
     */
    loop = g_main_new(TRUE);
    while(g_main_is_running(loop))
    {
        g_main_run(loop);
    }
    g_main_destroy(loop);
    return 0;
}
	
void
ifline_cb(GHashTable *results, guint indlen, gpointer user_data)
{
    SNMP_OBJECT *adsl, *nmsl;
    char *address, *netmask, buf[256];
    struct in_addr addr, netm, neta, in;
    TurtleHost *thost;
    /*struct gxnlkey *netkey;*/
    char *tmpa;
    char *netkey;
    int i;

    i = 0;
    adsl = g_hash_table_lookup(results, &i);

    i = 1;
    nmsl = g_hash_table_lookup(results, &i);

    if ((adsl && nmsl) == 0)
      return;

    g_snmp_printf (buf, sizeof (buf), adsl);
    address = g_strdup(buf);
    in.s_addr = inet_addr(buf);

    g_snmp_printf (buf, sizeof (buf), nmsl);
    netmask = g_strdup(buf);

    inet_aton(address, &addr);
    inet_aton(netmask, &netm);

    if (!find_host(ntohl(addr.s_addr)) && addr.s_addr != 0 && 
            ntohl(addr.s_addr) != INADDR_LOOPBACK  &&
            ntohl(addr.s_addr) != INADDR_UNSPEC_GROUP)
    {
        thost = g_malloc(sizeof(TurtleHost));
        thost->address = in;
        g_hash_table_insert(hostlist, (gpointer)(ntohl(addr.s_addr)), 
                (gpointer)thost);
        sendping(inet_addr(address), got_ping, NULL, NULL);
	g_print("New node found %s\n", address );
    }
    
    neta.s_addr = addr.s_addr & netm.s_addr;
	
    /* Check for loopback or invalid networks, which we ignore */
    if (neta.s_addr == IN_LOOPBACKNET ||
            neta.s_addr == 0)
        return;

    tmpa = g_strdup(inet_ntoa(neta));
    netkey = g_strdup_printf("%s/%s", tmpa, inet_ntoa(netm));
    g_free(tmpa);

    /*g_print("Checking to see if we have this net already, hash table is %d big\n", g_hash_table_size(netlist));*/
    if (g_hash_table_lookup(netlist, (gpointer)netkey) != NULL)
    {
        /* We found this one already */
        g_free(netkey);
        return;
    }
    /*g_hash_table_foreach(netlist, myghf, NULL);*/
    g_hash_table_insert(netlist, (gpointer)netkey, (gpointer)32);
    g_print("New network %s with netmask %s\n", inet_ntoa(neta), netmask);
}

void
atline_cb(GHashTable *results, guint indlen, gpointer user_data)
{
    SNMP_OBJECT *adsl;
    char buf[256];
    TurtleHost *thost;
    struct in_addr in;
    int i;

    i = 0;
    adsl = g_hash_table_lookup(results, &i);

    if (!adsl)
      return;

    g_snmp_printf (buf, sizeof (buf), adsl);

    in.s_addr = inet_addr(buf);

    if (ntohl(in.s_addr) == INADDR_LOOPBACK ||
            ntohl(in.s_addr) == INADDR_UNSPEC_GROUP)
        return;

    if (!find_host(ntohl(in.s_addr)))
    {
        thost = g_malloc(sizeof(TurtleHost));
        thost->address = in;
        g_hash_table_insert(hostlist, (gpointer)(ntohl(in.s_addr)), (gpointer)thost);
        sendping(inet_addr(buf), got_ping, NULL, NULL);
	g_print("New node found %s\n", buf );
    }
}

static void
atfinish_cb (gpointer user_data)
{
  req_data *req;

  req = (req_data *)user_data;

  req->atused = 0;
  g_snmp_table_destroy(req->attable);

  if ((req->atused || req->ifused) == 0)
    g_free(req);
}
  
static void
iffinish_cb (gpointer user_data)
{
  req_data *req;

  req = (req_data *)user_data;

  req->ifused = 0;
  g_snmp_table_destroy(req->iftable);

  if ((req->atused || req->ifused) == 0)
    g_free(req);
}

void
checkout_host(gulong address)
{

    GSList *ifobjs, *atobjs;
    req_data *req;
    struct in_addr neta;
    

    neta.s_addr = address;
    /*g_print("Checking out %s \n", inet_ntoa(neta));*/

    req = g_new0 (req_data, 1);
   
    ifobjs = NULL;
    atobjs = NULL;

    req->atused = 1;
    req->ifused = 1;

    req->host.domain  = AF_INET;
    req->host.rcomm   = "public";
    req->host.wcomm   = "private";
    req->host.retries = 10;
    req->host.name    = g_strdup(inet_ntoa(*(struct in_addr*)&(address)));
    req->host.status  = 0;
    req->host.port    = 161;
    req->host.timeout = 10;
    req->host.version = 0;

    /* First we find all interfaces, which tells us what networks the
     * device is connected to
     */

    g_pdu_add_oid (&ifobjs, ipaddr, oidlen(ipaddr), SNMP_NULL, NULL);
    g_pdu_add_oid (&ifobjs, ipnetmask, oidlen(ipnetmask), SNMP_NULL, NULL);

    req->iftable = g_snmp_table_new(&req->host, ifobjs, 
                                    iffinish_cb, ifline_cb, iffinish_cb, req);
    g_snmp_table_get(req->iftable);

    g_pdu_add_oid (&atobjs, atnetaddr, oidlen(atnetaddr), SNMP_NULL, NULL);
    g_pdu_add_oid (&atobjs, atphysaddr, oidlen(atphysaddr), SNMP_NULL, NULL);
    g_pdu_add_oid (&atobjs, atifindex, oidlen(atifindex), SNMP_NULL, NULL);
    req->attable = g_snmp_table_new(&req->host, atobjs, 
                                    atfinish_cb, atline_cb, atfinish_cb, req);
    g_snmp_table_get(req->attable);
}

