/* * 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" #import #import #import #import "SKMacros.h" #import "SKPortForwardingCommon.h" @interface SKPortForwardingServer () @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; @end @implementation SKPortForwardingServer - (instancetype)init { if (self = [super init]) { _socketQueue = dispatch_queue_create("SKPortForwardingServer", DISPATCH_QUEUE_SERIAL); _lastClientSocketTag = 0; _clientSockets = [NSMutableDictionary dictionary]; _protocol = [[PTProtocol alloc] initWithDispatchQueue:_socketQueue]; } return self; } - (void)dealloc { [self close]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)forwardConnectionsFromPort:(NSUInteger)port { [self _forwardConnectionsFromPort:port reportError:YES]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { [self _forwardConnectionsFromPort:port reportError:NO]; }]; } - (void)_forwardConnectionsFromPort:(NSUInteger)port reportError:(BOOL)shouldReportError { GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketQueue]; NSError *listenError; if ([serverSocket acceptOnPort:port error:&listenError]) { self.serverSocket = serverSocket; } else { if (shouldReportError) { SKLog(@"Failed to listen: %@", listenError); } } } - (void)listenForMultiplexingChannelOnPort:(NSUInteger)port { [self _listenForMultiplexingChannelOnPort:port reportError:YES]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { [self _listenForMultiplexingChannelOnPort:port reportError:NO]; }]; } - (void)_listenForMultiplexingChannelOnPort:(NSUInteger)port reportError:(BOOL)shouldReportError { PTChannel *channel = [[PTChannel alloc] initWithProtocol:_protocol delegate:self]; [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); } } else { SKTrace(@"Listening on 127.0.0.1:%lu", (unsigned long)port); self.serverChannel = channel; } }]; } - (void)close { if (self.serverChannel) { [self.serverChannel close]; self.serverChannel = nil; } [self.serverSocket disconnect]; } #pragma mark - PTChannelDelegate - (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]; } // 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); } - (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)]; [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] withTimeout:-1 tag:0]; SKTrace(@"channel -> socket (%d), %zu bytes", tag, payload.length); } if (type == SKPortForwardingFrameTypeClosePipe) { GCDAsyncSocket *sock = self.clientSockets[@(tag)]; [sock disconnectAfterWriting]; } } - (void)ioFrameChannel:(PTChannel *)channel didEndWithError:(NSError *)error { for (GCDAsyncSocket *sock in [_clientSockets objectEnumerator]) { [sock setDelegate:nil]; [sock disconnect]; } [self.clientSockets removeAllObjects]; SKTrace(@"Disconnected from %@, error = %@", channel.userInfo, error); } #pragma mark - GCDAsyncSocketDelegate - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket { dispatch_block_t block = ^() { if (!self.peerChannel) { [newSocket setDelegate:nil]; [newSocket disconnect]; } 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); [newSocket readDataWithTimeout:-1 tag:0]; }]; }; if (_peerChannel) { block(); } else { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), _socketQueue, block); } } - (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); [sock readDataWithTimeout:-1 tag:_]; }]; } - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err { 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); }]; } @end