@ironheaddd

Как отправить запрос к сайту через определенный DNS-сервер в python?

Добрый день!
Есть скрипт на python, который подключается к впн через сервис expressvpn, который в свою очередь переписывает файл /etc/resolv.conf и приводит его к виду:
# Generated by expressvpn
search expressvpn
nameserver 10.141.0.1

После подключения к гео (проходит по списку) скрипт запрашивает хэдеры с сайта, но часто возникает ошибка:
HTTPConnectionPool(host='domain.com', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HttpConnection object at 0x7f0fd5234c18>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'))

и после не работают ни curl, ни wget, ни ping. Но если я приведу /etc/resolv.conf к виду:
nameserver 8.8.8.8
то консольные команды работают (скрипт при этом не останавливается и продолжает отправлять запросы к сайту, но ошибка не уходит)
В этой связи возникает вопрос: можно ли указать свой DNS-сервер, чтобы python отправлял запросы ТОЛЬКО через него, не обращаясь к файлу /etc/resolv.conf?
Запрос хэдеров
def get_headers():
    for_write = []
    try:
        print('getting headers...')
        with requests.Session() as sess:
            head = sess.get("http://domain.com", timeout=60)
            print('get is complete!')
            for_write.append(head.headers['x-geo-detected'])
            for_write.append(head.headers['x-monitor'])
            with open("log.txt","a+") as f:
                f.write(str(for_write) + "\n")
            return True
    except BaseException as exc:
        print(exc)
        for_write.append('Don\'t get headers from domain.com with ' + str(exc))
        with open("log.txt","a+") as f:
            f.write(str(for_write) + "\n")
        return False


def main():
    open("log.txt","w").close() 
    while True:
        with open ("country_final.txt", "r") as country:
            for geo in country:
                i = 0
                geo = geo.replace("\n","")
                connect(geo)
                time.sleep(10)
                status()
                gh = get_headers()
                while gh == False:
                    i += 1
                    print('headers is False! Reconnecting...')
                    disconnect()
                    time.sleep(10)
                    connect(geo)
                    time.sleep(10)
                    gh = get_headers()
                    if i == 10:
                        break
                disconnect()
        send_log()

if __name__ == "__main__":
    main()


PS: в python не умею, это первый опыт пользования языком, пошла третья неделя "жизни" скрипта)
  • Вопрос задан
  • 421 просмотр
Пригласить эксперта
Ответы на вопрос 1
Vindicar
@Vindicar
RTFM!
Есть довольно старый ответ на стэковерфлоу. requests под капотом использует urllib3, а та содержит функцию urllib3.util.connection.create_connection(), ответственную за установку TCP соединения. Соответственно, можно попробовать подменить эту функцию на свою реализацию, и использовать пакет dnspython для запросов.
from urllib3.util import connection
import dns.resolver

def your_dns_resolver(hostname: str) -> str:
    """Получает на вход доменное имя, возвращает IP адрес в виде строки."""
    resolver = dns.resolver.Resolver(configure=False)
    resolver.nameservers = ["8.8.8.8"]  # список используемых DNS серверов.
    answer = resolver.resolve(hostname, "A")  # A-запись для IPv4 адреса, AAAA-запись для IPv6 адреса
    if len(answer) == 0:
        raise Exception("No A record found")
    return str(answer[0])

_orig_create_connection = connection.create_connection

def patched_create_connection(address, *args, **kwargs):
    """Wrap urllib3's create_connection to resolve the name elsewhere"""
    host, port = address  # оригинальный адрес для запроса
    hostname = your_dns_resolver(host)  # сами определяем IP по имени
    return _orig_create_connection((hostname, port), *args, **kwargs)  # дёргаем оригинальную функцию, чтобы создать соединение

connection.create_connection = patched_create_connection

Код не проверял, это скорее концепт чем готовый инструмент.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы