/*
 *
 *	This is free software. You can redistribute it and/or modify under
 *	the terms of the GNU General Public License version 2.
 *
 * 	Copyright (C) 1998 by kra
 *
 */
#include "hunt.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

static struct list l_icmp_packet = LIST_INIT(struct packet, p_next[MODULE_HOSTUP]);

struct host_up_info {
	unsigned int start_addr;
	unsigned int end_addr;
	int *up;
	unsigned int up_len;
};


static void func_icmp_packet(struct packet *p, void *arg)
{
	struct iphdr *iph = p->p_iph;
	struct icmphdr *icmph = p->p_hdr.p_icmph;
	struct host_up_info *hui = (struct host_up_info *) arg;
	
	if (ntohl(iph->saddr) >= ntohl(hui->start_addr) && 
	    ntohl(iph->saddr) <= ntohl(hui->end_addr) &&
	    icmph->type == 0 && icmph->code == 0) {
		packet_want(p);
		list_produce(&l_icmp_packet, p);
	}
}

static void perform_ping(struct host_up_info *hui, int count)
{
	struct mac_info *m;
	struct packet *p;
	struct timeval tv;
	struct timespec timeout;
	unsigned int ip, src_addr;
	unsigned int idx;
	int *up = hui->up;
	int j;
	
	printf("ping");
	fflush(stdout);
	for (j = 1; j <= count; j++) {
		for (ip = hui->start_addr; 
		     ntohl(ip) <= ntohl(hui->end_addr); 
		     ip = htonl(ntohl(ip) + 1)) {
			idx = ntohl(ip) - ntohl(hui->start_addr);
			if (!up[idx] &&
			    (m = mac_info_get(ip))) {
				send_icmp_request(my_eth_ip, ip, my_eth_mac,
						  m->mac, htons(j + 2000));
				mac_info_release(m);
			}
		}
		/* listen some time for reply */
		gettimeofday(&tv, NULL);
		timeout.tv_sec = tv.tv_sec + 2;
		timeout.tv_nsec = tv.tv_usec * 1000;
		while ((p = list_consume(&l_icmp_packet, &timeout))) {
			/* src addr is in the range start_addr .. end_addr */
			src_addr = p->p_iph->saddr;
			if (is_icmp_reply(p, src_addr, my_eth_ip,
					  p->p_ethh->h_source, my_eth_mac)) {
				idx = ntohl(src_addr) - ntohl(hui->start_addr);
				up[idx] = 1;
				host_lookup(src_addr, HL_MODE_DEFERRED);
			}
			packet_free(p);
		}
		printf(".");
		fflush(stdout);
	}
	printf("\n");
}

static void list_host_up(struct host_up_info *hui)
{
	unsigned int addr, idx;
	
	for (addr = hui->start_addr; 
	     ntohl(addr) <= ntohl(hui->end_addr);
	     addr = htonl(ntohl(addr) + 1)) {
		idx = ntohl(addr) - ntohl(hui->start_addr);
		if (hui->up[idx]) {
			printf("UP  %s\n", host_lookup(addr, HL_MODE_DEFERRED));
		}
	}
}

void host_up(void)
{
	static unsigned int start_ip_def = 0, end_ip_def = 0;
	unsigned int start_ip, end_ip;
	struct ifunc_item ifunc_icmp;
	struct host_up_info *hui;
	struct timespec ts;
	unsigned int len;
	
	if ((start_ip = menu_choose_hostname("start ip addr", host_lookup(start_ip_def, HL_MODE_NR))) == -1)
		return;
	if ((end_ip = menu_choose_hostname("end ip addr", host_lookup(end_ip_def, HL_MODE_NR))) == -1)
		return;
	if ((len = ntohl(end_ip) - ntohl(start_ip) + 1) < 0) {
		printf("bad addresses\n");
		return;
	}
	start_ip_def = start_ip;
	end_ip_def = end_ip;
	
	hui = malloc(sizeof(struct host_up_info));
	hui->start_addr = start_ip;
	hui->end_addr = end_ip;
	hui->up = malloc(sizeof(int) * len);
	hui->up_len = len;
	memset(hui->up, 0, sizeof(int) * len);

	printf("mac discovery\n");
	mac_discover_range(hui->start_addr, hui->end_addr, 2);
	ts.tv_sec = 1;
	ts.tv_nsec = 0;
	nanosleep(&ts, NULL);
	
	ifunc_icmp.func = func_icmp_packet;
	ifunc_icmp.arg = hui;
	list_enqueue(&l_ifunc_icmp, &ifunc_icmp);
	perform_ping(hui, 3);
	list_remove(&l_ifunc_icmp, &ifunc_icmp);
	packet_flush(&l_icmp_packet);
	
	list_host_up(hui);

	free(hui->up);
	free(hui);
}

