Skip to content

Latest commit

 

History

History
213 lines (125 loc) · 8.14 KB

README.md

File metadata and controls

213 lines (125 loc) · 8.14 KB

0. 目录

[TOC]

1. 简介

这是一个在android上使用tor进行网络通信的demo。

实现了:

  1. 在app中启动一个进程运行tor-android二进制文件

  2. 通过control port和运行的tor进程进行交互

  3. 启动两个线程分别模拟hidden serviceclient进行通信

2. 使用的预编译文件及第三方库简介

3. 工作流程简介

3.1 下载tor-android,并解压到项目的raw文件夹中

    [build.gradle]
    project.configurations {
        tor
    }
    dependencies {
        // https://bintray.com/briarproject/org.briarproject/tor-android
        tor 'org.briarproject:tor-android:0.3.4.8@zip'
    }
    project.afterEvaluate {
        project.copy {
            from configurations.tor.collect { zipTree(it) }
            into 'src/main/res/raw'
        }
    }

3.2 文件装载

installAssets()中,将tor-androidgeoip,torrc等文件载入到手机中,并赋予tor-android文件执行权限

3.3 启动tor进程

startTorProcess()中,启动一个进程运行tor-android二进制文件,并使用__OwningControllerProcess参数指定tor监控进程号为pid的进程(pid在这里是当前程序的进程号),如果该进程消失了,tor会自动停止。

然后等待生成用来认证的authenticate cookie file

3.4 连接到tor的控制端口(control port)

本demo中将jtorctl第三方库构建成了一个模块添加进来,并新增了addOnionV3接口,用来生成hidden service v3

openControlConnectionAndWaitForBootstrapped()中,新建一个socket,连接到tor提供的控制端口(control port),并使用第三方库jtorctl建立一个controlConnection对象与tor进行交互控制。

交互前需要使用刚生成的authenticate cookie file通过controlConnection向tor进行认证。

需要使用takeOwnership()接口告知tor监控该control port上的连接,当连接关闭后,tor就停止。协议文档中对TAKEOWNERSHIP命令的介绍如下:

This command is intended to be used with the __OwningControllerProcess configuration option. A controller that starts a Tor process which the user cannot easily control or stop should 'own' that Tor process:

  • When starting Tor, the controller should specify its PID in an __OwningControllerProcess on Tor's command line. This will cause Tor to poll for the existence of a process with that PID, and exit if it does not find such a process. (This is not a completely reliable way to detect whether the 'owning controller' is still running, but it should work well enough in most cases.)
  • Once the controller has connected to Tor's control port, it should send the TAKEOWNERSHIP command along its control connection. At this point, both the TAKEOWNERSHIP command and the __OwningControllerProcess option are in effect: Tor will exit when the control connection ends and Tor will exit if it detects that there is no process with the PID specified in the __OwningControllerProcess option.
  • After the controller has sent the TAKEOWNERSHIP command, it should send "RESETCONF __OwningControllerProcess" along its control connection. This will cause Tor to stop polling for the existence of a process with its owning controller's PID; Tor will still exit when the control connection ends.

该方法中还使用了setEvents()接口,请求服务端在设置的事件发生时向客户端发送通知。

并且使用getInfo()接口,从服务端获取status/bootstrap-phase信息,如果返回状态中包含PROGRESS=100,表示tor的circuit已经成功建立,可以进行通信了。

3.5 使用tor的控制端口(control port)使app与tor process进行交互

  • 初始化controlConnection

关于初始化controlConnection的过程在上文的启动tor进程连接到tor的控制端口中已经有详细的介绍了。

  • 使用jtorctl类库中的方法对tor process进行交互

在初始化过程完成之后,就可以使用jtorctl类库中的方法对tor process进行交互。

关于jtorctl类库中相关方法的使用和说明已经封装在ControlPortOperation类中,可以直接调用该类中的静态方法。

比如,在TorPlugin.run()中调用了如下代码:

ControlPortOperation.getConf(controlConnection, "SocksPort");//查询配置参数SocksPort的值
ControlPortOperation.getInfo(controlConnection, "version");//查询tor的版本号信息
ControlPortOperation.setEvents(controlConnection, this, Arrays.asList(EVENTS));//请求服务端在设置的事件发生时向客户端发送通知

3.6 启动两个线程分别模拟hidden service与client,进行通信

新建两个线程模拟hidden serviceclient,进行通信。

该过程在testServerAndClient()中。

3.6.1 服务端

  • 服务端绑定端口

bindToLocalPort()中,服务端新建一个ServerSocket,并绑定到一个端口(该端口为target port)提供服务。

  • 发布hidden service

publishHiddenService()中,服务端通过controlConnection向tor发送ADD_ONION命令,映射本地的target port到虚拟端口virtual port

客户端将通过服务端的hidden service addressvirtual port访问服务端,实际访问的是服务端的target port的服务。

这里设定的virtual port为80。

Tor通过controlConnection向服务端返回应答,应答中包含生成的hidden service addressprivate key等信息。

至此,服务端hidden service已经建立完成。

  • 服务端等待客户端的连接

acceptClientConnect()中,服务端等待客户端连接,并向客户端发送一条信息:"Hello client"

3.6.2 客户端

  • 配置tor proxy代理

在建立客户端socket的时候,需要使用到tor提供的代理端口(socks port),才能访问tor网络。该代理(该代理是socks5代理)端口在torrc文件中会进行配置:

/**
 * file: torrc
 */
SocksPort 59050

并且在代码中使用tor提供的代理:

InetSocketAddress proxy = new InetSocketAddress("127.0.0.1", SOCKS_PORT);
  • 连接到服务端

使用代理连接到服务端,并读取服务端发来的信息。

关于远程解析.onion域名

因为服务端的域名为xxx.onion,不是可以本地解析的地址,需要tor代理进行远程解析。

按理来说,在java中,进行远程解析应该使用InetSocketAddress.createUnresolved构造需要远程解析的域名地址,放入socket.connect方法中:

Socket socket  = new Socket(proxy);
socket.connect(InetSocketAddress.createUnresolved(hiddenServiceAddress, HIDDENSERVICE_VIRTUAL_PORT));

但是该方法在android中会遇到java.net.UnknownHostException: Host is unresolved的异常。

关于该问题的一个参考:android-how-to-let-tor-service-to-perform-the-dns-resolution-using-socket

Android will probably perform DNS resolution via DNS server specified in network configuration and the resolution of onion address will not work.

本代码中使用SocksSocket类重写了Socket类的connect方法,在客户端发送连接请求给服务端时,直接将域名直接封装到socks5数据包中,发送给代理。

并处理好connect方法中的客户端服务端的认证协商过程,关于该过程的详解请见socks5协议:rfc1928

socks5Socket = new SocksSocket(proxy, CONNECT_TO_PROXY_TIMEOUT, EXTRA_SOCKET_TIMEOUT);
socks5Socket.connect(InetSocketAddress.createUnresolved(hiddenServiceAddress, HIDDENSERVICE_VIRTUAL_PORT));