Usually, they say that overriding socket.socket will make socks proxy usable on any python libraries:
import socks
import socket
socks.set_default_proxy(socks.PROXY_TYPE_SOCKS5, "host", port)
socket.socket = socks.socksocket
However, it would not work well if the machine you want to connect can be connected only from the proxy (i.e. when RDNS is mandatory). This is because socket.create_connection() always resolves the machine's ip address locally:
(cf https://github.com/python/cpython/blob/2.7/Lib/socket.py#L557)
host, port = address
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
af, socktype, proto, canonname, sa = res ### sa is resolved locally!
sock = None
try:
sock = socket(af, socktype, proto) ### this socket is overridden by socks.socksocket, but RDNS will not work well.
sock.connect(sa)
return sock
So, if we need RDNS, create_connection needs to be wrapped as well:
_done_tryPatchCreateConnections = False
def tryPatchCreateConnections():
global _done_tryPatchCreateConnections
if _done_tryPatchCreateConnections:
return True
try:
import socks
import socket
socket.socket = socks.socksocket
def patchCreateConnection(module):
def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None, socket_options=None):
#print('trying to override create_connection')
if socks.socksocket.default_proxy is not None and socks.socksocket.default_proxy[3]:
return socks.create_connection(
address, timeout=timeout, source_address=source_address,
proxy_type=socks.socksocket.default_proxy[0],
proxy_addr=socks.socksocket.default_proxy[1],
proxy_port=socks.socksocket.default_proxy[2],
proxy_rdns=socks.socksocket.default_proxy[3],
proxy_username=socks.socksocket.default_proxy[4],
proxy_password=socks.socksocket.default_proxy[5],
socket_options=socket_options,
)
elif module==socket:
return module._real_create_connection(address, timeout=timeout, source_address=source_address)
else:
# likely urllib3
return module._real_create_connection(address, timeout=timeout, source_address=source_address, socket_options=socket_options)
module._real_create_connection = module.create_connection
module.create_connection = create_connection
module.socket = socks.socksocket
def patchCreateConnections():
patchCreateConnection(socket)
try:
import urllib3
patchCreateConnection(urllib3.util.connection)
except ImportError:
pass
try:
import requests
import requests.packages.urllib3
patchCreateConnection(requests.packages.urllib3.util.connection)
except ImportError:
pass
patchCreateConnections()
_done_tryPatchCreateConnections = True
return True
except ImportError:
return False