Python“请求”库 - 定义特定的 DNS?

新手上路,请多包涵

在我的项目中,我使用 python requests library 处理所有 HTTP 请求。

现在,我需要使用特定的 DNS 查询 http 服务器 - 有两个环境,每个环境都使用自己的 DNS,并且独立进行更改。

因此,当代码运行时,它应该使用特定于环境的 DNS,而不是在我的互联网连接中指定的 DNS。

有没有人使用 python-requests 尝试过这个?我只找到了 urllib2 的解决方案:

https://stackoverflow.com/questions/4623090/python-set-custom-dns-server-for-urllib-requests

原文由 Taku 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 938
2 个回答

requests 使用 urllib3 ,最终使用 httplib.HTTPConnection 以及,所以来自https: //stackions-setonquest03custompythover4.com2 的技术-dns-server-for-urllib-requests (现已删除,它仅链接到 Tell urllib2 to use custom DNS )在一定程度上仍然适用。

The urllib3.connection module subclasses httplib.HTTPConnection under the same name, having replaced the .connect() method with one that calls self._new_conn .反过来,这委托给 urllib3.util.connection.create_connection() 。修补 功能可能是最简单的方法:

 from urllib3.util import connection

_orig_create_connection = connection.create_connection

def patched_create_connection(address, *args, **kwargs):
    """Wrap urllib3's create_connection to resolve the name elsewhere"""
    # resolve hostname to an ip address; use your own
    # resolver here, as otherwise the system resolver will be used.
    host, port = address
    hostname = your_dns_resolver(host)

    return _orig_create_connection((hostname, port), *args, **kwargs)

connection.create_connection = patched_create_connection

并且您将提供自己的代码来将地址的 host 部分解析为 IP 地址,而不是依赖于 connection.create_connection() 调用(包装 socket.create_connection() )为您解析主机名。

像所有的 monkeypatching 一样,请注意代码在以后的版本中没有发生重大变化;此处的补丁是针对 urllib3 版本 1.21.1 创建的。但应该适用于早至 1.9 的版本。


请注意,此答案已重新编写以与更新的 urllib3 版本一起使用,这些版本添加了一个更方便的修补位置。请参阅旧方法的编辑历史,适用于 < 1.9 版本,作为 vendored urllib3 版本的补丁而不是独立安装。

原文由 Martijn Pieters 发布,翻译遵循 CC BY-SA 4.0 许可协议

您应该查看 TransportAdapters ,包括源代码。它们的文档不是很好,但它们提供了对 RFC 2818RFC 6125 中描述的许多功能的低级访问。特别是,这些文档鼓励(要求?)客户端代码支持特定于应用程序的 DNS,以便检查证书的 CommonName 和 SubjectAltName。您在这些调用中需要的关键字参数是“assert_hostname”。以下是使用请求库设置它的方法:

 from requests import Session, HTTPError
from requests.adapters import HTTPAdapter, DEFAULT_POOLSIZE, DEFAULT_RETRIES, DEFAULT_POOLBLOCK

class DNSResolverHTTPSAdapter(HTTPAdapter):
    def __init__(self, common_name, host, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE,
        max_retries=DEFAULT_RETRIES, pool_block=DEFAULT_POOLBLOCK):
        self.__common_name = common_name
        self.__host = host
        super(DNSResolverHTTPSAdapter, self).__init__(pool_connections=pool_connections, pool_maxsize=pool_maxsize,
            max_retries=max_retries, pool_block=pool_block)

    def get_connection(self, url, proxies=None):
        redirected_url = url.replace(self.__common_name, self.__host)
        return super(DNSResolverHTTPSAdapter, self).get_connection(redirected_url, proxies=proxies)

    def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
        pool_kwargs['assert_hostname'] = self.__common_name
        super(DNSResolverHTTPSAdapter, self).init_poolmanager(connections, maxsize, block=block, **pool_kwargs)

common_name = 'SuperSecretSarahServer'
host = '192.168.33.51'
port = 666
base_url = 'https://{}:{}/api/'.format(common_name, port)
my_session = Session()
my_session.mount(self.base_url.lower(), DNSResolverHTTPSAdapter(common_name, host))
user_name = 'sarah'
url = '{}users/{}'.format(self.base_url, user_name)
default_response_kwargs = {
    'auth': (NAME, PASSWORD),
    'headers': {'Content-Type': 'application/json'},
    'verify': SSL_OPTIONS['ca_certs'],
    'cert': (SSL_OPTIONS['certfile'], SSL_OPTIONS['keyfile'])
}
response = my_session.get(url, **default_response_kwargs)

我使用 common_name 作为证书上预期的名称以及您的代码将如何引用所需的机器。我使用 host 作为外部世界可识别的名称 - FQDN、IP、DNS 条目……当然,SSL_OPTIONS 字典(在我的示例中)必须在您的计算机上列出适当的证书/密钥文件名。 (另外,NAME 和 PASSWORD 应该解析为正确的字符串。)

原文由 Sarah Messer 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题