diff --git a/PortForwardingMacApp/main.m b/PortForwardingMacApp/main.m new file mode 100644 index 000000000..9c4afc369 --- /dev/null +++ b/PortForwardingMacApp/main.m @@ -0,0 +1,34 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#import + +static const char kPortForwardParamPrefix[] = "-portForward="; +static const char kMultiplexChannelPortParamPrefix[] = "-multiplexChannelPort="; + +static BOOL prefix(const char *pre, const char *str) { + return strncmp(pre, str, strlen(pre)) == 0; +} + +int main(int argc, char *argv[]) +{ + long connectionsPort = 8081; + long multiplexPort = 8025; + + for (int i = 1; i < argc; ++i) { + if (prefix(kPortForwardParamPrefix, argv[i])) { + connectionsPort = strtol(argv[i] + strlen(kPortForwardParamPrefix), NULL, 10); + } else if (prefix(kMultiplexChannelPortParamPrefix, argv[i])) { + multiplexPort = strtol(argv[i] + strlen(kMultiplexChannelPortParamPrefix), NULL, 10); + } + } + + FKPortForwardingClient *client = [FKPortForwardingClient new]; + [client forwardConnectionsToPort:connectionsPort]; + [client connectToMultiplexingChannelOnPort:multiplexPort]; + + [[NSRunLoop currentRunLoop] run]; + client = nil; + return 0; +} diff --git a/iOS/FlipperKit.podspec b/iOS/FlipperKit.podspec index 85694af6f..6ef5950e8 100644 --- a/iOS/FlipperKit.podspec +++ b/iOS/FlipperKit.podspec @@ -46,17 +46,26 @@ Pod::Spec.new do |spec| ss.private_header_files = 'iOS/FlipperKit/FBCxxUtils/**/*.h' end + spec.subspec "FKPortForwarding" do |ss| + ss.header_dir = "FKPortForwarding" + ss.dependency 'CocoaAsyncSocket', '~> 7.6' + ss.dependency 'PeerTalk', '~>0.0.2' + ss.compiler_flags = folly_compiler_flags + ss.source_files = 'iOS/FlipperKit/FKPortForwarding/FKPortForwarding{Server,Common}.{h,m}' + ss.private_header_files = 'iOS/FlipperKit/FKPortForwarding/FKPortForwarding{Server,Common}.h' + end + + spec.subspec "Core" do |ss| ss.dependency 'FlipperKit/FBDefines' ss.dependency 'FlipperKit/FBCxxUtils' ss.dependency 'FlipperKit/CppBridge' + ss.dependency 'FlipperKit/FKPortForwarding' ss.dependency 'Folly', '~>1.1' ss.dependency 'Flipper', '~>'+flipperkit_version - ss.dependency 'CocoaAsyncSocket', '~> 7.6' - ss.dependency 'PeerTalk', '~>0.0.2' ss.dependency 'OpenSSL-Static', '1.0.2.c1' ss.compiler_flags = folly_compiler_flags - ss.source_files = 'iOS/FlipperKit/FBDefines/*.{h,cpp,m,mm}', 'iOS/FlipperKit/CppBridge/*.{h,mm}', 'iOS/FlipperKit/FBCxxUtils/*.{h,mm}', 'iOS/FlipperKit/Utilities/**/*.{h,m}', 'iOS/FlipperKit/*.{h,m,mm}' + ss.source_files = 'iOS/FlipperKit/FBDefines/*.{h,cpp,m,mm}', 'iOS/FlipperKit/CppBridge/*.{h,mm}', 'iOS/FlipperKit/FBCxxUtils/*.{h,mm}', 'iOS/FlipperKit/*.{h,m,mm}' ss.public_header_files = 'iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h', 'iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h', 'iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h', diff --git a/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.h b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.h new file mode 100644 index 000000000..98829e0b1 --- /dev/null +++ b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#import + +@interface FKPortForwardingClient : NSObject + +- (instancetype)init; + +- (void)forwardConnectionsToPort:(NSUInteger)port; +- (void)connectToMultiplexingChannelOnPort:(NSUInteger)port; +- (void)close; + +@end diff --git a/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.m b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.m new file mode 100644 index 000000000..c951262d7 --- /dev/null +++ b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingClient.m @@ -0,0 +1,297 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "FKPortForwardingClient.h" + +#import +#import +#import + +#import "FKPortForwardingCommon.h" + +static const NSTimeInterval ReconnectDelay = 1.0; + +@interface FKPortForwardingClient () +{ + NSUInteger _destPort; + NSUInteger _channelPort; + NSNumber *_connectingToDeviceID; + NSNumber *_connectedDeviceID; + NSDictionary *_connectedDeviceProperties; + BOOL _notConnectedQueueSuspended; + PTChannel *_connectedChannel; + dispatch_queue_t _notConnectedQueue; + dispatch_queue_t _clientSocketsQueue; + NSMutableDictionary *_clientSockets; +} + +@property (atomic, readonly) NSNumber *connectedDeviceID; +@property (atomic, assign) PTChannel *connectedChannel; + +@end + +@implementation FKPortForwardingClient + +@synthesize connectedDeviceID = _connectedDeviceID; + +- (instancetype)init +{ + if (self = [super init]) { + _notConnectedQueue = dispatch_queue_create("FKPortForwarding.notConnectedQueue", DISPATCH_QUEUE_SERIAL); + _clientSocketsQueue = dispatch_queue_create("FKPortForwarding.clients", DISPATCH_QUEUE_SERIAL); + _clientSockets = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)forwardConnectionsToPort:(NSUInteger)port +{ + _destPort = port; +} + +- (void)connectToMultiplexingChannelOnPort:(NSUInteger)port +{ + _channelPort = port; + [self startListeningForDevices]; + [self enqueueConnectToLocalIPv4Port]; +} + +- (void)close +{ + [self.connectedChannel close]; +} + +- (PTChannel *)connectedChannel { + return _connectedChannel; +} + +- (void)setConnectedChannel:(PTChannel *)connectedChannel { + _connectedChannel = connectedChannel; + + if (!_connectedChannel) { + for (GCDAsyncSocket *sock in [_clientSockets objectEnumerator]) { + [sock setDelegate:nil]; + [sock disconnect]; + } + [_clientSockets removeAllObjects]; + } + + // Toggle the notConnectedQueue_ depending on if we are connected or not + if (!_connectedChannel && _notConnectedQueueSuspended) { + dispatch_resume(_notConnectedQueue); + _notConnectedQueueSuspended = NO; + } else if (_connectedChannel && !_notConnectedQueueSuspended) { + dispatch_suspend(_notConnectedQueue); + _notConnectedQueueSuspended = YES; + } + + if (!_connectedChannel && _connectingToDeviceID) { + [self enqueueConnectToUSBDevice]; + } +} + + +#pragma mark - PTChannelDelegate + +- (void)ioFrameChannel:(PTChannel *)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData *)payload { + //NSLog(@"received %@, %u, %u, %@", channel, type, tag, payload); + + if (type == FKPortForwardingFrameTypeOpenPipe) { + GCDAsyncSocket *sock = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_clientSocketsQueue]; + sock.userData = @(tag); + _clientSockets[@(tag)] = sock; + + NSError *connectError; + if (![sock connectToHost:@"localhost" onPort:_destPort error:&connectError]) { + FBPFLog(@"Failed to connect to local %lu - %@", (unsigned long)_destPort, connectError); + } + + FBPFTrace(@"open socket (%d)", tag); + } + + if (type == FKPortForwardingFrameTypeWriteToPipe) { + GCDAsyncSocket *sock = _clientSockets[@(tag)]; + [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] withTimeout:-1 tag:0]; + FBPFTrace(@"channel -> socket (%d) %zu bytes", tag, payload.length); + } + + if (type == FKPortForwardingFrameTypeClosePipe) { + GCDAsyncSocket *sock = _clientSockets[@(tag)]; + [sock disconnectAfterWriting]; + FBPFTrace(@"close socket (%d)", tag); + } +} + +- (void)ioFrameChannel:(PTChannel *)channel didEndWithError:(NSError *)error { + if (_connectedDeviceID && [_connectedDeviceID isEqualToNumber:channel.userInfo]) { + [self didDisconnectFromDevice:_connectedDeviceID]; + } + + if (_connectedChannel == channel) { + FBPFTrace(@"Disconnected from %@", channel.userInfo); + self.connectedChannel = nil; + } +} + + +#pragma mark - GCDAsyncSocketDelegate + + +- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port +{ + FBPFTrace(@"socket (%ld) connected to %@", (long)[sock.userData integerValue], host); + [sock readDataWithTimeout:-1 tag:0]; +} + +- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err +{ + UInt32 tag = [sock.userData unsignedIntValue]; + [_clientSockets removeObjectForKey:@(tag)]; + FBPFTrace(@"socket (%d) disconnected", (unsigned int)tag); + + [_connectedChannel sendFrameOfType:FKPortForwardingFrameTypeClosePipe tag:tag withPayload:nil callback:nil]; +} + +- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)_ +{ + UInt32 tag = [sock.userData unsignedIntValue]; + [_connectedChannel sendFrameOfType:FKPortForwardingFrameTypeWriteToPipe tag:tag withPayload:NSDataToGCDData(data) callback:^(NSError *error) { + FBPFTrace(@"channel -> socket (%d), %lu bytes", (unsigned int)tag, (unsigned long)data.length); + [sock readDataWithTimeout:-1 tag:0]; + }]; +} + +#pragma mark - Wired device connections + + +- (void)startListeningForDevices { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + __weak typeof(self) weakSelf = self; + + [nc addObserverForName:PTUSBDeviceDidAttachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) { + NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"]; + //NSLog(@"PTUSBDeviceDidAttachNotification: %@", note.userInfo); + FBPFTrace(@"PTUSBDeviceDidAttachNotification: %@", deviceID); + + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + dispatch_async(strongSelf->_notConnectedQueue, ^{ + [strongSelf didAttachToDevice:deviceID note:note]; + }); + }]; + + [nc addObserverForName:PTUSBDeviceDidDetachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) { + NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"]; + //NSLog(@"PTUSBDeviceDidDetachNotification: %@", note.userInfo); + FBPFTrace(@"PTUSBDeviceDidDetachNotification: %@", deviceID); + + [weakSelf didDetachFromDevice:deviceID]; + }]; +} + +- (void)didAttachToDevice:(NSNumber *)deviceID note:(NSNotification *)note +{ + if (!_connectingToDeviceID || ![deviceID isEqualToNumber:_connectingToDeviceID]) { + [self disconnectFromCurrentChannel]; + _connectingToDeviceID = deviceID; + _connectedDeviceProperties = [note.userInfo objectForKey:@"Properties"]; + [self enqueueConnectToUSBDevice]; + } +} + +- (void)didDetachFromDevice:(NSNumber *)deviceID +{ + if ([_connectingToDeviceID isEqualToNumber:deviceID]) { + _connectedDeviceProperties = nil; + _connectingToDeviceID = nil; + if (_connectedChannel) { + [_connectedChannel close]; + } + } +} + + +- (void)didDisconnectFromDevice:(NSNumber *)deviceID { + FBPFLog(@"Disconnected from device #%@", deviceID); + if ([_connectedDeviceID isEqualToNumber:deviceID]) { + [self willChangeValueForKey:@"connectedDeviceID"]; + _connectedDeviceID = nil; + [self didChangeValueForKey:@"connectedDeviceID"]; + } +} + + +- (void)disconnectFromCurrentChannel { + if (_connectedDeviceID && _connectedChannel) { + [_connectedChannel close]; + self.connectedChannel = nil; + } +} + +- (void)enqueueConnectToLocalIPv4Port { + dispatch_async(_notConnectedQueue, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self connectToLocalIPv4Port]; + }); + }); +} + + +- (void)connectToLocalIPv4Port { + PTChannel *channel = [PTChannel channelWithDelegate:self]; + channel.userInfo = [NSString stringWithFormat:@"127.0.0.1:%lu", (unsigned long)_channelPort]; + [channel connectToPort:_channelPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error, PTAddress *address) { + if (error) { + if (error.domain == NSPOSIXErrorDomain && (error.code == ECONNREFUSED || error.code == ETIMEDOUT)) { + // this is an expected state + } else { + FBPFTrace(@"Failed to connect to 127.0.0.1:%lu: %@", (unsigned long)_channelPort, error); + } + } else { + [self disconnectFromCurrentChannel]; + self.connectedChannel = channel; + channel.userInfo = address; + FBPFLog(@"Connected to %@", address); + } + [self performSelector:@selector(enqueueConnectToLocalIPv4Port) withObject:nil afterDelay:ReconnectDelay]; + }]; +} + +- (void)enqueueConnectToUSBDevice { + dispatch_async(_notConnectedQueue, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self connectToUSBDevice]; + }); + }); +} + + +- (void)connectToUSBDevice { + PTChannel *channel = [PTChannel channelWithDelegate:self]; + channel.userInfo = _connectingToDeviceID; + channel.delegate = self; + + [channel connectToPort:(int)_channelPort overUSBHub:PTUSBHub.sharedHub deviceID:_connectingToDeviceID callback:^(NSError *error) { + [self didConnectToChannel:channel withError:error]; + }]; +} + +- (void)didConnectToChannel:(PTChannel *)channel withError:(NSError *)error +{ + if (error) { + FBPFTrace(@"Failed to connect to device #%@: %@", channel.userInfo, error); + if (channel.userInfo == _connectingToDeviceID) { + [self performSelector:@selector(enqueueConnectToUSBDevice) withObject:nil afterDelay:ReconnectDelay]; + } + } else { + _connectedDeviceID = _connectingToDeviceID; + self.connectedChannel = channel; + FBPFLog(@"Connected to device #%@\n%@", _connectingToDeviceID, _connectedDeviceProperties); + } +} + + +@end diff --git a/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h new file mode 100644 index 000000000..8b8357dfe --- /dev/null +++ b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#import + +#define FBPFTrace(...) /*NSLog(__VA_ARGS__)*/ +#define FBPFLog(...) NSLog(__VA_ARGS__) + +enum { + FKPortForwardingFrameTypeOpenPipe = 201, + FKPortForwardingFrameTypeWriteToPipe = 202, + FKPortForwardingFrameTypeClosePipe = 203, +}; + +static dispatch_data_t NSDataToGCDData(NSData *data) { + __block NSData *retainedData = data; + return dispatch_data_create(data.bytes, data.length, nil, ^{ + retainedData = nil; + }); +} diff --git a/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h new file mode 100644 index 000000000..c73d7132c --- /dev/null +++ b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#import + +@interface FKPortForwardingServer : NSObject + +- (instancetype)init; + +- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port; +- (void)forwardConnectionsFromPort:(NSUInteger)port; +- (void)close; + +@end diff --git a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.m b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m similarity index 60% rename from iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.m rename to iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m index 7bb61075a..507f39517 100644 --- a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.m +++ b/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m @@ -1,39 +1,34 @@ -/* - * Copyright (c) 2018-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the LICENSE - * file in the root directory of this source tree. - * - */ -#import "SKPortForwardingServer.h" +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "FKPortForwardingServer.h" #import #import #import -#import "SKMacros.h" -#import "SKPortForwardingCommon.h" +#import "FKPortForwardingCommon.h" -@interface SKPortForwardingServer () +@interface FKPortForwardingServer () +{ + __weak PTChannel *_serverChannel; + __weak PTChannel *_peerChannel; -@property (nonatomic, weak) PTChannel *serverChannel; -@property (nonatomic, weak) PTChannel *peerChannel; - -@property (nonatomic, strong) GCDAsyncSocket *serverSocket; -@property (nonatomic, strong) NSMutableDictionary *clientSockets; -@property (nonatomic, assign) UInt32 lastClientSocketTag; -@property (nonatomic, strong) dispatch_queue_t socketQueue; -@property (nonatomic, strong) PTProtocol *protocol; + GCDAsyncSocket *_serverSocket; + NSMutableDictionary *_clientSockets; + UInt32 _lastClientSocketTag; + dispatch_queue_t _socketQueue; + PTProtocol *_protocol; +} @end -@implementation SKPortForwardingServer +@implementation FKPortForwardingServer - (instancetype)init { if (self = [super init]) { - _socketQueue = dispatch_queue_create("SKPortForwardingServer", DISPATCH_QUEUE_SERIAL); + _socketQueue = dispatch_queue_create("FKPortForwardingServer", DISPATCH_QUEUE_SERIAL); _lastClientSocketTag = 0; _clientSockets = [NSMutableDictionary dictionary]; _protocol = [[PTProtocol alloc] initWithDispatchQueue:_socketQueue]; @@ -60,10 +55,10 @@ GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketQueue]; NSError *listenError; if ([serverSocket acceptOnPort:port error:&listenError]) { - self.serverSocket = serverSocket; + _serverSocket = serverSocket; } else { if (shouldReportError) { - SKLog(@"Failed to listen: %@", listenError); + FBPFLog(@"Failed to listen: %@", listenError); } } } @@ -82,22 +77,22 @@ [channel listenOnPort:port IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) { if (error) { if (shouldReportError) { - SKLog(@"Failed to listen on 127.0.0.1:%lu: %@", (unsigned long)port, error); + FBPFLog(@"Failed to listen on 127.0.0.1:%lu: %@", (unsigned long)port, error); } } else { - SKTrace(@"Listening on 127.0.0.1:%lu", (unsigned long)port); - self.serverChannel = channel; + FBPFTrace(@"Listening on 127.0.0.1:%lu", (unsigned long)port); + self->_serverChannel = channel; } }]; } - (void)close { - if (self.serverChannel) { - [self.serverChannel close]; - self.serverChannel = nil; + if (_serverChannel) { + [_serverChannel close]; + _serverChannel = nil; } - [self.serverSocket disconnect]; + [_serverSocket disconnect]; } #pragma mark - PTChannelDelegate @@ -105,27 +100,27 @@ - (void)ioFrameChannel:(PTChannel *)channel didAcceptConnection:(PTChannel *)otherChannel fromAddress:(PTAddress *)address { // Cancel any other connection. We are FIFO, so the last connection // established will cancel any previous connection and "take its place". - if (self.peerChannel) { - [self.peerChannel cancel]; + if (_peerChannel) { + [_peerChannel cancel]; } // Weak pointer to current connection. Connection objects live by themselves // (owned by its parent dispatch queue) until they are closed. - self.peerChannel = otherChannel; - self.peerChannel.userInfo = address; - SKTrace(@"Connected to %@", address); + _peerChannel = otherChannel; + _peerChannel.userInfo = address; + FBPFTrace(@"Connected to %@", address); } - (void)ioFrameChannel:(PTChannel *)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData *)payload { //NSLog(@"didReceiveFrameOfType: %u, %u, %@", type, tag, payload); - if (type == SKPortForwardingFrameTypeWriteToPipe) { - GCDAsyncSocket *sock = self.clientSockets[@(tag)]; + if (type == FKPortForwardingFrameTypeWriteToPipe) { + GCDAsyncSocket *sock = _clientSockets[@(tag)]; [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] withTimeout:-1 tag:0]; - SKTrace(@"channel -> socket (%d), %zu bytes", tag, payload.length); + FBPFTrace(@"channel -> socket (%d), %zu bytes", tag, payload.length); } - if (type == SKPortForwardingFrameTypeClosePipe) { - GCDAsyncSocket *sock = self.clientSockets[@(tag)]; + if (type == FKPortForwardingFrameTypeClosePipe) { + GCDAsyncSocket *sock = _clientSockets[@(tag)]; [sock disconnectAfterWriting]; } } @@ -135,8 +130,8 @@ [sock setDelegate:nil]; [sock disconnect]; } - [self.clientSockets removeAllObjects]; - SKTrace(@"Disconnected from %@, error = %@", channel.userInfo, error); + [_clientSockets removeAllObjects]; + FBPFTrace(@"Disconnected from %@, error = %@", channel.userInfo, error); } @@ -145,7 +140,7 @@ - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket { dispatch_block_t block = ^() { - if (!self.peerChannel) { + if (!self->_peerChannel) { [newSocket setDelegate:nil]; [newSocket disconnect]; } @@ -153,9 +148,9 @@ UInt32 tag = ++self->_lastClientSocketTag; newSocket.userData = @(tag); newSocket.delegate = self; - self.clientSockets[@(tag)] = newSocket; - [self.peerChannel sendFrameOfType:SKPortForwardingFrameTypeOpenPipe tag:self->_lastClientSocketTag withPayload:nil callback:^(NSError *error) { - SKTrace(@"open socket (%d), error = %@", (unsigned int)tag, error); + self->_clientSockets[@(tag)] = newSocket; + [self->_peerChannel sendFrameOfType:FKPortForwardingFrameTypeOpenPipe tag:self->_lastClientSocketTag withPayload:nil callback:^(NSError *error) { + FBPFTrace(@"open socket (%d), error = %@", (unsigned int)tag, error); [newSocket readDataWithTimeout:-1 tag:0]; }]; }; @@ -170,9 +165,9 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)_ { UInt32 tag = [[sock userData] unsignedIntValue]; - SKTrace(@"Incoming data on socket (%d) - %lu bytes", (unsigned int)tag, (unsigned long)data.length); - [_peerChannel sendFrameOfType:SKPortForwardingFrameTypeWriteToPipe tag:tag withPayload:NSDataToGCDData(data) callback:^(NSError *error) { - SKTrace(@"socket (%d) -> channel %lu bytes, error = %@", (unsigned int)tag, (unsigned long)data.length, error); + FBPFTrace(@"Incoming data on socket (%d) - %lu bytes", (unsigned int)tag, (unsigned long)data.length); + [_peerChannel sendFrameOfType:FKPortForwardingFrameTypeWriteToPipe tag:tag withPayload:NSDataToGCDData(data) callback:^(NSError *error) { + FBPFTrace(@"socket (%d) -> channel %lu bytes, error = %@", (unsigned int)tag, (unsigned long)data.length, error); [sock readDataWithTimeout:-1 tag:_]; }]; } @@ -181,8 +176,8 @@ { UInt32 tag = [sock.userData unsignedIntValue]; [_clientSockets removeObjectForKey:@(tag)]; - [_peerChannel sendFrameOfType:SKPortForwardingFrameTypeClosePipe tag:tag withPayload:nil callback:^(NSError *error) { - SKTrace(@"socket (%d) disconnected, err = %@, peer error = %@", (unsigned int)tag, err, error); + [_peerChannel sendFrameOfType:FKPortForwardingFrameTypeClosePipe tag:tag withPayload:nil callback:^(NSError *error) { + FBPFTrace(@"socket (%d) disconnected, err = %@, peer error = %@", (unsigned int)tag, err, error); }]; } diff --git a/iOS/FlipperKit/FKPortForwarding/README.md b/iOS/FlipperKit/FKPortForwarding/README.md new file mode 100644 index 000000000..cd7686ac7 --- /dev/null +++ b/iOS/FlipperKit/FKPortForwarding/README.md @@ -0,0 +1,66 @@ +# FKPortForwarding + +FKPortForwarding lets you expose your Mac's port to iOS device via lightning +cable. The typical usecase is connecting to a TCP server that runs on OS X +from an iPhone app without common WiFi network. + +## Benefits: + + 1. No need to be on the same WiFi, worry about firewalls (fbguest) or VPN + 2. iOS app doesn't have to know your Mac's IP address + 3. Secure - communication is possible only when connected via USB + +## How it works + +iOS provides a way to connect to device's TCP server from Mac via USBHub, but +there is no API to connect from iOS to TCP server running on Mac. FKPortForwarding +uses [Peertalk](https://github.com/rsms/peertalk) to establish communication +channel from Mac to iOS, creates a TCP server on iOS and multiplexes all +connections to that server via the peertalk channel. Helper app running on Mac +listens for commands on the peertalk channel and initializes TCP connections +to local port and forwards all communication back via the same peertalk channel. + + + | + iOS Device | Mac + | + +----------------+ +----------------+ + |Peertalk Server | connect |Peertalk Client | + | <------------+ | + | | | | + | Port 8025| | | + +----+-----------+ +---------^------+ + | | + | | + incoming +----------------+ | | +--------------+ + connections |Proxy Server | | | |Real Server | + ------------->> | | +-------------+ commands | | | + | Port 8081| | create | | stream | | Port 8081| + +-+--------------+ +---------> Peertalk <----------+ +-^------------+ + | | Channel | ^ + | +--------+ | | +--------+ | outgoing + | | | onConnect | | connect | | | connections + +---> Client +---------------> OpenPipe +---------------> Client +-----+ + | #[tag] | onRead | | write | #[tag] | + | +---------------> WriteToPipe +---------------> | + | | onDisconnect | | disconnect | | + | +---------------> ClosePipe +---------------> | + | | | | | | + | | write | | onRead | | + | <---------------+ WriteToPipe <---------------+ | + | | close | | onDisconnect | | + | <---------------+ ClosePipe <---------------+ | + | | | | | | + +--------+ | | +--------+ + +-------------+ + +First, the library on iOS device creates a TCP server on the port we want to +forward (let's say 8081) and a special Peertalk server on port 8025. Mac helper +app looks for connected iOS devices, and once it finds one it connects to its +peertalk server. Only *one* channel is created that's going to be used for +multiplexing data. + +When a socket connects to local proxy server, FKPortForwarding is going to assign +a tag to the connection and use peertalk channel to tell Mac helper app to connect +to TCP port 8081 on Mac. Now events and data on both sides of the wire are going +to be multiplexed and transferred via the peertalk channel. diff --git a/iOS/FlipperKit/FlipperClient.mm b/iOS/FlipperKit/FlipperClient.mm index c74bf9bf8..415cedb72 100644 --- a/iOS/FlipperKit/FlipperClient.mm +++ b/iOS/FlipperKit/FlipperClient.mm @@ -18,7 +18,7 @@ #import "FlipperClient+Testing.h" #if !TARGET_OS_SIMULATOR -//#import "SKPortForwardingServer.h" +//#import #endif using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin; @@ -28,7 +28,7 @@ using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin; folly::ScopedEventBaseThread sonarThread; folly::ScopedEventBaseThread connectionThread; #if !TARGET_OS_SIMULATOR - // SKPortForwardingServer *_server; + // FKPortForwardingServer *_server; #endif } @@ -119,7 +119,7 @@ using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin; - (void)start; { #if !TARGET_OS_SIMULATOR - // _server = [SKPortForwardingServer new]; + // _server = [FKPortForwardingServer new]; // [_server forwardConnectionsFromPort:8088]; // [_server listenForMultiplexingChannelOnPort:8078]; #endif diff --git a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingCommon.h b/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingCommon.h deleted file mode 100644 index 13630dc81..000000000 --- a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingCommon.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2018-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the LICENSE - * file in the root directory of this source tree. - * - */ -#import - -enum { - SKPortForwardingFrameTypeOpenPipe = 201, - SKPortForwardingFrameTypeWriteToPipe = 202, - SKPortForwardingFrameTypeClosePipe = 203, -}; - -static dispatch_data_t NSDataToGCDData(NSData *data) { - __block NSData *retainedData = data; - return dispatch_data_create(data.bytes, data.length, nil, ^{ - retainedData = nil; - }); -} diff --git a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.h b/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.h deleted file mode 100644 index 72d4514a7..000000000 --- a/iOS/FlipperKit/Utilities/PortForwarding/SKPortForwardingServer.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the LICENSE - * file in the root directory of this source tree. - * - */ -#import - -@interface SKPortForwardingServer : NSObject - -- (instancetype)init; - -- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port; -- (void)forwardConnectionsFromPort:(NSUInteger)port; -- (void)close; - -@end