用Python连接.onion网络

2 投票
2 回答
4870 浏览
提问于 2025-04-17 16:32

我想让Python通过控制台访问.onion网站,下面的例子可以在Python中使用Tor,但当我尝试连接到.onion网站时,它会出现“名称或服务未知”的错误,我该怎么解决这个问题呢?

示例代码:

import socket
import socks
import httplib

def connectTor():
    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5,"127.0.0.1",9050,True)
    socket.socket = socks.socksocket
    print "Connected to tor"

def newIdentity():
    HOST = '127.0.0.1'
    socks.setdefaultproxy()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST,9051))
    s.send("AUTHENTICATE\r\n")
    response = s.recv(128)
    if response.startswith("250"):
        s.send("SIGNAL NEWNYM\r\n"),
    s.close()
    connectTor()

def readPage(page):
    conn = httplib.HTTPConnection(page)
    conn.request("GET","/")
    response = conn.getresponse()
    print (response.read())

def main():
    connectTor()
    print "Tor Ip Address :"
    readPage("my-ip.heroku.com")
    print "\n\n"
    readPage("od6j46sy5zg7aqze.onion")
    return 0

if __name__ == '__main__':
    main()

2 个回答

0

你可以在洋葱地址后面加上80这个端口号,这样就可以避免进行DNS查找。

比如说,你可以这样写:readPage("od6j46sy5zg7aqze.onion:80")

如果你使用的是urllib2库,还需要指定协议,也就是http。

例如:

import urllib2

print urllib2.urlopen("http://od6j46sy5zg7aqze.onion:80").read()

1

我觉得这可能是你的问题,但我也可能错了。

你依赖于一种叫做“猴子补丁”的技术来修改 socket.socket,目的是让 HTTPConnection 使用你的 SOCKS5 代理与 TOR 通信。但是 HTTPConnection 会调用 socket.create_connection,而这个函数又会调用 socket.getaddrinfo 来解析名字,然后才会调用 socket.socket 来创建连接。而 getaddrinfo 并不使用 socket,所以它没有被修改,因此它并没有通过你的 SOCKS5 代理进行通信,而是使用了你默认的名字解析器。

这样做对于普通的互联网主机代理连接是没问题的,因为 TOR 会返回和你正常名字解析器一样的 DNS 结果,比如“my-ip.heroku.com”。但对于“od6j46sy5zg7aqze.onion”就不行了,因为你的正常名字解析器没有 .onion 这个顶级域名。

如果你感兴趣,可以查看 HTTPConnection.connectsocket.create_connectiongetaddrinfo 的源码(最后一个是用 C 写的,具体内容会根据你的平台分散在模块中)。

那么,如何解决这个问题呢?看一下两个叫 socks 的 SOCKS5 模块,其中一个有一个函数可以直接替代 create_connection(它的接口虽然不完全相同,但对 HTTPConnection 来说足够用了);另一个没有,但你可以很容易地写一个(只需调用 socks.socksocket,然后调用它的 connect 方法)。或者你也可以修改 HTTPConnection,让它创建一个 socket.socket 并调用它的 connect 方法。

最后,你可能会想知道为什么大多数不同的 socks 模块都有一个叫 setdefaultproxy 的函数,里面有一个名为 remote_dns 的参数,声称可以让 DNS 解析在远程进行,但实际上并不奏效。其实,如果你使用 socks.socksocket,它是有效的,但如果你使用 socket.getaddrinfo,就不可能有效。

顺便说一下,如果你还没读过 DnsResolverTorifyHOWTO,在继续之前最好先读一下,因为仅仅试图拼凑出能工作的代码而不理解为什么它能工作,几乎肯定会导致你(或你的用户)在以为自己是匿名的情况下泄露信息。

撰写回答