FBDeviceControl/Commands/FBDeviceSocketForwardingCommands.m (87 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBDeviceSocketForwardingCommands.h"
#import "FBDevice.h"
#import "FBDeviceControlError.h"
@interface FBDeviceSocketForwardingCommands ()
@property (nonatomic, weak, readonly) FBDevice *device;
@end
@implementation FBDeviceSocketForwardingCommands
#pragma mark Initializers
+ (instancetype)commandsWithTarget:(FBDevice *)target
{
return [[self alloc] initWithDevice:target];
}
- (instancetype)initWithDevice:(FBDevice *)device
{
self = [super init];
if (!self) {
return nil;
}
_device = device;
return self;
}
#pragma mark FBSocketForwardingCommands Implementation
- (FBFuture<NSNull *> *)drainLocalFileInput:(int)localFileDescriptorInput localFileOutput:(int)localFileDescriptorOutput remotePort:(int)remotePort
{
NSError *error = nil;
id<FBDataConsumer> localConsumer = [FBFileWriter asyncWriterWithFileDescriptor:localFileDescriptorOutput closeOnEndOfFile:NO error:&error];
if (!localConsumer) {
return [FBFuture futureWithError:error];
}
return [[self
consumerForRemotePort:remotePort writingTo:localConsumer]
onQueue:self.device.asyncQueue pop:^(id<FBDataConsumer> remoteConsumer) {
id<FBFileReader> reader = [FBFileReader readerWithFileDescriptor:localFileDescriptorInput closeOnEndOfFile:NO consumer:remoteConsumer logger:nil];
return [[reader
startReading]
onQueue:self.device.asyncQueue fmap:^(id _) {
return reader.finishedReading;
}];
}];
}
#pragma mark Private
- (FBFutureContext<id<FBDataConsumer>> *)consumerForRemotePort:(int)remotePort writingTo:(id<FBDataConsumer>)consumer
{
return [[self
localSocketFromRemotePort:remotePort]
onQueue:self.device.asyncQueue pend:^(NSNumber *remoteSocket) {
NSError *error = nil;
id<FBDataConsumer> writer = [FBFileWriter asyncWriterWithFileDescriptor:remoteSocket.intValue closeOnEndOfFile:NO error:&error];
if (!writer) {
return [FBFuture futureWithError:error];
}
id<FBFileReader> reader = [FBFileReader readerWithFileDescriptor:remoteSocket.intValue closeOnEndOfFile:NO consumer:consumer logger:nil];
return [[reader
startReading]
mapReplace:writer];
}];
}
- (FBFutureContext<NSNumber *> *)localSocketFromRemotePort:(int)remotePort
{
id<FBControlCoreLogger> logger = self.device.logger;
return [[[self.device
connectToDeviceWithPurpose:@"Socket Connection"]
onQueue:self.device.workQueue pop:^(id<FBDeviceCommands> device) {
int connectionID = device.calls.GetConnectionID(device.amDeviceRef);
if (connectionID <= 0) {
return [[FBDeviceControlError
describeFormat:@"Failed to get ConnectionID from Device"]
failFuture];
}
[logger logFormat:@"Got connection ID %d, for device. Connecting to remote port %d", connectionID, remotePort];
int localSocket = 0;
int status = device.calls.USBMuxConnectByPort(connectionID, htons(remotePort), &localSocket);
if (status != 0) {
return [[FBDeviceControlError
describeFormat:@"Failed to connect to remote port %d", remotePort]
failFuture];
}
[logger logFormat:@"Got local socket %d for remote port %d", localSocket, remotePort];
return [FBFuture futureWithResult:@(localSocket)];
}]
onQueue:self.device.asyncQueue contextualTeardown:^(NSNumber *localSocketNumber, FBFutureState _) {
[logger logFormat:@"Closing local socket %@", localSocketNumber];
close(localSocketNumber.intValue);
return FBFuture.empty;
}];
}
@end