from urllib3.util import connection, parse_url
import dns.resolver as resolver
from dns import message, rdatatype
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager
from requests_toolbelt.adapters.socket_options import SocketOptionsAdapter
import requests
import ssl
import socket
import json
import base64

class dns:
    def __init__(self, url):
        self.url = url
        self._orig_create_connection = connection.create_connection
        connection.create_connection = self.patched_create_connection
    def resolve_dns(self, host):
        if len(self.url.split("/"))>=3 and self.url.split("/")[2] == host:
            my_resolver  = resolver.Resolver()
            my_resolver.nameservers = ['1.1.1.1']
            return str(my_resolver.query(host)[0])

        if host.count(".") == 3:
            return host

        if self.url.startswith('http'):
            headers = {
                    'accept': 'application/dns-message',
                    'content-type': 'application/dns-message',
                    }

            q = message.make_query(host, rdatatype.A)
            response = requests.post(self.url, data=q.to_wire(), headers=headers)
            try:
                return message.from_wire(response.content).answer[0].to_rdataset()[0].to_text()
            except:
                return host
        else:
            if ':' in self.url:
                url = self.url.split(':')[0]
                port = self.url.split(':')[1]
            else:
                url = self.url
                port = 53

            my_resolver = resolver.Resolver()
            my_resolver.nameservers = [url]
            my_resolver.port = int(port)
            result = resolver.query(host)
            return str(my_resolver.query(host)[0])

    def patched_create_connection(self, address, *args, **kwargs):
        host, port = address
        hostname = self.resolve_dns(host)
        return self._orig_create_connection((hostname, port), *args, **kwargs)

class Custom_Adapter(HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self.worker = kwargs.pop('worker', None)
        super().__init__(*args, **kwargs)

    def init_poolmanager(self, connections, maxsize, block=False, ssl_version=None, source_address=(), socket_options=[]):
        if ssl_version:
            ssl_context = ssl.create_default_context()
            ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
            ssl_context.maximum_version = ssl.TLSVersion.TLSv1_2
            ssl_context.options = ssl.PROTOCOL_TLS & ssl.OP_NO_TLSv1_3
            self.poolmanager = PoolManager(num_pools=connections,
                    maxsize=maxsize,
                    block=block,
                    source_address=source_address,
                    socket_options=socket_options,
                    ssl_context=ssl_context)
        else:
            self.poolmanager = PoolManager(num_pools=connections,
                    maxsize=maxsize,
                    block=block,
                    source_address=source_address,
                    socket_options=socket_options)

    def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
        parsed = parse_url(request.url)
        if self.worker and parsed.scheme in ('http', 'https'):
            request.headers['Original-Host'] = parsed.host
            if parsed.query:
                new_url = f"{parsed.scheme}://{self.worker}{parsed.path}?{parsed.query}"
            else:
                new_url = f"{parsed.scheme}://{self.worker}{parsed.path}"
            request.url = new_url
        return super().send(request, stream, timeout, verify, cert, proxies)

class session:
    def __init__(self, bind="", proxy="", dns="", worker="", force_tls1_2=False):
        self.session = requests.Session()

        self.session.mount('http://', Custom_Adapter(worker=worker))
        self.session.mount('https://', Custom_Adapter(worker=worker))

        if bind != "":
            if "." not in bind:
                socket_options = [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, bind.encode())]
                source_address = ("", 0)
            else:
                socket_options = []
                source_address = (bind, 0)
        else:
            source_address = ("", 0)
            socket_options = []
        if force_tls1_2:
            ssl_version = ssl.PROTOCOL_TLSv1_2
        else:
            ssl_version = None

        self.session.get_adapter('http://').init_poolmanager(
                connections=requests.adapters.DEFAULT_POOLSIZE,
                maxsize=requests.adapters.DEFAULT_POOLSIZE,
                source_address=source_address,
                socket_options=socket_options
                )
        self.session.get_adapter('https://').init_poolmanager(
                connections=requests.adapters.DEFAULT_POOLSIZE,
                maxsize=requests.adapters.DEFAULT_POOLSIZE,
                source_address=source_address,
                socket_options=socket_options,
                ssl_version=ssl_version
                )

        if proxy != "":
            self.session.proxies = { "http": proxy, "https": proxy }

    def get_session(self):
        return self.session

def parse_params(params, name, default=""):
    ret = default
    for p in params:
        if p.split("=")[0] == name and len(p.split("=")) >= 2:
            ret = p[len(p.split("=")[0])+1:]
    return ret


