Add jailbroken iPhone MCP screen app
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
#import "IMCVideoStreamServer.h"
|
||||
#import "IMCCanvasView.h"
|
||||
#import <arpa/inet.h>
|
||||
#import <netinet/in.h>
|
||||
#import <sys/socket.h>
|
||||
#import <string.h>
|
||||
#import <unistd.h>
|
||||
|
||||
static const NSInteger IMCFrameSize = 15000;
|
||||
static const NSInteger IMCUDPChunkSize = 1000;
|
||||
static const NSInteger IMCUDPChunkCount = 15;
|
||||
static const NSInteger IMCColorStreamPort = 8083;
|
||||
static const NSUInteger IMCColorHeaderSize = 16;
|
||||
|
||||
@interface IMCVideoStreamServer ()
|
||||
@property (nonatomic, weak) IMCCanvasView *canvasView;
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
@property (nonatomic, assign) BOOL debugEnabled;
|
||||
@property (nonatomic, strong) NSDate *startedAt;
|
||||
@property (nonatomic, strong) NSDate *lastFrameAt;
|
||||
@property (nonatomic, copy) NSString *lastProtocol;
|
||||
@property (nonatomic, assign) uint64_t tcpConnections;
|
||||
@property (nonatomic, assign) uint64_t tcpFrames;
|
||||
@property (nonatomic, assign) uint64_t tcpBytes;
|
||||
@property (nonatomic, assign) uint64_t udpFrames;
|
||||
@property (nonatomic, assign) uint64_t udpPackets;
|
||||
@property (nonatomic, assign) uint64_t udpBytes;
|
||||
@property (nonatomic, assign) uint64_t invalidUDPPackets;
|
||||
@property (nonatomic, assign) uint64_t discoveryRequests;
|
||||
@property (nonatomic, assign) uint64_t colorConnections;
|
||||
@property (nonatomic, assign) uint64_t colorFrames;
|
||||
@property (nonatomic, assign) uint64_t colorBytes;
|
||||
@property (nonatomic, assign) NSUInteger lastColorWidth;
|
||||
@property (nonatomic, assign) NSUInteger lastColorHeight;
|
||||
@end
|
||||
|
||||
@implementation IMCVideoStreamServer
|
||||
|
||||
- (instancetype)initWithCanvasView:(IMCCanvasView *)canvasView {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_canvasView = canvasView;
|
||||
_startedAt = [NSDate date];
|
||||
_lastProtocol = @"none";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
if (self.running) {
|
||||
return;
|
||||
}
|
||||
self.running = YES;
|
||||
[NSThread detachNewThreadSelector:@selector(tcpLoop) toTarget:self withObject:nil];
|
||||
[NSThread detachNewThreadSelector:@selector(udpLoop) toTarget:self withObject:nil];
|
||||
[NSThread detachNewThreadSelector:@selector(discoveryLoop) toTarget:self withObject:nil];
|
||||
[NSThread detachNewThreadSelector:@selector(colorTCPLoop) toTarget:self withObject:nil];
|
||||
}
|
||||
|
||||
- (BOOL)readExactly:(NSUInteger)length fromSocket:(int)socketFD intoData:(NSMutableData *)data {
|
||||
[data setLength:length];
|
||||
NSUInteger received = 0;
|
||||
while (self.running && received < length) {
|
||||
ssize_t count = recv(socketFD,
|
||||
(uint8_t *)data.mutableBytes + received,
|
||||
length - received,
|
||||
0);
|
||||
if (count <= 0) {
|
||||
return NO;
|
||||
}
|
||||
received += (NSUInteger)count;
|
||||
}
|
||||
return received == length;
|
||||
}
|
||||
|
||||
- (void)colorTCPLoop {
|
||||
@autoreleasepool {
|
||||
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (serverSocket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons((uint16_t)IMCColorStreamPort);
|
||||
if (bind(serverSocket, (struct sockaddr *)&address, sizeof(address)) < 0 ||
|
||||
listen(serverSocket, 1) < 0) {
|
||||
close(serverSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
while (self.running) {
|
||||
@autoreleasepool {
|
||||
int clientSocket = accept(serverSocket, NULL, NULL);
|
||||
if (clientSocket < 0) {
|
||||
continue;
|
||||
}
|
||||
@synchronized (self) {
|
||||
self.colorConnections += 1;
|
||||
}
|
||||
|
||||
NSMutableData *header = [NSMutableData data];
|
||||
while ([self readExactly:IMCColorHeaderSize
|
||||
fromSocket:clientSocket
|
||||
intoData:header]) {
|
||||
const uint8_t *bytes = header.bytes;
|
||||
if (memcmp(bytes, "IMCR", 4) != 0 || bytes[4] != 1 || bytes[5] != 1) {
|
||||
break;
|
||||
}
|
||||
NSUInteger width = ((NSUInteger)bytes[6] << 8) | bytes[7];
|
||||
NSUInteger height = ((NSUInteger)bytes[8] << 8) | bytes[9];
|
||||
uint32_t payloadLength = ((uint32_t)bytes[10] << 24) |
|
||||
((uint32_t)bytes[11] << 16) |
|
||||
((uint32_t)bytes[12] << 8) |
|
||||
bytes[13];
|
||||
if (width == 0 || height == 0 || width > 2048 || height > 2048 ||
|
||||
payloadLength != width * height * 2 || payloadLength > 8 * 1024 * 1024) {
|
||||
break;
|
||||
}
|
||||
|
||||
NSMutableData *payload = [NSMutableData data];
|
||||
if (![self readExactly:payloadLength
|
||||
fromSocket:clientSocket
|
||||
intoData:payload]) {
|
||||
break;
|
||||
}
|
||||
NSData *snapshot = [payload copy];
|
||||
@synchronized (self) {
|
||||
self.colorFrames += 1;
|
||||
self.colorBytes += payloadLength;
|
||||
self.lastColorWidth = width;
|
||||
self.lastColorHeight = height;
|
||||
self.lastProtocol = @"color_tcp";
|
||||
self.lastFrameAt = [NSDate date];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.canvasView displayRGB565FrameBuffer:snapshot width:width height:height];
|
||||
});
|
||||
}
|
||||
close(clientSocket);
|
||||
}
|
||||
}
|
||||
close(serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tcpLoop {
|
||||
@autoreleasepool {
|
||||
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (serverSocket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(8081);
|
||||
|
||||
if (bind(serverSocket, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
close(serverSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(serverSocket, 1) < 0) {
|
||||
close(serverSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
while (self.running) {
|
||||
@autoreleasepool {
|
||||
int clientSocket = accept(serverSocket, NULL, NULL);
|
||||
if (clientSocket < 0) {
|
||||
continue;
|
||||
}
|
||||
[self noteTCPConnection];
|
||||
|
||||
NSMutableData *frame = [NSMutableData dataWithLength:IMCFrameSize];
|
||||
NSUInteger received = 0;
|
||||
while (self.running) {
|
||||
ssize_t count = recv(clientSocket,
|
||||
(uint8_t *)frame.mutableBytes + received,
|
||||
IMCFrameSize - received,
|
||||
0);
|
||||
if (count <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
received += (NSUInteger)count;
|
||||
[self noteTCPBytes:(NSUInteger)count];
|
||||
if (received == IMCFrameSize) {
|
||||
[self displayFrame:frame protocol:@"tcp"];
|
||||
received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(clientSocket);
|
||||
}
|
||||
}
|
||||
|
||||
close(serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)udpLoop {
|
||||
@autoreleasepool {
|
||||
int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (udpSocket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(8082);
|
||||
|
||||
if (bind(udpSocket, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
close(udpSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableData *frame = [NSMutableData dataWithLength:IMCFrameSize];
|
||||
uint8_t currentFrameID = 0;
|
||||
uint16_t chunksReceived = 0;
|
||||
BOOL hasFrame = NO;
|
||||
uint8_t packet[1002];
|
||||
|
||||
while (self.running) {
|
||||
ssize_t count = recvfrom(udpSocket, packet, sizeof(packet), 0, NULL, NULL);
|
||||
if (count < 2) {
|
||||
[self noteInvalidUDPPacket];
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t frameID = packet[0];
|
||||
uint8_t chunkIndex = packet[1];
|
||||
if (chunkIndex >= IMCUDPChunkCount || count < 2 + IMCUDPChunkSize) {
|
||||
[self noteInvalidUDPPacket];
|
||||
continue;
|
||||
}
|
||||
[self noteUDPPacketBytes:(NSUInteger)count];
|
||||
|
||||
if (!hasFrame || frameID != currentFrameID) {
|
||||
currentFrameID = frameID;
|
||||
chunksReceived = 0;
|
||||
hasFrame = YES;
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)frame.mutableBytes + chunkIndex * IMCUDPChunkSize,
|
||||
packet + 2,
|
||||
IMCUDPChunkSize);
|
||||
chunksReceived |= (uint16_t)(1 << chunkIndex);
|
||||
|
||||
if (chunksReceived == 0x7fff) {
|
||||
[self displayFrame:frame protocol:@"udp"];
|
||||
chunksReceived = 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(udpSocket);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)discoveryLoop {
|
||||
@autoreleasepool {
|
||||
int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (udpSocket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
|
||||
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(5000);
|
||||
|
||||
if (bind(udpSocket, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
close(udpSocket);
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
while (self.running) {
|
||||
struct sockaddr_in clientAddress;
|
||||
socklen_t clientLength = sizeof(clientAddress);
|
||||
ssize_t count = recvfrom(udpSocket,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
0,
|
||||
(struct sockaddr *)&clientAddress,
|
||||
&clientLength);
|
||||
if (count == 15 && memcmp(buffer, "DISCOVER_SCREEN", 15) == 0) {
|
||||
[self noteDiscoveryRequest];
|
||||
const char *reply = "SCREEN_IP_8080";
|
||||
sendto(udpSocket,
|
||||
reply,
|
||||
strlen(reply),
|
||||
0,
|
||||
(struct sockaddr *)&clientAddress,
|
||||
clientLength);
|
||||
}
|
||||
}
|
||||
|
||||
close(udpSocket);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)displayFrame:(NSData *)frame protocol:(NSString *)protocol {
|
||||
[self noteFrameForProtocol:protocol];
|
||||
NSData *snapshot = [frame copy];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.canvasView displayMonochromeFrameBuffer:snapshot];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSDictionary *)streamStats {
|
||||
@synchronized (self) {
|
||||
NSTimeInterval uptime = [[NSDate date] timeIntervalSinceDate:self.startedAt ?: [NSDate date]];
|
||||
NSTimeInterval secondsSinceLastFrame = self.lastFrameAt ? [[NSDate date] timeIntervalSinceDate:self.lastFrameAt] : -1.0;
|
||||
return @{
|
||||
@"debug_enabled": @(self.debugEnabled),
|
||||
@"running": @(self.running),
|
||||
@"tcp_port": @8081,
|
||||
@"udp_port": @8082,
|
||||
@"color_tcp_port": @(IMCColorStreamPort),
|
||||
@"discovery_port": @5000,
|
||||
@"frame_size_bytes": @(IMCFrameSize),
|
||||
@"udp_chunk_size_bytes": @(IMCUDPChunkSize),
|
||||
@"udp_chunks_per_frame": @(IMCUDPChunkCount),
|
||||
@"uptime_sec": @((NSInteger)uptime),
|
||||
@"tcp_connections": @(self.tcpConnections),
|
||||
@"tcp_frames": @(self.tcpFrames),
|
||||
@"tcp_bytes": @(self.tcpBytes),
|
||||
@"udp_frames": @(self.udpFrames),
|
||||
@"udp_packets": @(self.udpPackets),
|
||||
@"udp_bytes": @(self.udpBytes),
|
||||
@"invalid_udp_packets": @(self.invalidUDPPackets),
|
||||
@"discovery_requests": @(self.discoveryRequests),
|
||||
@"color_connections": @(self.colorConnections),
|
||||
@"color_frames": @(self.colorFrames),
|
||||
@"color_bytes": @(self.colorBytes),
|
||||
@"last_color_width": @(self.lastColorWidth),
|
||||
@"last_color_height": @(self.lastColorHeight),
|
||||
@"last_protocol": self.lastProtocol ?: @"none",
|
||||
@"seconds_since_last_frame": @(secondsSinceLastFrame)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetStreamStats {
|
||||
@synchronized (self) {
|
||||
self.startedAt = [NSDate date];
|
||||
self.lastFrameAt = nil;
|
||||
self.lastProtocol = @"none";
|
||||
self.tcpConnections = 0;
|
||||
self.tcpFrames = 0;
|
||||
self.tcpBytes = 0;
|
||||
self.udpFrames = 0;
|
||||
self.udpPackets = 0;
|
||||
self.udpBytes = 0;
|
||||
self.invalidUDPPackets = 0;
|
||||
self.discoveryRequests = 0;
|
||||
self.colorConnections = 0;
|
||||
self.colorFrames = 0;
|
||||
self.colorBytes = 0;
|
||||
self.lastColorWidth = 0;
|
||||
self.lastColorHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDebugEnabled:(BOOL)enabled {
|
||||
@synchronized (self) {
|
||||
_debugEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteTCPConnection {
|
||||
@synchronized (self) {
|
||||
self.tcpConnections += 1;
|
||||
if (self.debugEnabled) {
|
||||
NSLog(@"IPhoneMCP stream: TCP client connected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteTCPBytes:(NSUInteger)count {
|
||||
@synchronized (self) {
|
||||
self.tcpBytes += count;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteUDPPacketBytes:(NSUInteger)count {
|
||||
@synchronized (self) {
|
||||
self.udpPackets += 1;
|
||||
self.udpBytes += count;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteInvalidUDPPacket {
|
||||
@synchronized (self) {
|
||||
self.invalidUDPPackets += 1;
|
||||
if (self.debugEnabled) {
|
||||
NSLog(@"IPhoneMCP stream: invalid UDP packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteDiscoveryRequest {
|
||||
@synchronized (self) {
|
||||
self.discoveryRequests += 1;
|
||||
if (self.debugEnabled) {
|
||||
NSLog(@"IPhoneMCP stream: discovery request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteFrameForProtocol:(NSString *)protocol {
|
||||
@synchronized (self) {
|
||||
if ([protocol isEqualToString:@"tcp"]) {
|
||||
self.tcpFrames += 1;
|
||||
} else if ([protocol isEqualToString:@"udp"]) {
|
||||
self.udpFrames += 1;
|
||||
}
|
||||
self.lastProtocol = protocol ?: @"none";
|
||||
self.lastFrameAt = [NSDate date];
|
||||
if (self.debugEnabled) {
|
||||
NSLog(@"IPhoneMCP stream: %@ frame displayed", self.lastProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user