socks_socket 2.1.0
socks_socket: ^2.1.0 copied to clipboard
SOCKS version 5 sockets for Dart and Flutter, *eg.* ElectrumX and/or Fulcrum over Tor via socket(s).
SOCKS sockets #
SOCKS version 5 sockets for Dart and Flutter, eg. ElectrumX and/or Fulcrum over Tor via socket(s).
Features #
- Dart & Flutter 3.
- Support for SOCKS version 5 protocol.
- Supports ElectrumX and Fulcrum servers via socket(s).
- Async support for non-blocking network communication.
- Lightweight and minimal dependencies.
- Configurable connection timeouts (handshake and operation).
- Reconnection support without creating a new instance.
- Newline option for line-delimited protocols (ElectrumX).
- Connection state tracking (disconnected, connecting, connected, error).
- Typed exception hierarchy with SOCKS5 reply codes.
- Tor circuit isolation via
isolationToken. - Cancellation of in-flight connections via
cancel().
Getting Started #
See socks_socket.dart itself for properties and methods and the example for reference.
import 'dart:io';
import 'package:socks_socket/socks.dart';
// The SOCKS5 port of your Tor instance, for example Tor.instance.port when
// Tor runs in-app through package:tor_ffi_plugin.
const proxyPort = 9050;
// Instantiate a socks socket at localhost and on the port selected by the tor service.
var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: proxyPort,
sslEnabled: true, // For SSL connections.
);
// Connect to the socks instantiated above.
await socksSocket.connect();
// Connect to bitcoin.stackwallet.com on port 50002 via socks socket.
//
// Note that this is an SSL example.
await socksSocket.connectTo('bitcoin.stackwallet.com', 50002);
// Send a server features command to the socket, see method for more specific usage example.
await socksSocket.sendServerFeaturesCommand();
socks.dart omits the ConnectionState alias for use alongside Flutter.
Timeout Configuration #
// Configure custom timeouts for slow networks like Tor.
var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: proxyPort,
sslEnabled: true,
handshakeTimeout: Duration(seconds: 60),
operationTimeout: Duration(seconds: 45),
);
Circuit Isolation #
// Use isolationToken to request a separate Tor circuit.
var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: proxyPort,
sslEnabled: true,
isolationToken: 'wallet-btc-001',
);
await socksSocket.connect();
await socksSocket.connectTo('bitcoin.stackwallet.com', 50002);
// Reconnect with a different token; requires an earlier connectTo().
await socksSocket.reconnect(isolationToken: 'wallet-btc-002');
Tokens are sent as SOCKS5 credentials. A proxy selecting no-auth is rejected unless requireIsolation: false.
Reconnection #
// After a connection drops, reconnect to the same target.
await socksSocket.reconnect();
// Continue sending data on the restored connection.
await socksSocket.write('{"jsonrpc":"2.0","method":"server.ping","id":1}',
newline: true);
If connect() fails, the instance is spent: state is error, another connect() throws, and reconnect() has no target. Create a new instance. After a failed connectTo(), reconnect() retries that target. After cancel() or close() during connect, reconnect() opens a new connection once a target is known. reconnect() closes the current connection first, which ends inputStream; a close() or destroy() while it is in progress cancels it, including one made from that stream's onDone. Peer close is noticed, and state updated, only while inputStream has a listener.
By default the connection closes once the peer closes its side. Pass closeOnPeerEof: false to create() to keep writing after a peer half-close until you call close().
destroy() aborts a connection without draining output; pending writes fail, and a connect, TLS handshake or reconnect() in flight ends with SocksCancelledException. Use close() to drain accepted output before closing; it ends a connect or reconnect() in flight the same way. When a protocol requires request EOF before the response, use closeOutput(), consume the response from inputStream, then call close().
Migrating to 2.0.0 #
SOCKSSocket.socket, responseController and subscription have been removed. All transport operations now go through the wrapper so writes and teardown share the same lifecycle tracking.
| Previous operation | Replacement |
|---|---|
socks.socket.destroy() |
socks.destroy() |
socks.socket.close() |
await socks.closeOutput() to preserve input (the native half-close behavior), or await socks.close() to close both directions |
socks.socket.add(bytes) |
socks.outputStream.add(bytes) |
socks.socket.addStream(source) |
await socks.outputStream.addStream(source) |
| Reading the underlying socket | socks.inputStream or socks.listen(...) |
socks.responseController, socks.subscription |
socks.inputStream; pause or cancel your own subscription to it |
SecureSocket.secure(socks.socket, ...) |
Set sslEnabled: true and, if needed, securityContext on SOCKSSocket.create(...) |
For an awaited binary write, use await socks.outputStream.addStream(Stream.value(bytes)). The output sink accepts one stream at a time. For text, use await socks.write(text). To signal request EOF while continuing to receive, use await socks.closeOutput(). It immediately rejects new writes while draining accepted output; a drain timeout or transport failure aborts the connection and fails pending output. To drain and finish the entire connection, use await socks.close().
Built-in TLS starts during connectTo(), after the SOCKS handshake. Upgrading an established plaintext application session to TLS is not exposed by SOCKSSocket.
SocksConnection.start still returns a ConnectionTask<Socket> for HttpClient.connectionFactory; that separate API is unchanged.
HttpClient Connections #
SocksConnection.start returns a cancellable ConnectionTask<Socket> for HttpClient.connectionFactory; see example/http/http_connection.dart. Pass tlsHost for HTTPS. Requires Dart 3.5.