Tor for React Native
``shell script`
npm i react-native-tor
// for ios builds
npx pod-install
then
`js
import Tor from "react-native-tor";
// Initialize the module
const tor = Tor();
//...
const makeTorRequest = async()=>{
// Start the daemon and socks proxy (no need for Orbot and yes iOS supported!)
await tor.startIfNotStarted();
try{
// Use built in client to make REST calls to .onion urls routed through the Sock5 proxy !
const resp = await tor.get('http://some.onion',{'Authorization': 'sometoken'});
// Note: self signed https endpoints, custom headers and multiform/json payloads supported!
await tor.post('https://someother.onion',JSON.stringify(resp.json),{'Authorization': 'sometoken'},true);
} catch(error){
// Catch a network or server error like you normally with any other fetch library
}
}
makeTorRequest();
`
:boom:
If you think this is awesome, please consider contributing to Privacy and Opensource !
---
This module uses multiple layers:
1. Pre-compiled rust sdk (sifir-rs-sdk) for the Tor daemon and Control code (Check CODE section for details)
2. Swift/Kotlin wrappers to access and control the daemon and provide native proxified clients.
3. TS/JS Native bridges to allow access from your components.
1. Install package
`sh`
npm install react-native-tor`
orshell script`
yarn add react-native-torandroid/app/build.gradle
2. Link libs
- Android: open and add`
`
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
`
- iOS:
`
cd ios/
pod install
iOS Projects note:
- iOS 11.1 > only: Support iOS Version is 11.1 and higher (#6)
- Bitcode not supported: Set Build Settings > Build Options > Enable Bitcode to No for both Debug and Release.
`js
import Tor from "react-native-tor";
// Intialize the module with some settings
const tor = Tor({
stopDaemonOnBackground :true, // Auto shut down daemon when app in background
});
// Start and boostrap the Tor daemon returning the socksProxy port that can be
// used to direct requests to.
const socksProxy = await tor.startIfNotStarted();
try{
// Use module built in proxied client to make a get request
const resp = tor.get('');
} catch(error){
// Client throws on Network errors and Response codes > 299
}
`$3
`js
import Tor from 'react-native-tor';
const tor = Tor();
await tor.startIfNotStarted()
const target = 'kciybn4d4vuqvobdl2kdp3r2rudqbqvsymqwg4jomzft6m6gaibaf6yd.onion:50001';
const conn = await tor.createTcpConnection({ target }, (data, err) => {
if(err){
console.error('error sending msg',err);
return
}
console.log('recieved tcp msg', data);
} );
try {
await conn.write({ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n);{ "id": 2, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n
await conn.write();
// ... moar writes
} catch (err) {
console.error('Error SendingTcpMSg', err);
}
// Eventually when you're done and after recieving your data close the connection
await conn.close();
}
`onData(data,null)
Note:
- The current TcpStream implementation emits per line of data received. That is it reads data from the socket until a new line is reached, at which time it will emit the string line received (by calling onData
- Ergo sum the current implementation is only suited for text data that is line delineated (Electrum server, etc..)
- Future implementations will support constant streams of buffered base64 data.
- If an error is received, or the connection is dropped will be called with the second parameter containing the error string (ie onData(null,'some error');
- Receiving an 'EOF' error from the target we're connected to signifies the end of a stream or the target dropped the connection.
- This will cause the module to drop the TcpConnection and remove all data event listeners.
- Should you wish to reconnect to the target you must initiate a new connection by calling createTcpConnection again.
Documentation is a WIP, Contribute!
`ts`
const tor = Tor({ ...params });`
defined as :ts`
/**
* Tor module factory function
* @param stopDaemonOnBackground
* @default true
* When set to true will shutdown the Tor daemon when the application is backgrounded preventing pre-emitive shutdowns by the OS
* @param startDaemonOnActive
* @default false
* When set to true will automatically start/restart the Tor daemon when the application is bought back to the foreground (from the background)
* @param os The OS the module is running on (Set automatically and is provided as an injectable for testing purposes)
* @default The os the module is running on.
*/
declare const _default: ({ stopDaemonOnBackground, startDaemonOnActive, os, }?: {
stopDaemonOnBackground?: boolean | undefined;
startDaemonOnActive?: boolean | undefined;
os?: "ios" | "android" | "windows" | "macos" | "web" | undefined;
}) => TorType;`
This returns an instance of TorType module which gives control over the Tor daemon and access to the Socks5 enabled client:tsapplication/x-www-form-urlencoded
declare type TorType = {
/**
* Send a GET request routed through the SOCKS proxy on the native side
* Starts the Tor Daemon automatically if not already started
* @param url
* @param headers
@param trustSSL @default true When true accepts any* SSL cert for the onion URL
*/
get(url: string, headers?: RequestHeaders, trustSSL?: boolean): Promise
/**
* Send a POST request routed through the SOCKS proxy on the native side
* Starts the Tor Daemon automatically if not already started
* @param url
@param body JSON encoded (JSON.stringify) string of the payload to send Note: Client will treat the body as a JSON string and set request header accordingly
* The client also supports sending the payload as by setting the Content-Type header to application/x-www-form-urlencoded in the header param`
* @param headers
@param trustSSL @default true When true accepts any* SSL cert for the onion URL
*/
post(url: string, body: RequestBody[RequestMethod.POST], headers?: RequestHeaders, trustSSL?: boolean): Promise
/**
* Send a DELETE request routed through the SOCKS proxy on the native side
* Starts the Tor Daemon automatically if not already started
* @param url
* @param headers
@param trustSSL @default true When true accepts any* SSL cert for the onion URL
*/
delete(url: string, body?: RequestBody[RequestMethod.DELETE], headers?: RequestHeaders, trustSSL?: boolean): Promise
/** Starts the Tor Daemon if not started and returns a promise that fullfills with the socks port number when boostraping is complete.
* If the function was previously called it will return the promise without attempting to start the daemon again.
* Useful when used as a guard in your transport or action layer
*/
startIfNotStarted(): Promise
/**
* Stops a running Tor Daemon
*/
stopIfRunning(): Promise
/**
* Returns the current status of the Daemon
* Some :
* NOTINIT - Not initialized or run (call startIfNotStarted to the startDaemon)
* STARTING - Daemon is starting and bootsraping
* DONE - Daemon has completed boostraing and socks proxy is ready to be used to route traffic.
*
*/
getDaemonStatus(): Promise
/**
* Accessor the Native request function
* Should not be used unless you know what you are doing.
*/
request: NativeTor['request'];
/**
* Factory function for creating a peristant Tcp connection to a target
* See createTcpConnectio;
*/
createTcpConnection: typeof createTcpConnection;
};`
Tcp Stream API:ts
interface TcpStream {
/**
* Called to close and end the Tcp connection
*/
close(): Promise
/**
* Send a message (write on socket)
* @param msg
*/
write(msg: string): Promise
}
/**
/*
* Factory function to create a persistent TcpStream connection to a target
* Wraps the native side emitter and subscribes to the targets data messages (string).
* The TcpStream currently emits per line of data received . That is it reads data from the socket until a new line is reached, at which time
* it will emit the data read (by calling onData(data,null). If an error is received or the connection is dropped it onData will be called
* with the second parameter containing the error string (ie onData(null,'some error');
* Note: Receiving an 'EOF' error from the target we're connected to signifies the end of a stream or the target dropped the connection.
* This will cause the module to drop the TcpConnection and remove all data event listeners.
* Should you wish to reconnect to the target you must initiate a new connection by calling createTcpConnection again.
* @param param {target: String, writeTimeout: Number} :
* target onion to connect to (ex: kciybn4d4vuqvobdl2kdp3r2rudqbqvsymqwg4jomzft6m6gaibaf6yd.onion:50001)`
* 'writeTimeout' in seconds to wait before timing out on writing to the socket (Defaults to 7)
* @param onData TcpConnDatahandler node style callback called when data or an error is received for this connection
* @returns TcpStream
*/
const createTcpConnection = async (
param: { target: string; writeTimeout?: number },
onData: TcpConnDatahandler
): Promise
You can also check the provided Example Application for a quick reference.
---
- Torproject for everything they do.
- @afilini for his amazing work on libtor and constant support!
- @GodSaysHodl for his generosity and backing of FOSS Bitcoin projects with financial bounties and encouragement.
This module is the product of love and dedication for principles I believe in.
If you find this module helpful or useful please do consider support it by contributing in the best way that suits you, here are some ideas:
Funding = More time to focus on what matters.
If you found this useful please help fund development for this and other privacy focused projects,
- Send some Bitcoin/SATS:
bc1q8442hgk32h2x5undjlnu56q2ewnmnpys30zstj
- Fund Tor projects and exit nodes
https://blog.torproject.org/support-tor-network-donate-exit-node-providers
- Librepay:
https://liberapay.com/gabidi/
See the contributing guide to learn how to contribute to the repository and the development workflow.
There are 3 layers of libraries this project relies on:
1. The Sifir Rust SDK
providing lower level control of the Tor daemon and where building the iOS and Android libraries is done.
2. The iOS and Android wrapper Code to bridge and provide a socks client.
This is where the mobile native code interacts with the prebuilt Rust libraries with step1 via FFI and exposes that functionality
to React. It is also where the proxied client forwards requests recieved from React.
3. The Typescript module. This is the module React interacts with.
Depending on the level of modifications needed you will need to work with 1 or all 3 layers.
https://github.com/Sifir-io/sifir-rs-sdk/
Use the helper scripts/ for both the Android Tor and iOS Tor modules to compile them.
2. Copy the compiled Universal iOS dylib to the folder ./ios/library/universal/
a. Modify Pod spec file if needed
3. Bootstrap and run the example app to verify all is good
1. yarn bootstrap
2. cd example
3. in one terminal yarn start
4. in another terminal yarn ios or yarn android`- Better API Docs
- Event emitter from Rust to Native on Boostrap status
- Enable secret service API
- Start new hidden service on phone.
- Restore hidden service from key.
- Capture daemon logs into files.
- Search for available ports for socks proxy for iOS
- Return a Context API (status, etc..) as part of the package to make it easier for developers to build reactive components on topof.
- Build on Request capability
- PUT calls
- DELETE
- Add body support
- ~Sockets~
- Streaming ?
- ~Investigate stability builds on older mobile API's (Currently minSdk is Android 26 and iOS 10)~
- Investigate the possibility of creating a NetworkExtension on iOS which act as a VPN for the app which regular REST libaries can be used on.
MIT