/* $Id: target.cc,v 1.9 2002/07/22 04:54:55 fygrave Exp $ */
/*
** Copyright (C) 2001 Fyodor Yarochkin <fygrave@tigerteam.net>,
**                    Ofir Arkin       <ofir@sys-security.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** All material for nonprofit, educational use only.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/


#include "xprobe.h"
#include "target.h"
#include "interface.h"
#include "xprobe_module_hdlr.h"
#include "os_matrix.h"
#include "xplib/xplib.h"

extern Interface *ui;
extern Xprobe_Module_Hdlr   *xmh;
extern OS_Name *oses;

/*
 *************************
 * Target_Net:: object methods
 *************************
 */
int Target_Net::parse_mask(char *mask_ascii) {

    if (!mask_ascii) return FAIL;
    if (!atoi(mask_ascii) || atoi(mask_ascii) > 32) {
        ui->error("Incorrect netmask specification: %s "
                "(1-32 allowed)\n", mask_ascii);
        return FAIL;
    }
    mask = htonl((0xffffffff<<(32-atoi(mask_ascii))));
    return OK;
}

int Target_Net::parse_host(char *target) {
    char *host_ascii;
    char *mask_ascii;

    host_ascii = strdup(target);
    if((mask_ascii = strchr(host_ascii,'/')) != NULL) {
		*mask_ascii='\0';
		mask_ascii++;
        if (parse_mask(mask_ascii) != OK) {
            free(host_ascii);
            return FAIL;
        }
    } else {
        mask = 0xffffffff;
    }

    if (resolve_host(host_ascii) == FAIL) {
        ui->error("Can not resolve %s: %s", host_ascii,
                     hstrerror(h_errno));
        free(host_ascii);
        return FAIL;
    }
    addr &= mask; 
    free(host_ascii);
    return OK;
}

int Target_Net::resolve_host(char *host) {

    struct hostent *host_serv;

    if ((addr = inet_addr(host)) == INADDR_NONE)
    {
        host_serv = gethostbyname(host);
        if (host_serv == NULL)
            return FAIL;
	memcpy((void *)&addr, (void *)host_serv->h_addr, host_serv->h_length);
    }
    return OK;
}

Target_Net::Target_Net(char *target) {
    init(target);
}

Target_Net::Target_Net(void) {

    ascii_name = NULL;
    addr = mask = counter = 0;
}

int Target_Net::init(char *target) {

    ascii_name = strdup(target);
    if (parse_host(ascii_name) == FAIL) return FAIL;
    counter = ntohl(addr);
    return OK;
}
    
unsigned long Target_Net::getnext(void) {

    if (counter > ntohl(addr|mask^0xffffffff))
        return 0;
    return (htonl(counter++));
}



/*
 ******************************
 * Target:: object methods
 ******************************
 */

void Target::add_p(map <int, char> &ic_map, int p, char s) {
            ic_map.insert(pair<int, char>(p, s));
}

int Target::find_stat_p(map <int, char> &ic_map, char s) {
    map <int, char>::iterator ic_map_i;
        for (ic_map_i = ic_map.begin(); ic_map_i != ic_map.end(); ic_map_i++) {
            if ((*ic_map_i).second == s) return (*ic_map_i).first;
        }
        return -1;
}

void Target::add_port(int proto, int port, char s) {

    switch(proto) {
        case IPPROTO_TCP:
            add_p(tcp_ports, port, s);
            break;
        case IPPROTO_UDP:    
            add_p(udp_ports, port, s);
            break;
        default:
            xprobe_debug(XPROBE_DEBUG_TARGET, "Unknown protocol: %i for port %i",
                            proto, port);
    }
}

int Target::get_port(int proto, int s) {

    switch(proto) {
        case IPPROTO_TCP:
            return (find_stat_p(tcp_ports, s));
            break;
        case IPPROTO_UDP:    
            return (find_stat_p(tcp_ports, s));
            break;
        case IPPROTO_ICMP:    
            return (find_stat_p(tcp_ports, s));
            break;
        default:
            return -1;
    }

}

void Target::add_protocol(int proto, char s) {
        add_p(protocols,  proto, s);
}

struct in_addr Target::get_interface_addr(void) {
    struct in_addr a;
    a = xp_get_iface_addr(xp_get_interface(addr));
    return a;
}

char * Target::get_interface(void) {
    char *in;
    in = xp_get_interface(addr);
    return in;
}

void Target::set_ttl(int type, int val) {
    /*XXX: do proper search in the base later */
    if (get_ttl(type) != FAIL) return; /* already  there */
    ttls.insert(pair<int, int>(type, val));
}

int Target::get_ttl(int type) {
    map <int, int>::iterator ttls_i;
    
    ttls_i = ttls.find(type);
    if (ttls_i != ttls.end()) return (*ttls_i).second;
    return FAIL;

}

int Target::check_alive(void) {
    OS_Matrix *os = new OS_Matrix(xmh->loaded_mods_num(XPROBE_MODULE_ALIVETEST));
    int ret = 0;

    xmh->exec(XPROBE_MODULE_ALIVETEST, this, os);

    if (os->get_score(os->get_top(0)) != 0) ret = 1;

    ui->msg("[+] Host: %s is %s (Guess probability: %i%%)\n",
            inet_ntoa(addr), ret == 1?"up":"down", os->get_prcnt_score(os->get_top(0)));

    delete os;
    return(ret);
}

int Target::os_probe(void) {
    OS_Matrix *os = new OS_Matrix(xmh->loaded_mods_num(XPROBE_MODULE_OSTEST));
    int ret = 0, i;

    xmh->exec(XPROBE_MODULE_OSTEST, this, os);

    if (os->get_score(os->get_top(0)) != 0) ret = 1;

    ui->msg("[+] Primary guess:\n");
    ui->msg("[+] Host %s Running OS: %s (Guess probability: %i%%)\n",
            inet_ntoa(addr), oses->osid2char(os->get_top(0)), os->get_prcnt_score(os->get_top(0)));
    ui->msg("[+] Other guesses:\n");
    i = 1;
    while (os->get_top(i) && os->get_prcnt_score(os->get_top(i))) {
        ui->msg("[+] Host %s Running OS: %s (Guess probability: %i%%)\n",
                inet_ntoa(addr), oses->osid2char(os->get_top(i)),
                os->get_prcnt_score(os->get_top(i)));
        i++;
    }

    delete os;
    return(ret);
}

