From 2eebfee00708f5a89570b4f4001bcb40add2284f Mon Sep 17 00:00:00 2001 From: Gus Date: Fri, 20 Nov 2020 06:40:59 -0500 Subject: [PATCH] IOS works! --- .gitignore | 1 + example/ios/Podfile | 8 +- .../ios/TorExample.xcodeproj/project.pbxproj | 7 ++ example/ios/TorExample/AppDelegate.m | 36 ++++---- .../AppIcon.appiconset/Contents.json | 45 ++++++---- example/ios/TorExample/Info.plist | 6 ++ example/src/App.tsx | 47 ++++++++-- ios/Tor-Bridging-Header.h | 2 +- ios/Tor.m | 20 +++-- ios/Tor.swift | 88 ++++++++++++++++++- ios/Tor.xcodeproj/project.pbxproj | 49 ++++------- ios/sifir-tor.h | 44 ++++++++++ package.json | 6 +- react-native-tor.podspec | 4 +- src/index.tsx | 5 +- 15 files changed, 275 insertions(+), 93 deletions(-) create mode 100644 ios/sifir-tor.h diff --git a/.gitignore b/.gitignore index 4f1c43c..1deab1a 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,4 @@ android/keystores/debug.keystore # generated by bob lib/ +**/**/*.swp diff --git a/example/ios/Podfile b/example/ios/Podfile index 10d8d37..5eada54 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -61,8 +61,8 @@ target 'TorExample' do # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. - add_flipper_pods! - post_install do |installer| - flipper_post_install(installer) - end + # add_flipper_pods! + # post_install do |installer| + # flipper_post_install(installer) + # end end diff --git a/example/ios/TorExample.xcodeproj/project.pbxproj b/example/ios/TorExample.xcodeproj/project.pbxproj index 9d08c19..d211b4d 100644 --- a/example/ios/TorExample.xcodeproj/project.pbxproj +++ b/example/ios/TorExample.xcodeproj/project.pbxproj @@ -142,6 +142,7 @@ ORGANIZATIONNAME = Facebook; TargetAttributes = { 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = D6MP3CUG29; LastSwiftMigration = 1110; }; }; @@ -269,6 +270,9 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; + DEVELOPMENT_TEAM = D6MP3CUG29; + ENABLE_BITCODE = NO; + GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = TorExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_CFLAGS = ( @@ -296,6 +300,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = D6MP3CUG29; + ENABLE_BITCODE = NO; + GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = TorExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_CFLAGS = ( diff --git a/example/ios/TorExample/AppDelegate.m b/example/ios/TorExample/AppDelegate.m index 7faedb5..b554a56 100644 --- a/example/ios/TorExample/AppDelegate.m +++ b/example/ios/TorExample/AppDelegate.m @@ -12,30 +12,30 @@ #import #if DEBUG -#import -#import -#import -#import -#import -#import -static void InitializeFlipper(UIApplication *application) { - FlipperClient *client = [FlipperClient sharedClient]; - SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; - [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; - [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; - [client addPlugin:[FlipperKitReactPlugin new]]; - [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; - [client start]; -} +//#import +//#import +//#import +//#import +//#import +//#import +//static void InitializeFlipper(UIApplication *application) { +// FlipperClient *client = [FlipperClient sharedClient]; +// SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; +// [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; +// [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; +// [client addPlugin:[FlipperKitReactPlugin new]]; +// [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; +// [client start]; +//} #endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - #if DEBUG - InitializeFlipper(application); - #endif +// #if DEBUG +// InitializeFlipper(application); +// #endif RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"TorExample" diff --git a/example/ios/TorExample/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/TorExample/Images.xcassets/AppIcon.appiconset/Contents.json index 118c98f..8121323 100644 --- a/example/ios/TorExample/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/example/ios/TorExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -2,37 +2,52 @@ "images" : [ { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/example/ios/TorExample/Info.plist b/example/ios/TorExample/Info.plist index b0a4dd2..6055ede 100644 --- a/example/ios/TorExample/Info.plist +++ b/example/ios/TorExample/Info.plist @@ -39,6 +39,12 @@ NSLocationWhenInUseUsageDescription + UIBackgroundModes + + fetch + processing + remote-notification + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/example/src/App.tsx b/example/src/App.tsx index 3e00595..72703b6 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,21 +1,41 @@ import * as React from 'react'; -import { StyleSheet, View, Text, Button } from 'react-native'; -import Tor from 'react-native-tor'; +import { StyleSheet, View, Text, Button, TextInput } from 'react-native'; +import TorBridge from 'react-native-tor'; export default function App() { const [socksPort, setSocksPort] = React.useState(); + const [onion, setOnion] = React.useState( + 'http://keybase5wmilwokqirssclfnsqrjdsi7jdir5wy7y7iu3tanwmtp6oid.onion' + ); const startTor = async () => { - const port = await Tor.startDaemon(); - console.log('Tor started socks port', port); - setSocksPort(port); + try { + const port = await TorBridge.startDaemon(); + console.log('Tor started socks port', port); + setSocksPort(port); + } catch (err) { + console.error(err); + } }; const stopTor = async () => { - await Tor.stopDaemon(); + try { + await TorBridge.stopDaemon(); + } catch (err) { + console.error(err); + } setSocksPort(undefined); console.log('Tor stopped'); }; + const getOnion = async () => { + try { + if (!onion) throw 'No onion detected'; + let resp = await TorBridge.getOnionUrl(onion); + console.log('got resp', resp); + } catch (err) { + console.error(err); + } + }; return ( @@ -23,9 +43,22 @@ export default function App() { Start Tor - + + {!!socksPort && ( + + + + + )} SocksPort: {socksPort} diff --git a/ios/Tor-Bridging-Header.h b/ios/Tor-Bridging-Header.h index 5fff35c..84660e3 100644 --- a/ios/Tor-Bridging-Header.h +++ b/ios/Tor-Bridging-Header.h @@ -1 +1 @@ -#import +#import "React/RCTBridgeModule.h" diff --git a/ios/Tor.m b/ios/Tor.m index 7a30b51..c328e92 100644 --- a/ios/Tor.m +++ b/ios/Tor.m @@ -1,9 +1,19 @@ -#import +#import "React/RCTBridgeModule.h" -@interface RCT_EXTERN_MODULE(Tor, NSObject) +@interface RCT_EXTERN_REMAP_MODULE(TorBridge, Tor, NSObject) -RCT_EXTERN_METHOD(multiply:(float)a withB:(float)b - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD( + startDaemon:(RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) +RCT_EXTERN_METHOD( + stopDaemon:(RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) +RCT_EXTERN_METHOD( + getOnionUrl:(NSString*)url + resolver:(RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) @end diff --git a/ios/Tor.swift b/ios/Tor.swift index 63899a0..7ca10e5 100644 --- a/ios/Tor.swift +++ b/ios/Tor.swift @@ -1,8 +1,88 @@ @objc(Tor) class Tor: NSObject { - - @objc(multiply:withB:withResolver:withRejecter:) - func multiply(a: Float, b: Float, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void { - resolve(a*b) + var service:Optional = nil; + + func getProxiedClient()->URLSession{ + let config = URLSessionConfiguration.default; + config.requestCachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData; + config.connectionProxyDictionary = [AnyHashable: Any](); + config.connectionProxyDictionary?[kCFNetworkProxiesHTTPEnable as String] = 1; + config.connectionProxyDictionary?[kCFStreamPropertySOCKSProxyHost as String] = "127.0.0.1"; + config.connectionProxyDictionary?[kCFStreamPropertySOCKSProxyPort as String] = 19032; + config.connectionProxyDictionary?[kCFProxyTypeSOCKS as String] = 1; + return URLSession.init(configuration: config, delegate: nil, delegateQueue: OperationQueue.current) + } + + @objc(getOnionUrl:resolver:rejecter:) + func getOnionUrl(url:String, resolve:@escaping RCTPromiseResolveBlock,reject:@escaping RCTPromiseRejectBlock){ + let session = getProxiedClient(); + guard let _url = URL(string:url) else { + // FIXME just to test + reject("TOR","Could not parse url",NSError.init(domain: "TOR", code: 404)); + return; + } + do{ + let task = session.dataTask(with: _url) { data, resp, error in + guard let dataResp = data , error == nil else { + reject("TOR",error?.localizedDescription,error); + return; + } + resolve(dataResp.base64EncodedString()); + return; + } + task.resume(); + } catch{ + reject("TOR",error.localizedDescription,error); + + } + } + + @objc(startDaemon:rejecter:) + func startDaemon( resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)->Void{ + if service != nil { + reject("TOR","Tor Service Already Running. Call `stopDaemon` first.",NSError.init(domain: "TOR", code: 99)); + return; + } + + do { + let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(),isDirectory: true) + let socksPort:UInt16 = 19032; + // this gives file:///Users/.../tmp/ so we remove the file:// prefix and trailing slash + let path = try String(temporaryDirectoryURL.absoluteString.dropFirst(7).dropLast()); + let call_result = get_owned_TorService(path, socksPort).pointee; + switch(call_result.message.tag){ + case Success: + service = Optional.some(call_result.result); + resolve(socksPort); + return; + case Error: + // Convert RustByteSlice to String + let error_body = call_result.message.error._0 + let buffer = UnsafeBufferPointer(start: error_body.bytes, count: Int(error_body.len)); + if let string = String(bytes: buffer, encoding: String.Encoding.utf8){ + reject("TOR",string,NSError.init(domain: "TOR", code: 0)) + } else { + reject("TOR","unknown",NSError.init(domain: "TOR", code: 99)); + } + return; + default: + reject("TOR","unknown-default",NSError.init(domain: "TOR", code: 99)); + return; + } + + } + catch { + reject("TOR",error.localizedDescription,error); + } + + } + + @objc(stopDaemon:rejecter:) + func stopDaemon( resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)->Void { + if let hasSevice = service { + shutdown_owned_TorService(hasSevice); + service = nil + } + resolve(true); } } diff --git a/ios/Tor.xcodeproj/project.pbxproj b/ios/Tor.xcodeproj/project.pbxproj index d4cf892..3460003 100644 --- a/ios/Tor.xcodeproj/project.pbxproj +++ b/ios/Tor.xcodeproj/project.pbxproj @@ -7,11 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - - - F4FF95D7245B92E800C19C63 /* Tor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* Tor.swift */; }; - - 5E555C0D2413F4C50049A1A2 /* Tor.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* Tor.mm */; }; + 85C70A3025646C68003FF04C /* libsifir_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 85C70A2E25646C67003FF04C /* libsifir_ios.a */; }; + F4FF95D7245B92E800C19C63 /* Tor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* Tor.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -27,13 +24,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 134814201AA4EA6300B7C361 /* libTor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTor.a; sourceTree = BUILT_PRODUCTS_DIR; }; - - + 855294A225646B1000C35C05 /* libTor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libTor.a; path = "/Users/ghassanabidi/Projects/react-native-tor/ios/build/Debug-iphoneos/libTor.a"; sourceTree = ""; }; + 85C70A2E25646C67003FF04C /* libsifir_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsifir_ios.a; sourceTree = ""; }; + 85C70A2F25646C67003FF04C /* sifir-tor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "sifir-tor.h"; sourceTree = ""; }; B3E7B5891CC2AC0600A0062D /* Tor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tor.m; sourceTree = ""; }; F4FF95D5245B92E700C19C63 /* Tor-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tor-Bridging-Header.h"; sourceTree = ""; }; F4FF95D6245B92E800C19C63 /* Tor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tor.swift; sourceTree = ""; }; - /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -41,30 +37,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 85C70A3025646C68003FF04C /* libsifir_ios.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 134814211AA4EA7D00B7C361 /* Products */ = { - isa = PBXGroup; - children = ( - 134814201AA4EA6300B7C361 /* libTor.a */, - ); - name = Products; - sourceTree = ""; - }; 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - - F4FF95D6245B92E800C19C63 /* Tor.swift */, B3E7B5891CC2AC0600A0062D /* Tor.m */, F4FF95D5245B92E700C19C63 /* Tor-Bridging-Header.h */, - - 134814211AA4EA7D00B7C361 /* Products */, + 85C70A2E25646C67003FF04C /* libsifir_ios.a */, + 85C70A2F25646C67003FF04C /* sifir-tor.h */, ); sourceTree = ""; }; @@ -85,7 +72,7 @@ ); name = Tor; productName = RCTDataManager; - productReference = 134814201AA4EA6300B7C361 /* libTor.a */; + productReference = 855294A225646B1000C35C05 /* libTor.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -125,11 +112,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - - F4FF95D7245B92E800C19C63 /* Tor.swift in Sources */, - B3E7B58A1CC2AC0600A0062D /* Tor.m in Sources */, - ); runOnlyForDeploymentPostprocessing = 0; }; @@ -238,15 +221,16 @@ "$(SRCROOT)/../../../React/**", "$(SRCROOT)/../../react-native/React/**", ); - LIBRARY_SEARCH_PATHS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Tor; SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Tor-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - }; name = Debug; }; @@ -259,14 +243,15 @@ "$(SRCROOT)/../../../React/**", "$(SRCROOT)/../../react-native/React/**", ); - LIBRARY_SEARCH_PATHS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Tor; SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Tor-Bridging-Header.h"; SWIFT_VERSION = 5.0; - }; name = Release; }; diff --git a/ios/sifir-tor.h b/ios/sifir-tor.h new file mode 100644 index 0000000..e99a11a --- /dev/null +++ b/ios/sifir-tor.h @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +typedef struct OwnedTorService OwnedTorService_t; + +typedef struct { + const uint8_t *bytes; + uintptr_t len; +} RustByteSlice; + +typedef enum { + Success, + Error, +} ResultMessage_Tag; + +typedef struct { + RustByteSlice _0; +} Error_Body; + +typedef struct { + ResultMessage_Tag tag; + union { + Error_Body error; + }; +} ResultMessage; + +/** + * Since the FFI simply starts and shutdowns the daemon we use an + * Opaque pointer here to pass across the FFI + */ +typedef struct { + OwnedTorService_t *result; + ResultMessage message; +} BoxedResult_OwnedTorService; + +BoxedResult_OwnedTorService *get_owned_TorService(const char *data_dir, uint16_t socks_port); + +/** + *# Safety + * Destroy and release ownedTorBox which will shut down owned connection and shutdown daemon + */ +void shutdown_owned_TorService(OwnedTorService_t *owned_client); diff --git a/package.json b/package.json index 87b468d..202fc44 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,13 @@ "ios", "android" ], - "repository": "https://github.com/gabidi/react-native-tor", + "repository": "", "author": "gabidi (https://github.com/gabidi)", "license": "MIT", "bugs": { - "url": "https://github.com/gabidi/react-native-tor/issues" + "url": "https://github.com/Sifir-io/react-native-tor/issues" }, - "homepage": "https://github.com/gabidi/react-native-tor#readme", + "homepage": "https://github.com/Sifir-io/react-native-tor/", "devDependencies": { "@commitlint/config-conventional": "^8.3.4", "@react-native-community/bob": "^0.16.2", diff --git a/react-native-tor.podspec b/react-native-tor.podspec index d007fce..fd66315 100644 --- a/react-native-tor.podspec +++ b/react-native-tor.podspec @@ -11,11 +11,11 @@ Pod::Spec.new do |s| s.authors = package["author"] s.platforms = { :ios => "9.0" } - s.source = { :git => "https://github.com/gabidi/react-native-tor.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/Sifir-io/react-native-tor.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,mm,swift}" - + s.ios.vendored_library = "ios/libsifir_ios.a" s.dependency "React" end diff --git a/src/index.tsx b/src/index.tsx index 51b98de..4dc8a70 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,8 +4,9 @@ type SocksPortNumber = number; type TorType = { startDaemon(): Promise; stopDaemon(): Promise; + getOnionUrl(url: string): Promise; }; -const { Tor } = NativeModules; +const { TorBridge } = NativeModules; -export default Tor as TorType; +export default TorBridge as TorType;