Can I change SOCKS proxy within a function using SocksiPy? - python

I'm trying to write a function which will take a URL and return the contents of that URL. There is one additional argument (useTor) which, when set to True, will use SocksiPy to route the request over a SOCKS 5 proxy server (in this case, Tor).
I can set the proxy globally for all connections just fine but I cannot work out two things:
How can I move this setting into a function so that it can be decided on the useTor variable? I'm unable to access socks within the function and have no idea how to do so.
I'm assuming that if I don't set the proxy, then the next time the request is made it'll go direct. The SocksiPy documentation doesn't seem to give any indication of as to how the proxy is reset.
Can anyone advise? My (beginners) code is below:
import gzip
import socks
import socket
def create_connection(address, timeout=None, source_address=None):
sock = socks.socksocket()
sock.connect(address)
return sock
# next line works just fine if I want to set the proxy globally
# socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
socket.socket = socks.socksocket
socket.create_connection = create_connection
import urllib2
import sys
def getURL(url, useTor=False):
if useTor:
print "Using tor..."
# Throws- AttributeError: 'module' object has no attribute 'setproxy'
socks.setproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
else:
print "Not using tor..."
# Not sure how to cancel the proxy, assuming it persists
opener = urllib2.build_opener()
usock = opener.open(url)
url = usock.geturl()
encoding = usock.info().get("Content-Encoding")
if encoding in ('gzip', 'x-gzip', 'deflate'):
content = usock.read()
if encoding == 'deflate':
data = StringIO.StringIO(zlib.decompress(content))
else:
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(content))
result = data.read()
else:
result = usock.read()
usock.close()
return result
# Connect to the same site both with and without using Tor
print getURL('https://check.torproject.org', False)
print getURL('https://check.torproject.org', True)

Example
Simply invoke socksocket.set_proxy with no arguments, this will effectively remove any previously set proxy settings.
import socks
sck = socks.socksocket ()
# use TOR
sck.setproxy (socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
# reset to normal use
sck.setproxy ()
Details
By looking at the source of socks.py, and digging into the contents of socksocket.setproxy, we quickly realize that in order to discard of any previous proxy attributes we simply invoke the function with no additional arguments (besides self).
class socksocket(socket.socket):
... # additional functionality ignored
def setproxy(self,proxytype=None,addr=None,port=None,rdns=True,username=None,password=None):
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets the proxy to be used.
proxytype - The type of the proxy to be used. Three types
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
addr - The address of the server (IP or DNS).
port - The port of the server. Defaults to 1080 for SOCKS
servers and 8080 for HTTP proxy servers.
rdns - Should DNS queries be preformed on the remote side
(rather than the local side). The default is True.
Note: This has no effect with SOCKS4 servers.
username - Username to authenticate with to the server.
The default is no authentication.
password - Password to authenticate with to the server.
Only relevant when username is also provided.
"""
self.__proxy = (proxytype,addr,port,rdns,username,password)
... # additional functionality ignored
Note: When a new connection is about to be negotiated, the implementation will use the contents of self.__proxy unless the potentially required element is None (in which case the setting is simply ignored).

Related

python ssl (eqivalent of openssl s_client -showcerts ) How to get list of CAs for client certs from server

I have a group of nginx servers, that accept client certificates.
They have the ssl_client_certificate option with a file containing one or more CAs
If I use a web browser, then the web browser seems to receive a list of valid CAs for client certs. The browser shows only client certs signed by one of these CAs.
Following openssl command gives me a list of CA certs:
openssl s_client -showcerts -servername myserver.com -connect myserver.com:443 </dev/null
The lines I am interested in look following way:
---
Acceptable client certificate CA names
/C=XX/O=XX XXXX
/C=YY/O=Y/OU=YY YYYYYL
...
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
How can I get the same information with python?
I do have following code snippet, that allows to obtain a server's certificate, but this code does not return the list of CAs for client certs.
import ssl
def get_server_cert(hostname, port):
conn = ssl.create_connection((hostname, port))
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sock = context.wrap_socket(conn, server_hostname=hostname)
cert = sock.getpeercert(True)
cert = ssl.DER_cert_to_PEM_cert(cert)
return cerft
I expected to find a functional equivalent of getpeercert(), something like getpeercas() but didn't find anything.
Current workaround:
import os
import subprocess
def get_client_cert_cas(hostname, port):
"""
returns a list of CAs, for which client certs are accepted
"""
cmd = [
"openssl",
"s_client",
"-showcerts",
"-servername", hostname,
"-connect", hostname + ":" + str(port),
]
stdin = open(os.devnull, "r")
stderr = open(os.devnull, "w")
output = subprocess.check_output(cmd, stdin=stdin, stderr=stderr)
ca_signatures = []
state = 0
for line in output.decode().split("\n"):
print(state, line)
if state == 0:
if line == "Acceptable client certificate CA names":
state = 1
elif state == 1:
if line.startswith("Client Certificate Types:"):
break
ca_signatures.append(line)
return ca_signatures
Update:Solution with pyopenssl (Thanks Steffen Ullrich)
#Steffen Ulrich suggested to use pyopenssl, which has a method get_client_ca_list() and this helped me to write a small code snippet.
Below code seems to work. Not sure if it can be improved or whether there are any pit falls.
If nobody is answering within the next days I will post this as a potential answer.
import socket
from OpenSSL import SSL
def get_client_cert_cas(hostname, port):
ctx = SSL.Context(SSL.SSLv23_METHOD)
# If we don't force to NOT use TLSv1.3 get_client_ca_list() returns
# an empty result
ctx.set_options(SSL.OP_NO_TLSv1_3)
sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
# next line for SNI
sock.set_tlsext_host_name(hostname.encode("utf-8"))
sock.connect((hostname, port))
# without handshake get_client_ca_list will be empty
sock.do_handshake()
return sock.get_client_ca_list()
Update: 2021-03-31
Above suggested solution using pyopenssl works in most cases.
However sock.get_client_ca_list()) cannot be called immediately after performing a sock.connect((hostname, port))
Some actions seem to be required in between these two commands.
Initially I used sock.send(b"G"), but now I use sock.do_handshake(), which seems a little cleaner.
Even stranger, the solution doesn't work with TLSv1.3 so I had to exclude it.
As a generic example in python
first you need to contact the server to learn which issuer CA subjects it accepts:
from socket import socket, AF_INET, SOCK_STREAM
from OpenSSL import SSL
from OpenSSL.crypto import X509Name
from certifi import where
import idna
def get_server_expected_client_subjects(host :str, port :int = 443) -> list[X509Name]:
expected_subjects = []
ctx = SSL.Context(method=SSL.SSLv23_METHOD)
ctx.verify_mode = SSL.VERIFY_NONE
ctx.check_hostname = False
conn = SSL.Connection(ctx, socket(AF_INET, SOCK_STREAM))
conn.connect((host, port))
conn.settimeout(3)
conn.set_tlsext_host_name(idna.encode(host))
conn.setblocking(1)
conn.set_connect_state()
try:
conn.do_handshake()
expected_subjects :list[X509Name] = conn.get_client_ca_list()
except SSL.Error as err:
raise SSL.Error from err
finally:
conn.close()
return expected_subjects
This did not have the client certificate, so the TLS connection would fail. There are a lot of bad practices here, but unfortunately they are necessary and the only way to gather the message from the server before we actually want to attempt client authentication using hte correct certificate.
Next you load the cert based on the server:
from pathlib import Path
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
from pathlib import Path
def check_client_cert_issuer(client_pem :str, expected_subjects :list) -> str:
client_cert = None
if len(expected_subjects) > 0:
client_cert_path = Path(client_pem)
cert = load_certificate(FILETYPE_PEM, client_cert_path.read_bytes())
issuer_subject = cert.get_issuer()
for check in expected_subjects:
if issuer_subject.commonName == check.commonName:
client_cert = client_pem
break
if client_cert is None or not isinstance(client_cert, str):
raise Exception('X509_V_ERR_SUBJECT_ISSUER_MISMATCH') # OpenSSL error code 29
return client_cert
In a real app (not an example snippet) you would have a database of some sort to take the server subject and lookup the location of the cert to load - this example does it in reverse for demonstration only.
Make the TLS connection, and capture any OpenSSL errors:
from socket import socket, AF_INET, SOCK_STREAM
from OpenSSL import SSL
from OpenSSL.crypto import X509, FILETYPE_PEM
from certifi import where
import idna
def openssl_verifier(conn :SSL.Connection, server_cert :X509, errno :int, depth :int, preverify_ok :int):
ok = 1
verifier_errors = conn.get_app_data()
if not isinstance(verifier_errors, list):
verifier_errors = []
if errno in OPENSSL_CODES.keys():
ok = 0
verifier_errors.append((server_cert, OPENSSL_CODES[errno]))
conn.set_app_data(verifier_errors)
return ok
client_pem = '/path/to/client.pem'
client_issuer_ca = '/path/to/ca.pem'
host = 'example.com'
port = 443
ctx = SSL.Context(method=SSL.SSLv23_METHOD) # will negotiate TLS1.3 or lower protocol, what every is highest possible during negotiation
ctx.load_verify_locations(cafile=where())
if client_pem is not None:
ctx.use_certificate_file(certfile=client_pem, filetype=FILETYPE_PEM)
if client_issuer_ca is not None:
ctx.load_client_ca(cafile=client_issuer_ca)
ctx.set_verify(SSL.VERIFY_NONE, openssl_verifier)
ctx.check_hostname = False
conn = SSL.Connection(ctx, socket(AF_INET, SOCK_STREAM))
conn.connect((host, port))
conn.settimeout(3)
conn.set_tlsext_host_name(idna.encode(host))
conn.setblocking(1)
conn.set_connect_state()
try:
conn.do_handshake()
verifier_errors = conn.get_app_data()
except SSL.Error as err:
raise SSL.Error from err
finally:
conn.close()
# handle your errors in your main app
print(verifier_errors)
Just make sure you handle those OPENSSL_CODES errors if any are encountered, the lookup dictionary is here.
Many validations occur pre verification inside OpenSSL itself and all PyOpenSSL will do is a basic validation. so we need to access these codes from OpenSSL if we want to do Client Authentication, i.e. on the client and throw away the response from an untrusted server if it fails any authentication checks on the client side, per Client Authorisation or rather mutual-TLS dictates
#Stof's solution is more complete than this one.
So I selected his answer as 'official' answer.
This answer predates his, but might still be of some interest.
With #Steffen Ullrich's help I found following solution,
which works for all the (nginx with a ssl_client_certificate setting) servers that I tested with.
It requires to install an external package
pip install pyopenssl
Then following work will work:
import socket
from OpenSSL import SSL
def get_client_cert_cas(hostname, port):
ctx = SSL.Context(SSL.SSLv23_METHOD)
# If we don't force to NOT use TLSv1.3 get_client_ca_list() returns
# an empty result
ctx.set_options(SSL.OP_NO_TLSv1_3)
sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
# next line for SNI
sock.set_tlsext_host_name(hostname.encode("utf-8"))
sock.connect((hostname, port))
# without handshake get_client_ca_list will be empty
sock.do_handshake()
return sock.get_client_ca_list()
The line sock.do_handshake() is required to trigger enough of the SSL protocol. Otherwise client_ca_list information doesn't seem to be populated.
At least for the servers, that I tested I had to make sure TLSv1.3 is not used. I don't know whether this is a bug, a feature or whether with TLSv1.3 another function has to be called prior to calling get_client_ca_list()
I am no pyopenssl expert, but could imagine, that there is a more elegant / more explicit way to get the same behavior.
but so far this works for me for all the servers, that I encountered.

How to close requests.Session()?

I am trying to close a requests.Session() but its not getting closed.
s = requests.Session()
s.verify = 'cert.pem'
res1 = s.get("https://<ip>:<port>/<route>")
s.close() #Not working
res2 = s.get("https://<ip>:<port>/<route>") # this is still working which means s.close() didn't work.
How do I close the session? s.close() is not throwing any error also which means it is a valid syntax but I am not understanding what exactly it is doing.
In requests's source code, Session.close only close all underlying Adapter. And further closing a Adapter is clearing underlying PoolManager. Then all the
established connections inside this PoolManager will be closed. But PoolManager will create a fresh connection if there is no usable connection.
Critical code:
# requests.Session
def close(self):
"""Closes all adapters and as such the session"""
for v in self.adapters.values():
v.close()
# requests.adapters.HTTPAdapter
def close(self):
"""Disposes of any internal state.
Currently, this closes the PoolManager and any active ProxyManager,
which closes any pooled connections.
"""
self.poolmanager.clear()
for proxy in self.proxy_manager.values():
proxy.clear()
# urllib3.poolmanager.PoolManager
def connection_from_pool_key(self, pool_key, request_context=None):
"""
Get a :class:`ConnectionPool` based on the provided pool key.
``pool_key`` should be a namedtuple that only contains immutable
objects. At a minimum it must have the ``scheme``, ``host``, and
``port`` fields.
"""
with self.pools.lock:
# If the scheme, host, or port doesn't match existing open
# connections, open a new ConnectionPool.
pool = self.pools.get(pool_key)
if pool:
return pool
# Make a fresh ConnectionPool of the desired type
scheme = request_context['scheme']
host = request_context['host']
port = request_context['port']
pool = self._new_pool(scheme, host, port, request_context=request_context)
self.pools[pool_key] = pool
return pool
So if I understand its structure well, when you close a Session, you are almost the same as creating a new Session and assign it to old one. So you can still use it to send request.
Or if I misunderstand anything, welcome to correct me :D

Request from different IP addresses

I want to use urllib2 and make a request from different IP addresses.
I have checked this but I am having no luck: Source interface with Python and urllib2
Code from link:
class BoundHTTPHandler(urllib2.HTTPHandler):
def __init__(self, source_address=None, debuglevel=0):
urllib2.HTTPHandler.__init__(self, debuglevel)
self.http_class = functools.partial(httplib.HTTPConnection,
source_address=source_address)
def http_open(self, req):
return self.do_open(self.http_class, req)
# test
handler = BoundHTTPHandler("192.168.1.1", 0)
opener = urllib2.build_opener(handler)
urllib2.install_opener(opener)
urllib2.urlopen("http://google.com/").read()
Error: TypeError: init() got an unexpected keyword argument 'source_address'
And how would I run this code before using urllib2?
import socket
true_socket = socket.socket
def bound_socket(*a, **k):
sock = true_socket(*a, **k)
sock.bind((sourceIP, 0))
return sock
socket.socket = bound_socket
So you have the bound_socket function, then what?
Edit I don't believe my python version supports the source_address, which is why I'm getting the error, I think.
So, let's try the socket code.
The code socket.socket = bound_socket affects all code that is run after it in any module globally i.e., once you run it; you don't need to do anything else.
httplib.HTTPConnection class has source_address parameter in Python 2.7 therefore your BoundHTTPHandler should also work.

Python + Twisted + FtpClient + SOCKS

I just started using Twisted. I want to connect to an FTP server and perform some basic operations (use threading if possible). I am using this example.
Which does the job quite well. The question is how to add a SOCKS4/5 proxy usage to the code? Can somebody please provide a working example? I have tried this link too.
But,
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
An example of using the FTP client
"""
# Twisted imports
from twisted.protocols.ftp import FTPClient, FTPFileListProtocol
from twisted.internet.protocol import Protocol, ClientCreator
from twisted.python import usage
from twisted.internet import reactor, endpoints
# Socks support test
from socksclient import SOCKSv4ClientProtocol, SOCKSWrapper
from twisted.web import client
# Standard library imports
import string
import sys
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class BufferingProtocol(Protocol):
"""Simple utility class that holds all data written to it in a buffer."""
def __init__(self):
self.buffer = StringIO()
def dataReceived(self, data):
self.buffer.write(data)
# Define some callbacks
def success(response):
print 'Success! Got response:'
print '---'
if response is None:
print None
else:
print string.join(response, '\n')
print '---'
def fail(error):
print 'Failed. Error was:'
print error
def showFiles(result, fileListProtocol):
print 'Processed file listing:'
for file in fileListProtocol.files:
print ' %s: %d bytes, %s' \
% (file['filename'], file['size'], file['date'])
print 'Total: %d files' % (len(fileListProtocol.files))
def showBuffer(result, bufferProtocol):
print 'Got data:'
print bufferProtocol.buffer.getvalue()
class Options(usage.Options):
optParameters = [['host', 'h', 'example.com'],
['port', 'p', 21],
['username', 'u', 'webmaster'],
['password', None, 'justapass'],
['passive', None, 0],
['debug', 'd', 1],
]
# Socks support
def wrappercb(proxy):
print "connected to proxy", proxy
pass
def run():
def sockswrapper(proxy, url):
dest = client._parse(url) # scheme, host, port, path
endpoint = endpoints.TCP4ClientEndpoint(reactor, dest[1], dest[2])
return SOCKSWrapper(reactor, proxy[1], proxy[2], endpoint)
# Get config
config = Options()
config.parseOptions()
config.opts['port'] = int(config.opts['port'])
config.opts['passive'] = int(config.opts['passive'])
config.opts['debug'] = int(config.opts['debug'])
# Create the client
FTPClient.debug = config.opts['debug']
creator = ClientCreator(reactor, FTPClient, config.opts['username'],
config.opts['password'], passive=config.opts['passive'])
#creator.connectTCP(config.opts['host'], config.opts['port']).addCallback(connectionMade).addErrback(connectionFailed)
# Socks support
proxy = (None, '1.1.1.1', 1111, True, None, None)
sw = sockswrapper(proxy, "ftp://example.com")
d = sw.connect(creator)
d.addCallback(wrappercb)
reactor.run()
def connectionFailed(f):
print "Connection Failed:", f
reactor.stop()
def connectionMade(ftpClient):
# Get the current working directory
ftpClient.pwd().addCallbacks(success, fail)
# Get a detailed listing of the current directory
fileList = FTPFileListProtocol()
d = ftpClient.list('.', fileList)
d.addCallbacks(showFiles, fail, callbackArgs=(fileList,))
# Change to the parent directory
ftpClient.cdup().addCallbacks(success, fail)
# Create a buffer
proto = BufferingProtocol()
# Get short listing of current directory, and quit when done
d = ftpClient.nlst('.', proto)
d.addCallbacks(showBuffer, fail, callbackArgs=(proto,))
d.addCallback(lambda result: reactor.stop())
# this only runs if the module was *not* imported
if __name__ == '__main__':
run()
I know the code is wrong. I need Solution.
Okay, so here's a solution (gist) that uses python's built-in ftplib, as well as the open source SocksiPy module.
It doesn't use twisted, and it doesn't explicitly use threads, but using and communicting between threads is pretty easily done with threading.Thread and threading.Queue in python's standard threading module
Basically, we need to subclass ftplib.FTP to support substituting our own create_connection method and add proxy configuration semantics.
The "main" logic just configures an FTP client that connects via a localhost socks proxy, such as one created by ssh -D localhost:1080 socksproxy.example.com, and downloads a source snapshot for GNU autoconf to the local disk.
import ftplib
import socket
import socks # socksipy (https://github.com/mikedougherty/SocksiPy)
class FTP(ftplib.FTP):
def __init__(self, host='', user='', passwd='', acct='',
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
proxyconfig=None):
"""Like ftplib.FTP constructor, but with an added `proxyconfig` kwarg
`proxyconfig` should be a dictionary that may contain the following
keys:
proxytype - The type of the proxy to be used. Three types
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
addr - The address of the server (IP or DNS).
port - The port of the server. Defaults to 1080 for SOCKS
servers and 8080 for HTTP proxy servers.
rdns - Should DNS queries be preformed on the remote side
(rather than the local side). The default is True.
Note: This has no effect with SOCKS4 servers.
username - Username to authenticate with to the server.
The default is no authentication.
password - Password to authenticate with to the server.
Only relevant when username is also provided.
"""
self.proxyconfig = proxyconfig or {}
ftplib.FTP.__init__(self, host, user, passwd, acct, timeout)
def connect(self, host='', port=0, timeout=-999):
'''Connect to host. Arguments are:
- host: hostname to connect to (string, default previous host)
- port: port to connect to (integer, default previous port)
'''
if host != '':
self.host = host
if port > 0:
self.port = port
if timeout != -999:
self.timeout = timeout
self.sock = self.create_connection(self.host, self.port)
self.af = self.sock.family
self.file = self.sock.makefile('rb')
self.welcome = self.getresp()
return self.welcome
def create_connection(self, host=None, port=None):
host, port = host or self.host, port or self.port
if self.proxyconfig:
phost, pport = self.proxyconfig['addr'], self.proxyconfig['port']
err = None
for res in socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
sock = None
try:
sock = socks.socksocket(af, socktype, proto)
sock.setproxy(**self.proxyconfig)
if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
sock.settimeout(self.timeout)
sock.connect((host, port))
return sock
except socket.error as _:
err = _
if sock is not None:
sock.close()
if err is not None:
raise err
else:
raise socket.error("getaddrinfo returns an empty list")
else:
sock = socket.create_connection((host, port), self.timeout)
return sock
def ntransfercmd(self, cmd, rest=None):
size = None
if self.passiveserver:
host, port = self.makepasv()
conn = self.create_connection(host, port)
try:
if rest is not None:
self.sendcmd("REST %s" % rest)
resp = self.sendcmd(cmd)
# Some servers apparently send a 200 reply to
# a LIST or STOR command, before the 150 reply
# (and way before the 226 reply). This seems to
# be in violation of the protocol (which only allows
# 1xx or error messages for LIST), so we just discard
# this response.
if resp[0] == '2':
resp = self.getresp()
if resp[0] != '1':
raise ftplib.error_reply, resp
except:
conn.close()
raise
else:
raise Exception("Active transfers not supported")
if resp[:3] == '150':
# this is conditional in case we received a 125
size = ftplib.parse150(resp)
return conn, size
if __name__ == '__main__':
ftp = FTP(host='ftp.gnu.org', user='anonymous', passwd='guest',
proxyconfig=dict(proxytype=socks.PROXY_TYPE_SOCKS5, rdns=False,
addr='localhost', port=1080))
with open('autoconf-2.69.tar.xz', mode='w') as f:
ftp.retrbinary("RETR /gnu/autoconf/autoconf-2.69.tar.xz", f.write)
To elaborate why I asked some of my original questions:
1) Do you need to support active transfers or will PASV transfers be sufficient?
Active transfers are much harder to do via a socks proxy because they require the use of the PORT command. With the PORT command, your ftp client tells the FTP server to connect to you on a specific port (e.g., on your PC) in order to send the data. This is likely to not work for users behind a firewall or NAT/router. If your SOCKS proxy server is not behind a firewall, or has a public IP, it is possible to support active transfers, but it is complicated: It requires your SOCKS server (ssh -D does support this) and client library (socksipy does not) to support remote port binding. It also requires the appropriate hooks in the application (my example throws an exception if passiveserver = False) to do a remote BIND instead of a local one.
2) Does it have to use twisted?
Twisted is great, but I'm not the best at it, and I haven't found a really great SOCKS client implementation. Ideally there would be a library out there that allowed you to define and/or chain proxies together, returning an object that implements the IReactorTCP interface, but I have not yet found anything like this just yet.
3) Is your socks proxy behind a VIP or just a single host directly connected to the Internet?
This matters because of the way PASV transfer security works. In a PASV transfer, the client asks the server to provide a port to connect in order to start a data transfer. When the server accepts a connection on that port, it SHOULD verify the client is connected from the same source IP as the connection that requested the transfer. If your SOCKS server is behind a VIP, it is less likely that the outbound IP of the connection made for the PASV transfers will match the outbound IP of the primary communication connection.

How to change Tor identity in Python?

I have the following script:
import socks
import socket
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
socket.socket = socks.socksocket
import urllib2
print(urllib2.urlopen("http://www.ifconfig.me/ip").read())
which uses tor and SocksiPy
Now I want to change tor identity with each request, for example:
for i in range(0, 10):
#somehow change tor identity
print(urllib2.urlopen("http://www.ifconfig.me/ip").read())
How can I do this?
Tor wrote a new TOR control library in Python, stem. It can be found on PyPI. They provide some nice tutorials how to work with it, one of them explains how to change your identity:
from stem import Signal
from stem.control import Controller
with Controller.from_port(port = 9051) as controller:
controller.authenticate()
controller.signal(Signal.NEWNYM)
Make sure your config is correct.
Today, I have searched a lot about this question, and finally managed to answer myself. But before I need to say that pirvoxy and tor should be configured correctly. First script, then a little bit about configuration:
import urllib2
from TorCtl import TorCtl
proxy_support = urllib2.ProxyHandler({"http" : "127.0.0.1:8118"})
opener = urllib2.build_opener(proxy_support)
def newId():
conn = TorCtl.connect(controlAddr="127.0.0.1", controlPort=9051, passphrase="your_password")
conn.send_signal("NEWNYM")
for i in range(0, 10):
print "case "+str(i+1)
newId()
proxy_support = urllib2.ProxyHandler({"http" : "127.0.0.1:8118"})
urllib2.install_opener(opener)
print(urllib2.urlopen("http://www.ifconfig.me/ip").read())
Above script gets new IP and checks it from ifconfig.me web site. About configuration:
We need Privoxy. to use TOR with HTTP connections, privoxy should work with tor. We can do it by adding thi to /etc/privoxy/config file:
forward-socks5 / localhost:9050 . #dot is important at the end
then we configure ControlPort in /etc/tor/torrc file. We need just uncomment this line:
ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
then we just restart tor:
/etc/init.d/tor restart
Another simple solution, no external libraries required, works for both IPv4 and IPv6:
import socket
try:
tor_c = socket.create_connection((TOR_CTRL_HOST, TOR_CTRL_PORT))
tor_c.send('AUTHENTICATE "{}"\r\nSIGNAL NEWNYM\r\n'.format(TOR_CTRL_PWD))
response = tor_c.recv(1024)
if response != '250 OK\r\n250 OK\r\n':
sys.stderr.write('Unexpected response from Tor control port: {}\n'.format(response))
except Exception, e:
sys.stderr.write('Error connecting to Tor control port: {}\n'.format(repr(e)))
This is a video where im using STEM, SockSipy, Tor 100% working :)
#!/usr/bin/python
import socks
import socket
import time
from stem.control import Controller
from stem import Signal
import urllib2
import sys
def info():
print "[*] Welcome to Chart-Cheat Script"
print "[*] This script works with running TOR only"
print "[*] usage is chartcheat.py domain"
print "[*] argument domain must be in format www.example.com"
print "[*] Example: chartcheat.py www.example.com"
return
if len(sys.argv)==2:
info();
counter = 0
url = str(sys.argv[1]);
with Controller.from_port(port = 9051) as controller:
controller.authenticate()
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
socket.socket = socks.socksocket
#visiting url in infinite loop
while True:
urllib2.urlopen("http://"+url)
counter=counter+1
print "Page " + url + " visited = " + str(counter)
#wait till next identity will be available
controller.signal(Signal.NEWNYM)
time.sleep(controller.get_newnym_wait())
else:
info();
In case you are running python3, urllib package in python3 will be the same as urllib2 package in python2.
You can enable tor control server by uncommenting few lines in
/etc/tor/torrc
And use stem library to send NEWNYM signal to change circuit.
controller.signal(Signal.NEWNYM)
You can read tutorial here.
you can write something like this :
def renew_connection():
with Controller.from_port(port=9051) as controller:
controller.authenticate(password='password')
controller.signal(Signal.NEWNYM)
controller.close()
def request_tor(url, headers):
renew_connection()
session = requests.session()
session.proxies = {}
session.proxies['http'] = 'socks5h://localhost:9050'
print((session.get(url)).text)
The following could work:
for i in range(0, 10):
#somehow change tor identity
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050+i)
socket.socket = socks.socksocket
print(urllib2.urlopen("http://www.ifconfig.me/ip").read())
You basically set set the proxy prior to making each connection. I am asuming that you have different proxies for different IPs since you have not stated how you intend to change the IP

Categories