📜 ⬆️ ⬇️

Easy tor integration into the android application using the example of a rutracker client

I wondered for a long time whether it is easy to add proxying via tor in Android application. It seems to be a fairly obvious task, plus the torus browsers have already been under this platform for a long time ... But there are many tasks that are more difficult than they seem. For those who are impatient, I will say straight away - yes, it is possible, and it turns out quite easily, quickly and coolly. In particular, if you do not dig from scratch, but take advantage of my work.

For example, I will use the application to work with a rutracker - no one likes code that works with a spherical horse in a vacuum. Previously, this application bypassed the blocking with the help of Google Compression Proxy - but alas - either the root tracker, or Google drank the possibility of authorization with this proxy. I must say at once that, of course, there are all kinds of vpna and stuff that you use to easily bypass the lock and view the serials. But this is not about this. As you understand, a torus can be used in a mobile application for a huge amount of things - for example, to access web sites in .onion or to implement a highly secure messenger.

How to connect the library to work with the Thor


How to build from scratch


If you are not interested in building from scratch, then immediately go to the next heading.

So, what we have on this topic from the finished toolkit. There is a special repository from some guys under the leadership of Microsoft (link in the basement). It seems that everything worked for them - but the quality and assembly mechanism are simply terrifying. And the repository is outdated for two years. And the compiled version of the library is not there, there are only pretty dumb instructions on how to build it yourself (in the style - “I did it, I don’t know why, but without this nothing worked”). However, the available instructions are enough to update the code to the current state and fix all the strange jambs.

  1. We clone this repository.
    ')
  2. We update there the component that is responsible for managing the torus - jtorctl. They used fork of the main repository with briar edits, but these edits are already included in the main repository, so it is better to take them from the main one. You can connect from the maven repository, but I usually pick up such things with the source code - you can see right away, get rid of the analysis and edit the bugs on the fly - the project is rather raw, regardless of age.

  3. We update geoip and geoip6 — databases of blocks of IP addresses with reference to the geographic location of each block for IPv4 and IPv6 versions, respectively. To do this, download the windows expert bundle on the website of the torus.

  4. We update the torus itself (that is, the native library). There is no standard public access (well, or I was looking badly) - so we go to the guys who develop the torus and the torus browser for android (Orbot and Orfox), take the latest release of their Orbot and remove the library from there. Thor is pretty fresh there, which is nice.

  5. Hand over everything that stopped compiling in our project. Several functions in dependent libraries have changed, but in general, everything is intuitive and fixable in 5 minutes.

  6. Following the recommendations of our project readme, we create local maven repositories and build our project from a heap of pieces. By the way, please note that the build script is so crooked that in one place includes the previous release itself. Horror. So I recommend to rewrite it anew, in simple and understandable language, in order to get an ordinary aar library at the output.

How to collect from my work


I have already done points 1-6 for you, so just compile the library from my repository, or download it in the release section. The link will be in the “basement” of the post. However, I draw your attention to the fact that it will be correct to check the code and libraries for compliance with the original ones and the absence of bookmarks. You should not add such things blindly to your applications.

How to stop worrying and start proxying through the torus


First you need to turn on the torus:

int totalSecondsPerTorStartup = 4 * 60; int totalTriesPerTorStartup = 5; try { boolean ok = onionProxyManager.startWithRepeat(totalSecondsPerTorStartup, totalTriesPerTorStartup); if (!ok) Log.e("TorTest", "Couldn't start Tor!"); } catch (InterruptedException | IOException e) { e.printStackTrace(); } 

Then wait for it to pick up:

 while (!onionProxyManager.isRunning()) Thread.sleep(90); 

If everything went well - hurray, he listens to our localhost on some random port:

 Log.v("My App", "Tor initialized on port " + onionProxyManager.getIPv4LocalHostSocksPort()); 

But this is not all. We now have a torus that listens to the port as a Socks4a proxy. However, not all standard libraries can work with Socks4a. There, for reasons of anonymity, it is required that the host resolving occurs on a proxy, and not earlier. I do not know which of the standard libraries can do this, and I had code written with Apache HttpComponents. I already wrote earlier why they can be used, and this post is not about that. If you want, you can implement the same on any other library.

So, to use httpComponents, we need to rewrite ConnectionSocketFactory and SSLConnectionSocketFactory.

SSLConnectionSocketFactory
 public class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory { public MySSLConnectionSocketFactory(final SSLContext sslContext) { super(sslContext); } @Override public Socket createSocket(final HttpContext context) throws IOException { return new Socket(); } @Override public Socket connectSocket( int connectTimeout, Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { Args.notNull(host, "HTTP host"); Args.notNull(remoteAddress, "Remote address"); InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); socket = new Socket(); connectTimeout = 100000; socket.setSoTimeout(connectTimeout); socket.connect(new InetSocketAddress(socksaddr.getHostName(), socksaddr.getPort()), connectTimeout); DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); outputStream.write((byte) 0x04); outputStream.write((byte) 0x01); outputStream.writeShort((short) host.getPort()); outputStream.writeInt(0x01); outputStream.write((byte) 0x00); outputStream.write(host.getHostName().getBytes()); outputStream.write((byte) 0x00); DataInputStream inputStream = new DataInputStream(socket.getInputStream()); if (inputStream.readByte() != (byte) 0x00 || inputStream.readByte() != (byte) 0x5a) { throw new IOException("SOCKS4a connect failed"); } else Log.v("SSLConnectionSF", "SOCKS4a connect ok!"); inputStream.readShort(); inputStream.readInt(); SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createLayeredSocket(socket, host.getHostName(), host.getPort(), context); prepareSocket(sslSocket); return sslSocket; } } 


ConnectionSocketFactory
 public class MyConnectionSocketFactory implements ConnectionSocketFactory { @Override public Socket createSocket(final HttpContext context) throws IOException { return new Socket(); } @Override public Socket connectSocket( int connectTimeout, Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException, ConnectTimeoutException { InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); socket = new Socket(); connectTimeout = 100000; socket.setSoTimeout(connectTimeout); socket.connect(new InetSocketAddress(socksaddr.getHostName(), socksaddr.getPort()), connectTimeout); DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); outputStream.write((byte) 0x04); outputStream.write((byte) 0x01); outputStream.writeShort((short) host.getPort()); outputStream.writeInt(0x01); outputStream.write((byte) 0x00); outputStream.write(host.getHostName().getBytes()); outputStream.write((byte) 0x00); DataInputStream inputStream = new DataInputStream(socket.getInputStream()); if (inputStream.readByte() != (byte) 0x00 || inputStream.readByte() != (byte) 0x5a) { throw new IOException("SOCKS4a connect failed"); } else Log.v("SSLConnectionSF", "SOCKS4a connect ok!"); inputStream.readShort(); inputStream.readInt(); return socket; } } 


Using these factories is easy and simple. To do this, create an HttpClient that uses these libraries:

  public HttpClient getNewHttpClient() { Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", new MyConnectionSocketFactory()) .register("https", new MySSLConnectionSocketFactory(SSLContexts.createSystemDefault())) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); return HttpClients.custom() .setConnectionManager(cm) .build(); } 

And tell him our proxy server:

  HttpClient cli = getNewHttpClient(); int port = onionProxyManager.getIPv4LocalHostSocksPort(); InetSocketAddress socksaddr = new InetSocketAddress("127.0.0.1", port); HttpClientContext context = HttpClientContext.create(); context.setAttribute("socks.address", socksaddr); 

Everything, now we can use the torus in the same way as if we were making ordinary queries. Moreover, we can also access the .onion web sites.

Result


I used the resulting code in my rutracker application. Yes, the torus initialization takes about 20 seconds, and the pages do not load so quickly - but then we are guaranteed to pass the lock. And all resources that are not blocked are loaded via a normal connection. It would be possible to skip the rest of the resources through the Google Compression Proxy, but many complained that they had blocked this proxy - so I did not do that. Of course, the application could have done a lot more - for example, to cache static on the phone to save traffic and faster work - but this is not so critical, and I wrote the application rather for example.

Conclusion


Thor on the android is a cool and handy thing that works quite well and can really be used in your applications. By the way, yes, there is a much easier way to do this - just require the installation of the Orbot, which itself will raise your torus. But I do not like the dependencies of some applications on others, and the 3 extra megabytes are not so critical in the size of the application. So if someone liked my decision - use, make pull requests, and may there be freedom with you.

References:


  1. Source library ;
  2. My library build ;
  3. Rutracker application ;
  4. Guardian Project - the guys to whom we owe the presence of a native library torus.

Source: https://habr.com/ru/post/313030/


All Articles