Buc*_*uck 5 python python-requests
我正在编写一些python客户端代码,由于某些环境限制,我想指定一个URL并控制其解析方式。我可以使用--resolve标志使用curl来完成此操作。有没有办法对Python的请求库做类似的事情?
理想情况下,这将在Python 2.7中起作用,但我也可以使3.x解决方案也起作用。
dhu*_*ker 13
我已经尝试找出解决方案有一段时间了,最后偶然发现了这篇文章。@supersam654 提供的解决方案并没有立即对我起作用(使用 https 和 python 3.8),但是几天的睡眠让我得到了这个无论版本如何都有效的解决方案(没有测试太多版本,但天真地希望情况就是如此)。
它也应该适用于 ipv6 - 尽管我也没有测试过。
解决方案的关键是对所有调用使用默认的 getaddrinfo() (对其输出不做任何假设) - 只需将主机名替换为 IP 地址即可覆盖它!因此,我对它的效果做出了宏大的声明;-)
import socket
dns_cache = {}
# Capture a dict of hostname and their IPs to override with
def override_dns(domain, ip):
dns_cache[domain] = ip
prv_getaddrinfo = socket.getaddrinfo
# Override default socket.getaddrinfo() and pass ip instead of host
# if override is detected
def new_getaddrinfo(*args):
if args[0] in dns_cache:
print("Forcing FQDN: {} to IP: {}".format(args[0], dns_cache[args[0]]))
return prv_getaddrinfo(dns_cache[args[0]], *args[1:])
else:
return prv_getaddrinfo(*args)
socket.getaddrinfo = new_getaddrinfo
Run Code Online (Sandbox Code Playgroud)
要使用上述逻辑 - 只需在发出请求之前调用该函数(您可以使用 IP 地址或其他 FQDN 覆盖!):
override_dns('www.example.com', '192.168.1.100')
Run Code Online (Sandbox Code Playgroud)
我相信这是比我之前使用的ForcedIPHTTPSAdapter更好的解决方案。
在做了一些挖掘之后,我(不出所料)发现 Requests 通过要求 Python 来解析主机名(这是要求您的操作系统来做)。首先,我找到了一些示例代码来劫持 DNS 解析(告诉 urllib2 使用自定义 DNS),然后我在套接字文档中找到了有关 Python 如何解析主机名的更多细节。然后这只是将所有东西连接在一起的问题:
import socket
import requests
def is_ipv4(s):
# Feel free to improve this: /sf/ask/827957301/
return ':' not in s
dns_cache = {}
def add_custom_dns(domain, port, ip):
key = (domain, port)
# Strange parameters explained at:
# https://docs.python.org/2/library/socket.html#socket.getaddrinfo
# Values were taken from the output of `socket.getaddrinfo(...)`
if is_ipv4(ip):
value = (socket.AddressFamily.AF_INET, 0, 0, '', (ip, port))
else: # ipv6
value = (socket.AddressFamily.AF_INET6, 0, 0, '', (ip, port, 0, 0))
dns_cache[key] = [value]
# Inspired by: /sf/answers/1054599801/
prv_getaddrinfo = socket.getaddrinfo
def new_getaddrinfo(*args):
# Uncomment to see what calls to `getaddrinfo` look like.
# print(args)
try:
return dns_cache[args[:2]] # hostname and port
except KeyError:
return prv_getaddrinfo(*args)
socket.getaddrinfo = new_getaddrinfo
# Redirect example.com to the IP of test.domain.com (completely unrelated).
add_custom_dns('example.com', 80, '66.96.162.92')
res = requests.get('http://example.com')
print(res.text) # Prints out the HTML of test.domain.com.
Run Code Online (Sandbox Code Playgroud)
我在写这篇文章时遇到了一些警告:
https. 代码工作正常(只需使用https://and443而不是http://and 80)。但是,SSL 证书与域名相关联,Requests 将尝试将证书上的名称验证为您尝试连接到的原始域。getaddrinfo返回 IPv4 和 IPv6 地址略有不同的信息。我的实现对我来说is_ipv4感觉很糟糕,如果您在实际应用程序中使用它,我强烈推荐一个更好的版本。迟到的答案,但有一个名为forcediphttpsadapter的模块正是这样做的:
pip3 install forcediphttpsadapter
Run Code Online (Sandbox Code Playgroud)
import requests
from forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter
url = 'https://domain.tld/path'
session = requests.Session()
session.mount(url, ForcedIPHTTPSAdapter(dest_ip='x.x.x.x')) # type the desired ip
r = session.get(url, verify=False)
print(r.text)
...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1915 次 |
| 最近记录: |