18

Sample code:

#!/usr/bin/python
import socks
import socket
import urllib2

socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS4, "127.0.0.1", 9050, True)
socket.socket = socks.socksocket

print urllib2.urlopen("http://almien.co.uk/m/tools/net/ip/").read()

TOR is running a SOCKS proxy on port 9050 (its default). The request goes through TOR, surfacing at an IP address other than my own. However, TOR console gives the warning:

"Feb 28 22:44:26.233 [warn] Your application (using socks4 to port 80) is giving Tor only an IP address. Applications that do DNS resolves themselves may leak information. Consider using Socks4A (e.g. via privoxy or socat) instead. For more information, please see https://wiki.torproject.org/TheOnionRouter/TorFAQ#SOCKSAndDNS."

i.e. DNS lookups aren't going through the proxy. But that's what the 4th parameter to setdefaultproxy is supposed to do, right?

From http://socksipy.sourceforge.net/readme.txt:

setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])

rdns - This is a boolean flag than modifies the behavior regarding DNS resolving. If it is set to True, DNS resolving will be preformed remotely, on the server.

Same effect with both PROXY_TYPE_SOCKS4 and PROXY_TYPE_SOCKS5 selected.

It can't be a local DNS cache (if urllib2 even supports that) because it happens when I change the URL to a domain that this computer has never visited before.

OJW
  • 4,514
  • 6
  • 40
  • 48

3 Answers3

21

The problem is that httplib.HTTPConnection uses the socket module's create_connection helper function which does the DNS request via the usual getaddrinfo method before connecting the socket.

The solution is to make your own create_connection function and monkey-patch it into the socket module before importing urllib2, just like we do with the socket class.

import socks
import socket
def create_connection(address, timeout=None, source_address=None):
    sock = socks.socksocket()
    sock.connect(address)
    return sock

socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)

# patch the socket module
socket.socket = socks.socksocket
socket.create_connection = create_connection

import urllib2

# Now you can go ahead and scrape those shady darknet .onion sites
Gareth Davidson
  • 4,857
  • 2
  • 26
  • 45
  • 6
    NOTE: The new SOCKS port seems to be 9150. I literally spent an hour, disabling the firewall etc. trying to figure out why it didn't connect... – Mastergalen Nov 03 '13 at 14:33
  • I needed to remove the monkey patching to get it working in 2.7 – hoju Mar 17 '15 at 08:21
  • @Mastergalen, not really. It could be any port if you change the default settings in your TOR config. The default is SocksPort 9150 and ControlPort 9151. – Aleksandr Feb 21 '17 at 14:49
5

The problem is that you are importing urllib2 before you set up the socks connection.

Try this instead:

import socks
import socket

socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS4, '127.0.0.1', 9050, True)
socket.socket = socks.socksocket

import urllib2
print urllib2.urlopen("http://almien.co.uk/m/tools/net/ip/").read()

Manual request example:

import socks                                                         
import urlparse                                                      

SOCKS_HOST = 'localhost'                                             
SOCKS_PORT = 9050                                                    
SOCKS_TYPE = socks.PROXY_TYPE_SOCKS5                                 

url = 'http://www.whatismyip.com/automation/n09230945.asp'           
parsed = urlparse.urlparse(url)                                      


socket = socks.socksocket()                                          
socket.setproxy(SOCKS_TYPE, SOCKS_HOST, SOCKS_PORT)                  
socket.connect((parsed.netloc, 80))                                  
socket.send('''GET %(uri)s HTTP/1.1                                  
host: %(host)s                                                       
connection: close                                                    

''' % dict(                                                          
    uri=parsed.path,                                                 
    host=parsed.netloc,                                              
))                                                                   

print socket.recv(1024)                                              
socket.close()
Wolph
  • 78,177
  • 11
  • 137
  • 148
  • Actually that was the code I originally had, and doesn't seem to work any differently – OJW Feb 28 '11 at 23:12
  • @OJW: are you 100% sure that you don't import urllib2 somewhere else before that? The above code works perfectly for me if I simply paste it in some file. – Wolph Feb 28 '11 at 23:38
  • By "it works", do you mean that it downloads the page via the proxy (that bit works for me too), or are you also watching the TOR STDOUT to confirm that DNS requests are going via TOR and not direct? – OJW Mar 01 '11 at 00:34
  • @OJW: I didn't use TOR for my test but simply an ssh connection while monitoring the connection for DNS requests with Wireshark. Although there is a chance that I didn't see the DNS request because of the caching DNS server that I run locally. – Wolph Mar 01 '11 at 03:09
  • @OJW: here's a recipe for a tor dns proxy, just in case you can't get it working: http://www.koders.com/python/fid6ADC126A3BAFB160A793FD3FB610CF0B06E531D7.aspx?s=socket#L13 – Wolph Mar 01 '11 at 03:10
  • Just tried it with Wireshark here too, and I can see the DNS request going out in cleartext to default gateway, before a load of SSL communications between TOR and some random host. – OJW Mar 01 '11 at 21:03
  • @OJW: indeed... after clearing my dns cache I saw the DNS request too :( The socksipy docs seem to be faulty. The problem seems to be in urllib2, I don't get any requests when manually passing the http headers to the socket. – Wolph Mar 01 '11 at 23:40
  • Raised as http://bugs.python.org/issue11375 – OJW Mar 04 '11 at 21:19
  • Python bug 11375 was closed as "invalid", so looks like this behaviour will never be fixed – OJW Jul 01 '11 at 18:54
  • 3
    NOTE: The new SOCKS port seems to be 9150. I literally spent an hour, disabling the firewall etc. trying to figure out why it didn't connect... – Mastergalen Nov 03 '13 at 14:29
3

I've published an article with complete source code showing how to use urllib2 + SOCKS + Tor on http://blog.databigbang.com/distributed-scraping-with-multiple-tor-circuits/

Hope it solves your issues.

sw.
  • 3,240
  • 2
  • 33
  • 43