Foundation-Devices/tor

SOCKS5 Flutter

xAffan opened this issue ยท 21 comments

How do you actually use the socks5 proxy in flutter? It seems possible but the DNS lookup is still performed locally, causing onion lookup to fail!
We have to use socks5h/socks4a not socks5/socks4.

Hi @xAffan. We use this plugin in https://github.com/Foundation-Devices/envoy and a Rust dependency of ours (https://github.com/bitcoindevkit) looks up onion addresses without fail.

We have never actually tried accessing .onions from Dart code. Maybe @sneurlax did?

I haven't found a way to lookup onion addresses from dart. Both of you seem much more experienced than me, therefore, I would be very happy if you guided me how to lookup onion addresses preferably from dart, if not, then from bitcoindevkit dependency(?)

More specifically, I need a way to use tor proxy with onion lookups on normal http libraries in dart.
I found this https://github.com/utopicnarwhal/flibusta-mobile/blob/master/lib/services/http_client/dio_http_client_adapters/socks_http_client_adapter.dart
However, it is not adapted for dart 3. As a newbie, I have no idea how to do so.

I do get onion responses when running arti locally on desktop, but haven't thoroughly tested it when arti's running as bundled in this package. We are still waiting on arti to release full, secure/private onion addresses and will probably finish integration after that. As of 1.1.11 I thought arti had experimental .onion address support.

@xAffan I had to write a custom SOCKS5 package for communication with electrumx because nothing else I found worked/supported sockets properly. You might have success finding something else that works, but this may work or we may just have to patch it for SOCKS5H: https://github.com/cypherstack/socks_socket

It does actually support .onion. However, I am unable to connect to it properly using flutter, I just need to be able to use socks5h with some http client. I succeeded with using socks5 because someone had made a package implementating socks5 but I suspect it is not socks5h and does DNS locally.

May I ask what you're trying to connect to? And/or what SOCKS5 package you're using?

I've tried these SOCKS5 dart packages which work for basic functionality but didn't totally work for what we needed (with ElectrumX use with sockets)--unrelated to your .onion address issues, so I'd check them out in case they might work:

This is what I wrote when none of the above worked perfectly for what we needed, so maybe this will help?

If none of the above work, if you can provide a working commandline example of a SOCKS5H command/response or an example of a SOCKS5 call that works and a SOCKS5H one that doesn't, that would help me get started on extending that socks_socket package to support SOCKS5H.

PS if none of the upper packages work, the links from here might help with using socks_socket. I need to finish it, add an example, and document it a bit better :D but hopefully one of the 3 other packages above work for your use case.

I was using https://github.com/LacticWhale/socks_dart
It worked but couldn't connect to onion sites due to DNS lookup failure I believe.
I will now test with your package to see if it works and the other two packages as well.

Tor Project: FAQ basically says we should use SOCKS 4a. If we use SOCKS 5, it won't work. It is apparently on purpose to avoid DNS lookups.

I tried it a few years ago in C code, doing SOCKS 4a handshake myself. It works. I also use Boost.Asio and libcurl respectively in my C++ codes. They all work as long as we stick to SOCKS 4a.

I'm new to Flutter, and haven't tried using SOCKS 4a in Dart code to connect onion service yet. If using SOCKS 4a works in Dart/Flutter code, please let us know. Thanks.

Tor Project: FAQ basically says we should use SOCKS 4a. If we use SOCKS 5, it won't work. It is apparently on purpose to avoid DNS lookups.

I tried it a few years ago in C code, doing SOCKS 4a handshake myself. It works. I also use Boost.Asio and libcurl respectively in my C++ codes. They all work as long as we stick to SOCKS 4a.

I'm new to Flutter, and haven't tried using SOCKS 4a in Dart code to connect onion service yet. If using SOCKS 4a works in Dart/Flutter code, please let us know. Thanks.

It is possible to use SOCKS 4a.
https://github.com/utopicnarwhal/flibusta-mobile/blob/master/lib/services/http_client/dio_http_client_adapters/socks_http_client_adapter.dart
I would have gone ahead and used it but it is dart 2.x code. Anyways, this project did use SOCKS 4a.

Hey @xAffan, did you ever try socks_socket.dart?

I had to use it again today for connecting electrum_adapter via Tor. If you can report some issues/errors, I'll try and fix them. Or if you provide some minimal reproducible code showing what you want to succeed that doesn't, I'll try and extend socks_socket to accomplish that.

You can use it like

import 'package:tor/socks_socket.dart';

<some async func>:
await Tor.init();
await Tor.instance.start();

SOCKSSocket socksSocket = await SOCKSSocket.create(
  proxyHost: InternetAddress.loopbackIPv4,
  proxyPort: Tor.instance.port,
  sslEnabled: true,
);
await socksSocket?.connect();
await _socksSocket?.connectTo(<destHost>, <destPort>);

Hey @xAffan, did you ever try socks_socket.dart?

I had to use it again today for connecting electrum_adapter via Tor. If you can report some issues/errors, I'll try and fix them. Or if you provide some minimal reproducible code showing what you want to succeed that doesn't, I'll try and extend socks_socket to accomplish that.

You can use it like

import 'package:tor/socks_socket.dart';

<some async func>:
await Tor.init();
await Tor.instance.start();

SOCKSSocket socksSocket = await SOCKSSocket.create(
  proxyHost: InternetAddress.loopbackIPv4,
  proxyPort: Tor.instance.port,
  sslEnabled: true,
);
await socksSocket?.connect();
await _socksSocket?.connectTo(<destHost>, <destPort>);

I would use this but I am not quite sure how to use HTTP protocol with this? I need to make some GET and POST requests, and I am not exactly sure how to.

@xAffan if you don't need it for electrum servers, maybe my socks_socket is not for you. But see https://github.com/Foundation-Devices/tor/blob/main/lib/socks_socket.dart#L321 (the sendServerFeaturesCommand method) for a close example: instead of sending a JSON command, you'll send a raw GET or POST request. For example:

Future<void> makeGetRequest(SOCKSSocket socksSocket) async {
  // Domain to make a GET request to
  var domain = 'example.com';

  // GET request path
  var path = '/api/data';

  // Prepare the GET request
  var getRequest = 'GET $path HTTP/1.1\r\n'
      'Host: $domain\r\n'
      'Connection: close\r\n\r\n';

  // Send the GET request
  socksSocket.write(getRequest);

  // Read and print the response
  await socksSocket.inputStream
      .transform(utf8.decoder)
      .transform(LineSplitter())
      .forEach(print);
}

Future<void> makePostRequest(SOCKSSocket socksSocket) async {
  // Domain to make a POST request to
  var domain = 'example.com';

  // POST request path
  var path = '/api/data';

  // POST request body
  var requestBody = jsonEncode({
    'key': 'value',
  });

  // Prepare the POST request
  var postRequest = 'POST $path HTTP/1.1\r\n'
      'Host: $domain\r\n'
      'Content-Type: application/json\r\n'
      'Content-Length: ${requestBody.length}\r\n'
      'Connection: close\r\n\r\n'
      '$requestBody';

  // Send the POST request
  socksSocket.write(postRequest);

  // Read and print the response
  await socksSocket.inputStream
      .transform(utf8.decoder)
      .transform(LineSplitter())
      .forEach(print);
}

These may not work if you copy and paste them but show the general approach you might take towards making GET and POST requests via socks_socket.

It may help if you can post a small example of exactly what you're trying to do (or something similar enough)

Unfortunately, raw requests won't do - they will take too much time and I regularly need to interact with a site.
here's me using ur sample.

Launching lib\main.dart on Windows in debug mode...
โˆš Built build\windows\x64\runner\Debug\main.exe.
Connecting to VM Service at ws://127.0.0.1:50397/ZLWv9PAmtXU=/ws
flutter: NOW: 2024-04-24 04:15:19.796316
flutter: Instance of Tor created!
Starting proxy!
flutter: Done awaiting; tor should be running
flutter: Starting tor took 261 seconds. Proxy running on port 43787
flutter: 192.42.116.199

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: SocketException: Failed host lookup: 'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion' (OS Error: No such host is known.

, errno = 11001)
#0 _NativeSocket.lookup. (dart:io-patch/socket_patch.dart:520:9)

#1 SocksTCPClient.connect (package:socks5_proxy/src/client/socks_tcp_client.dart:66:18)

#2 SocksTCPClient.assignToHttpClientWithSecureOptions. (package:socks5_proxy/src/client/socks_tcp_client.dart:46:40)

#3 _ConnectionTarget.connect. (dart:_http/http_impl.dart:2490:32)

#4 _HttpClient._openUrl. (dart:_http/http_impl.dart:2787:15)

#5 _MyAppState.build. (package:main/main.dart:158:45)

@sneurlax It'd be great if we can connect with onion servers, e.g. DuckDuckGo https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion.

For now, we can only connect to ordinary websites. If we try to connect to an onion server, it will fail. In my case of using LacticWhale/socks_dart to connect an onion router, it throws:

SocketException: Failed host lookup: 'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion' (OS Error: No address associated with hostname, errno = 7)

By the way, does your sneurlax/socks_socket also provide a wayt to create or adapt a HttpClient or Client? Classes HttpClient and Client are from packages dart:io and http respectively. It'd be more user-friendly if it does. Some Flutter packages, e.g. Supabase, use Client.

@sneurlax It'd be great if we can connect with onion servers, e.g. DuckDuckGo https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion.

For now, we can only connect to ordinary websites. If we try to connect to an onion server, it will fail. In my case of using LacticWhale/socks_dart to connect an onion router, it throws:

SocketException: Failed host lookup: 'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion' (OS Error: No address associated with hostname, errno = 7)

By the way, does your sneurlax/socks_socket also provide a wayt to create or adapt a HttpClient or Client? Classes HttpClient and Client are from packages dart:io and http respectively. It'd be more user-friendly if it does. Some Flutter packages, e.g. Supabase, use Client.

I suspect, it's not using Tor Proxy for DNS resolution, which is why host lookup fails. It seems as though there is no solution that I am aware of

@xAffan

I suspect, it's not using Tor Proxy for DNS resolution, which is why host lookup fails. It seems as though there is no solution that I am aware of

In my case, it's LacticWhale/socks_dart that tries to do DNS lookup and fails, which behavior is well-described in Tor Project: FAQ

Even though SOCKS 5 can accept either an IP or a hostname, most applications supporting SOCKS 5 try to resolve the name before passing it to the SOCKS proxy.

Re-edit: Remove misleading information.

@xAffan

I suspect, it's not using Tor Proxy for DNS resolution, which is why host lookup fails. It seems as though there is no solution that I am aware of

In my case, it's LacticWhale/socks_dart that tries to do DNS lookup and fails, which behavior is well-described in Tor Project: FAQ
Even though SOCKS 5 can accept either an IP or a hostname, most applications supporting SOCKS 5 try to resolve the name before passing it to the SOCKS proxy.

I've been looking for a Dart/Flutter package to support SOCKS 4a but it seems that there is none. SOCKS 4a only accepts hostname, so the package that support SOCKS 4a isn't likely to send DNS queries to Tor Proxy, because, again, SOCKS 4a only accept hostname.

That is certainly some good insight. I saw a package claiming to support socks4/5. Although, when I tried it, it simply did not work. Worth a try? https://github.com/tayoji-io/socks_proxy

@xAffan

That is certainly some good insight. I saw a package claiming to support socks4/5. Although, when I tried it, it simply did not work. Worth a try? https://github.com/tayoji-io/socks_proxy

I've tried tayoji-io/socks_proxy: dart package,http/socks4/socks5 proxy too. Same. It doesn't work. My best guess is that both tayoji-io/socks_proxy and LacticWhale/socks_dart themselves tried to do DNS lookup and failed, before they had a chance to send the .onion domain name to the Tor proxy.

After a little research and chatting with Copilot, I think I was wrong about SOCKS 4a. SOCKS 4a accepts both IPv4 address and domain name, just like SOCKS 5 does. The reason why we cannot access onion servers through Tor proxy has nothing to do with using SOCKS 5 or SOCKS 4a, but rather the SOCKS client packages we used all try to DNS lookup the .onion domain name, which of cause will fail because no DNS servers know anything about the top-level domain .onion.