package cz.vut.jmeter.ddos.httpsflood;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

import javax.swing.*;

import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.gui.util.HorizontalPanel;
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.util.JMeterUtils;

import eu.gity.jmeter.ddos.common.DDoSInputVerifier;
import eu.gity.jmeter.ddos.common.DDoSUtils;

import static eu.gity.jmeter.ddos.properties.strings.DdosPropertiesStrings.*;

/**
 * This class provides GUI for HTTPS Flood attack and allows spoofing of source IPs
 * 
 * @author Karel Lanzhotsky
 *
 */

public class HttpsFloodGui extends AbstractSamplerGui{
	private static final long serialVersionUID = 240L;

    private JTextField targetIP;
    private JTextField sourceIPMIN;
    private JTextField sourceIPMAX;
    private JButton applySettings;
    private JButton resetSettings;
    
    private final JComboBox selectInterface = new JComboBox<String>();
    private final DDoSInputVerifier verifier = new DDoSInputVerifier();
    
    public HttpsFloodGui() {
        init();
        registerTestStateListener();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLabelResource() {
        return DDOS_HTTPSFLOOD_TITLE; // $NON-NLS-1$
    }
    public String getStaticLabel() {
    	return JMeterUtils.getLocaleString(DDOS_HTTPSFLOOD_TITLE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configure(TestElement element) {
    	List<String> devs = DDoSUtils.getInstance().getValidDevNames();
        if (devs.size() > selectInterface.getItemCount()) {
            selectInterface.setModel(new DefaultComboBoxModel<String>(devs.toArray(new String[devs.size()])));
        }
    	targetIP.setText(element.getPropertyAsString(HttpsFloodSampler.TARGETIP));
        sourceIPMIN.setText(element.getPropertyAsString(HttpsFloodSampler.SOURCEIPMIN));
        sourceIPMAX.setText(element.getPropertyAsString(HttpsFloodSampler.SOURCEIPMAX));
        super.configure(element);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public TestElement createTestElement() {
        HttpsFloodSampler sampler = new HttpsFloodSampler();
        modifyTestElement(sampler);
        return sampler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void modifyTestElement(TestElement te) {
        te.clear();
        configureTestElement(te);
        Object selectedDev = selectInterface.getSelectedItem();
        if (selectedDev != null) { //Maybe no network devices were found       
            te.setProperty(HttpsFloodSampler.INTERFACE, selectedDev.toString());
        }
        te.setProperty(HttpsFloodSampler.TARGETIP, targetIP.getText());
        te.setProperty(HttpsFloodSampler.SOURCEIPMIN, sourceIPMIN.getText());
        te.setProperty(HttpsFloodSampler.SOURCEIPMAX, sourceIPMAX.getText());
    }

    /*
     * Helper method to set up the GUI screen
     */
    private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
        // Standard setup
        setLayout(new BorderLayout(5, 5));
        setBorder(makeBorder());
        add(makeTitlePanel(), BorderLayout.NORTH); // Add the standard title

        // Specific setup
        VerticalPanel mainPanel = new VerticalPanel();
        mainPanel.add(createInterfacePanel());
        mainPanel.add(createTargetPanel());
        mainPanel.add(createSourcePanel());
        
        
        add(mainPanel, BorderLayout.CENTER);
    }
    /**
     * Create panel for selecting source interface
     * 
     * @return panel with menu to select network interface
     */

    private Component createInterfacePanel() {
    	JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_NET_INTERFACE));
    	label.setLabelFor(selectInterface);
    	
    	JPanel interfacePanel = new JPanel(new BorderLayout(5, 0));
        interfacePanel.add(label, BorderLayout.WEST);
        interfacePanel.add(selectInterface, BorderLayout.CENTER);
        
        return interfacePanel;
    }
    /**
     * Create a panel for setting of target IP
     *
     * @return the panel for entering target IP
     */
    private Component createTargetPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_TARGET_IP)); //$NON-NLS-1$

        
        targetIP = new JTextField("192.168.0.1");
        targetIP.setName("ip");
        targetIP.setInputVerifier(verifier);
        label.setLabelFor(targetIP);
        
        

        JPanel targetPanel = new JPanel(new BorderLayout(5, 0));
        targetPanel.add(label, BorderLayout.WEST);
        targetPanel.add(targetIP, BorderLayout.CENTER);

        return targetPanel;
    }
    /**
     * Creates panel for setting source IPs 
     * 
     * @return panel used to set source IPs and buttons for applying or resetting these addresses
     */
    private Component createSourcePanel() {
        JLabel labelS = new JLabel(JMeterUtils.getLocaleString(DDOS_SOURCE_IP)); //$NON-NLS-1$

        
        sourceIPMIN = new JTextField("192.168.0.10");
        sourceIPMIN.setName("ip");
        sourceIPMIN.setInputVerifier(verifier);
        sourceIPMAX = new JTextField("192.168.0.20");
        sourceIPMAX.setName("ip");
        sourceIPMAX.setInputVerifier(verifier);
        
        applySettings = new JButton("Apply IPs");
        resetSettings = new JButton("Reset Settings");
        
        /**
         * Button controls which assign IP addresses to interface
         */
        applySettings.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0){
				long min = ipToDec(sourceIPMIN.getText());
				long max = ipToDec(sourceIPMAX.getText());
				String[] apply = {"/bin/bash","-c","sudo bash /tmp/addIpAddresses.sh"};
				
				
				try {
					createShFile(generateIpRange(min,max),selectInterface.getSelectedItem().toString());
					ProcessBuilder pb = new ProcessBuilder();
					pb.command(apply);
					Process p = pb.start();
					
				}
				catch(IOException e){
					e.printStackTrace();
				}
				
				
			}
        		
        });
        
        /**
         * Button reverts any changes done to interface
         */
        
        resetSettings.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				String iface = selectInterface.getSelectedItem().toString();
				String[] reset = {"/bin/bash", "-c","sudo ip addr flush dev " +iface + 
												" && sudo ip link set " +iface + " down" + 
												" && sudo ip link set " +iface+" up"}; 
				
			
			try {
				ProcessBuilder pb = new ProcessBuilder();
				pb.command(reset);
				Process p = pb.start();
			}
			catch(IOException e) {
				e.printStackTrace();
			}
        	
        	
			}
		});
        
        labelS.setLabelFor(sourceIPMIN);
        

        final JPanel sourcePanel = new HorizontalPanel();
        JLabel labelMin = new JLabel("Min: ");
        labelMin.setLabelFor(sourceIPMIN);
        JLabel labelMax = new JLabel("Max: ");
        labelMin.setLabelFor(sourceIPMAX);
        
        sourcePanel.add(labelS);
        sourcePanel.add(labelMin);
        sourcePanel.add(sourceIPMIN);
        sourcePanel.add(labelMax);
        sourcePanel.add(sourceIPMAX);
        sourcePanel.add(applySettings);
        sourcePanel.add(resetSettings);
        return sourcePanel;
    }

    /**
     * Functions used for generating IPs and creating settings for IP spoofing
     */
    
    //converts octal form of IP to decimal number 
  	public static long ipToDec (String ipAddr) {
  		String[] ipArray = ipAddr.split("\\."); 
  		long ipDecimal = 0;
  		for (int i = 0;i < 4; i++) {
  			int power = 3 - i;
  			int address = Integer.parseInt(ipArray[i]);
  			ipDecimal += address * Math.pow(256, power);
  		}
  		
  		return ipDecimal;
  	}
  	//generates IP addresses of desired range
  	public static long[] generateIpRange(long IpMin, long IpMax) {
  		int range = Math.toIntExact(IpMax - IpMin)+1;
  		long[] ipArray = new long[range];
  		for(int i = 0; i < range; i++) {
  			ipArray[i] = IpMin + i; 
  		}
  		return ipArray;
  	}
  	//takes decimal value of IP address and converts it to octal form
  	public static String decToIp(long ipAddrDec) {
  		String ipString = "";
  		ipString = ((ipAddrDec >> 24) & 0xFF) + "." + 	//first octet
  					((ipAddrDec >> 16) & 0xFF) + "." + 	//second octet
  					((ipAddrDec >> 8) & 0xFF) + "." +	//third octet
  					(ipAddrDec & 0xFF);					//fourth octet
  		return ipString;
  	}
  	//creates .sh file to add addresses to network interface for source IP spoofing
  	public static void createShFile(long[] ipDec, String iface) throws IOException {
  		BufferedWriter outputWriter = new BufferedWriter(new FileWriter("/tmp/addIpAddresses.sh"));
  		
  		//deletes any IPs on interface
  		outputWriter.write("ip addr flush dev "+iface);
  		outputWriter.newLine();
  		for(int i = 0;i < ipDec.length;i++) {
  			
  			//writes IPs for spoofing into the file
  			outputWriter.write("ip addr add "+decToIp(ipDec[i])+"/24 "+"dev "+iface);
  			outputWriter.newLine();
  		}
  		outputWriter.flush();
  		outputWriter.close();
  	}
    
    @Override
    public void clearGui() {
        super.clearGui();

    }
    /**
     * Enables/disables GUI components (used when test starts/ends)
     *
     * @param en true if the components should be enabled, false if disabled
     */
    private void enableGUIComponents(boolean en) {
        targetIP.setEnabled(en);
        sourceIPMIN.setEnabled(en);
        sourceIPMAX.setEnabled(en);
        applySettings.setEnabled(en);
        resetSettings.setEnabled(en);
    }
    /**
     * Registers a listener which reacts to start/end of the test events
     */
    private void registerTestStateListener() {
        TestStateListener testStateListener = new TestStateListener() {

            @Override
            public void testStarted() {
                enableGUIComponents(false);
            }

            @Override
            public void testStarted(String host) {
                testStarted();
            }

            @Override
            public void testEnded() {
                //Register again for a next run
                registerTestStateListener();
                enableGUIComponents(true);
            }

            @Override
            public void testEnded(String host) {
                testEnded();
            }
        };
        StandardJMeterEngine.register(testStateListener);
    }
}
