Lookin_PTUSBHub.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. #import "Lookin_PTUSBHub.h"
  3. #import "Lookin_PTPrivate.h"
  4. #include <netinet/in.h>
  5. #include <sys/socket.h>
  6. #include <sys/ioctl.h>
  7. #include <sys/un.h>
  8. #include <err.h>
  9. NSString * const Lookin_PTUSBHubErrorDomain = @"PTUSBHubError";
  10. typedef uint32_t USBMuxPacketType;
  11. enum {
  12. USBMuxPacketTypeResult = 1,
  13. USBMuxPacketTypeConnect = 2,
  14. USBMuxPacketTypeListen = 3,
  15. USBMuxPacketTypeDeviceAdd = 4,
  16. USBMuxPacketTypeDeviceRemove = 5,
  17. // ? = 6,
  18. // ? = 7,
  19. USBMuxPacketTypePlistPayload = 8,
  20. };
  21. typedef uint32_t USBMuxPacketProtocol;
  22. enum {
  23. USBMuxPacketProtocolBinary = 0,
  24. USBMuxPacketProtocolPlist = 1,
  25. };
  26. typedef uint32_t USBMuxReplyCode;
  27. enum {
  28. USBMuxReplyCodeOK = 0,
  29. USBMuxReplyCodeBadCommand = 1,
  30. USBMuxReplyCodeBadDevice = 2,
  31. USBMuxReplyCodeConnectionRefused = 3,
  32. // ? = 4,
  33. // ? = 5,
  34. USBMuxReplyCodeBadVersion = 6,
  35. };
  36. typedef struct usbmux_packet {
  37. uint32_t size;
  38. USBMuxPacketProtocol protocol;
  39. USBMuxPacketType type;
  40. uint32_t tag;
  41. char data[0];
  42. } __attribute__((__packed__)) usbmux_packet_t;
  43. static const uint32_t kUsbmuxPacketMaxPayloadSize = UINT32_MAX - (uint32_t)sizeof(usbmux_packet_t);
  44. static uint32_t usbmux_packet_payload_size(usbmux_packet_t *upacket) {
  45. return upacket->size - sizeof(usbmux_packet_t);
  46. }
  47. static void *usbmux_packet_payload(usbmux_packet_t *upacket) {
  48. return (void*)upacket->data;
  49. }
  50. static void usbmux_packet_set_payload(usbmux_packet_t *upacket,
  51. const void *payload,
  52. uint32_t payloadLength)
  53. {
  54. memcpy(usbmux_packet_payload(upacket), payload, payloadLength);
  55. }
  56. static usbmux_packet_t *usbmux_packet_alloc(uint32_t payloadSize) {
  57. assert(payloadSize <= kUsbmuxPacketMaxPayloadSize);
  58. uint32_t upacketSize = sizeof(usbmux_packet_t) + payloadSize;
  59. usbmux_packet_t *upacket = CFAllocatorAllocate(kCFAllocatorDefault, upacketSize, 0);
  60. memset(upacket, 0, sizeof(usbmux_packet_t));
  61. upacket->size = upacketSize;
  62. return upacket;
  63. }
  64. static usbmux_packet_t *usbmux_packet_create(USBMuxPacketProtocol protocol,
  65. USBMuxPacketType type,
  66. uint32_t tag,
  67. const void *payload,
  68. uint32_t payloadSize)
  69. {
  70. usbmux_packet_t *upacket = usbmux_packet_alloc(payloadSize);
  71. if (!upacket) {
  72. return NULL;
  73. }
  74. upacket->protocol = protocol;
  75. upacket->type = type;
  76. upacket->tag = tag;
  77. if (payload && payloadSize) {
  78. usbmux_packet_set_payload(upacket, payload, (uint32_t)payloadSize);
  79. }
  80. return upacket;
  81. }
  82. static void usbmux_packet_free(usbmux_packet_t *upacket) {
  83. CFAllocatorDeallocate(kCFAllocatorDefault, upacket);
  84. }
  85. NSString * const Lookin_PTUSBDeviceDidAttachNotification = @"Lookin_PTUSBDeviceDidAttachNotification";
  86. NSString * const Lookin_PTUSBDeviceDidDetachNotification = @"Lookin_PTUSBDeviceDidDetachNotification";
  87. static NSString *kPlistPacketTypeListen = @"Listen";
  88. static NSString *kPlistPacketTypeConnect = @"Connect";
  89. // Represents a channel of communication between the host process and a remote
  90. // (device) system. In practice, a Lookin_PTUSBChannel is connected to a usbmuxd
  91. // endpoint which is configured to either listen for device changes (the
  92. // PTUSBHub's channel is usually configured as a device notification listener) or
  93. // configured as a TCP bridge (e.g. channels returned from PTUSBHub's
  94. // connectToDevice:port:callback:). You should not create channels yourself, but
  95. // let Lookin_PTUSBHub provide you with already configured channels.
  96. @interface Lookin_PTUSBChannel : NSObject {
  97. dispatch_io_t channel_;
  98. dispatch_queue_t queue_;
  99. uint32_t nextPacketTag_;
  100. NSMutableDictionary *responseQueue_;
  101. BOOL autoReadPackets_;
  102. BOOL isReadingPackets_;
  103. }
  104. // The underlying dispatch I/O channel. This is handy if you want to handle your
  105. // own I/O logic without Lookin_PTUSBChannel. Remember to dispatch_retain() the channel
  106. // if you plan on using it as it might be released from the Lookin_PTUSBChannel at any
  107. // point in time.
  108. @property (readonly) dispatch_io_t dispatchChannel;
  109. // The underlying file descriptor.
  110. @property (readonly) dispatch_fd_t fileDescriptor;
  111. // Send data
  112. - (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback;
  113. - (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback;
  114. // Read data
  115. - (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback;
  116. // Close the channel, preventing further reads and writes, but letting currently
  117. // queued reads and writes finish.
  118. - (void)cancel;
  119. // Close the channel, preventing further reads and writes, immediately
  120. // terminating any ongoing reads and writes.
  121. - (void)stop;
  122. @end
  123. @interface Lookin_PTUSBChannel (Private)
  124. + (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload;
  125. - (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError *error))onEnd;
  126. - (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback;
  127. - (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error;
  128. - (uint32_t)nextPacketTag;
  129. - (void)sendPacketOfType:(USBMuxPacketType)type overProtocol:(USBMuxPacketProtocol)protocol tag:(uint32_t)tag payload:(NSData*)payload callback:(void(^)(NSError*))callback;
  130. - (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError *error))callback;
  131. - (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError *error, NSDictionary *responsePacket))callback;
  132. - (void)scheduleReadPacketWithCallback:(void(^)(NSError *error, NSDictionary *packet, uint32_t packetTag))callback;
  133. - (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler;
  134. - (void)setNeedsReadingPacket;
  135. @end
  136. @interface Lookin_PTUSBHub () {
  137. Lookin_PTUSBChannel *channel_;
  138. }
  139. - (void)handleBroadcastPacket:(NSDictionary*)packet;
  140. @end
  141. @implementation Lookin_PTUSBHub
  142. + (Lookin_PTUSBHub*)sharedHub {
  143. static Lookin_PTUSBHub *gSharedHub;
  144. static dispatch_once_t onceToken;
  145. dispatch_once(&onceToken, ^{
  146. gSharedHub = [Lookin_PTUSBHub new];
  147. [gSharedHub listenOnQueue:dispatch_get_main_queue() onStart:^(NSError *error) {
  148. if (error) {
  149. NSLog(@"Lookin_PTUSBHub failed to initialize: %@", error);
  150. }
  151. } onEnd:nil];
  152. });
  153. return gSharedHub;
  154. }
  155. - (id)init {
  156. if (!(self = [super init])) return nil;
  157. return self;
  158. }
  159. - (void)listenOnQueue:(dispatch_queue_t)queue onStart:(void(^)(NSError*))onStart onEnd:(void(^)(NSError*))onEnd {
  160. if (channel_) {
  161. if (onStart) onStart(nil);
  162. return;
  163. }
  164. channel_ = [Lookin_PTUSBChannel new];
  165. NSError *error = nil;
  166. if ([channel_ openOnQueue:queue error:&error onEnd:onEnd]) {
  167. [channel_ listenWithBroadcastHandler:^(NSDictionary *packet) { [self handleBroadcastPacket:packet]; } callback:onStart];
  168. } else if (onStart) {
  169. onStart(error);
  170. }
  171. }
  172. - (void)connectToDevice:(NSNumber*)deviceID port:(int)port onStart:(void(^)(NSError*, dispatch_io_t))onStart onEnd:(void(^)(NSError*))onEnd {
  173. Lookin_PTUSBChannel *channel = [Lookin_PTUSBChannel new];
  174. NSError *error = nil;
  175. if (![channel openOnQueue:dispatch_get_main_queue() error:&error onEnd:onEnd]) {
  176. onStart(error, nil);
  177. return;
  178. }
  179. port = ((port<<8) & 0xFF00) | (port>>8); // limit
  180. NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeConnect
  181. payload:[NSDictionary dictionaryWithObjectsAndKeys:
  182. deviceID, @"DeviceID",
  183. [NSNumber numberWithInt:port], @"PortNumber",
  184. nil]];
  185. [channel sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
  186. NSError *error = error_;
  187. [channel errorFromPlistResponse:responsePacket error:&error];
  188. onStart(error, (error ? nil : channel.dispatchChannel) );
  189. }];
  190. }
  191. - (void)handleBroadcastPacket:(NSDictionary*)packet {
  192. NSString *messageType = [packet objectForKey:@"MessageType"];
  193. if ([@"Attached" isEqualToString:messageType]) {
  194. [[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidAttachNotification object:self userInfo:packet];
  195. } else if ([@"Detached" isEqualToString:messageType]) {
  196. [[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidDetachNotification object:self userInfo:packet];
  197. } else {
  198. NSLog(@"Warning: Unhandled broadcast message: %@", packet);
  199. }
  200. }
  201. @end
  202. #pragma mark -
  203. @implementation Lookin_PTUSBChannel
  204. + (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload {
  205. NSDictionary *packet = nil;
  206. static NSString *bundleName = nil;
  207. static NSString *bundleVersion = nil;
  208. static dispatch_once_t onceToken;
  209. dispatch_once(&onceToken, ^{
  210. NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
  211. if (infoDict) {
  212. bundleName = [infoDict objectForKey:@"CFBundleName"];
  213. bundleVersion = [[infoDict objectForKey:@"CFBundleVersion"] description];
  214. }
  215. });
  216. if (bundleName) {
  217. packet = [NSDictionary dictionaryWithObjectsAndKeys:
  218. messageType, @"MessageType",
  219. bundleName, @"ProgName",
  220. bundleVersion, @"ClientVersionString",
  221. nil];
  222. } else {
  223. packet = [NSDictionary dictionaryWithObjectsAndKeys:messageType, @"MessageType", nil];
  224. }
  225. if (payload) {
  226. NSMutableDictionary *mpacket = [NSMutableDictionary dictionaryWithDictionary:payload];
  227. [mpacket addEntriesFromDictionary:packet];
  228. packet = mpacket;
  229. }
  230. return packet;
  231. }
  232. - (id)init {
  233. if (!(self = [super init])) return nil;
  234. return self;
  235. }
  236. - (void)dealloc {
  237. //NSLog(@"dealloc %@", self);
  238. if (channel_) {
  239. #if PT_DISPATCH_RETAIN_RELEASE
  240. dispatch_release(channel_);
  241. #endif
  242. channel_ = nil;
  243. }
  244. }
  245. - (BOOL)valid {
  246. return !!channel_;
  247. }
  248. - (dispatch_io_t)dispatchChannel {
  249. return channel_;
  250. }
  251. - (dispatch_fd_t)fileDescriptor {
  252. return dispatch_io_get_descriptor(channel_);
  253. }
  254. - (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError*))onEnd {
  255. assert(queue != nil);
  256. assert(channel_ == nil);
  257. queue_ = queue;
  258. // Create socket
  259. dispatch_fd_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
  260. if (fd == -1) {
  261. if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  262. return NO;
  263. }
  264. // prevent SIGPIPE
  265. int on = 1;
  266. setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
  267. // Connect socket
  268. struct sockaddr_un addr;
  269. addr.sun_family = AF_UNIX;
  270. strcpy(addr.sun_path, "/var/run/usbmuxd");
  271. socklen_t socklen = sizeof(addr);
  272. if (connect(fd, (struct sockaddr*)&addr, socklen) == -1) {
  273. if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  274. return NO;
  275. }
  276. channel_ = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue_, ^(int error) {
  277. close(fd);
  278. if (onEnd) {
  279. onEnd(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]);
  280. }
  281. });
  282. return YES;
  283. }
  284. - (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback {
  285. autoReadPackets_ = YES;
  286. [self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
  287. NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeListen payload:nil];
  288. [self sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
  289. if (!callback)
  290. return;
  291. NSError *error = error_;
  292. [self errorFromPlistResponse:responsePacket error:&error];
  293. callback(error);
  294. }];
  295. }
  296. - (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error {
  297. if (!*error) {
  298. NSNumber *n = [packet objectForKey:@"Number"];
  299. if (!n) {
  300. *error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:(n ? n.integerValue : 0) userInfo:nil];
  301. return NO;
  302. }
  303. USBMuxReplyCode replyCode = (USBMuxReplyCode)n.integerValue;
  304. if (replyCode != 0) {
  305. NSString *errmessage = @"Unspecified error";
  306. switch (replyCode) {
  307. case USBMuxReplyCodeBadCommand: errmessage = @"illegal command"; break;
  308. case USBMuxReplyCodeBadDevice: errmessage = @"unknown device"; break;
  309. case USBMuxReplyCodeConnectionRefused: errmessage = @"connection refused"; break;
  310. case USBMuxReplyCodeBadVersion: errmessage = @"invalid version"; break;
  311. default: break;
  312. }
  313. *error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:replyCode userInfo:[NSDictionary dictionaryWithObject:errmessage forKey:NSLocalizedDescriptionKey]];
  314. return NO;
  315. }
  316. }
  317. return YES;
  318. }
  319. - (uint32_t)nextPacketTag {
  320. return ++nextPacketTag_;
  321. }
  322. - (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError*, NSDictionary*))callback {
  323. uint32_t tag = [self nextPacketTag];
  324. [self sendPacket:packet tag:tag callback:^(NSError *error) {
  325. if (error) {
  326. callback(error, nil);
  327. return;
  328. }
  329. // TODO: timeout un-triggered callbacks in responseQueue_
  330. if (!self->responseQueue_) self->responseQueue_ = [NSMutableDictionary new];
  331. [self->responseQueue_ setObject:callback forKey:[NSNumber numberWithUnsignedInt:tag]];
  332. }];
  333. // We are awaiting a response
  334. [self setNeedsReadingPacket];
  335. }
  336. - (void)setNeedsReadingPacket {
  337. if (!isReadingPackets_) {
  338. [self scheduleReadPacketWithBroadcastHandler:nil];
  339. }
  340. }
  341. - (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler {
  342. assert(isReadingPackets_ == NO);
  343. [self scheduleReadPacketWithCallback:^(NSError *error, NSDictionary *packet, uint32_t packetTag) {
  344. // Interpret the package we just received
  345. if (packetTag == 0) {
  346. // Broadcast message
  347. //NSLog(@"Received broadcast: %@", packet);
  348. if (broadcastHandler) broadcastHandler(packet);
  349. } else if (self->responseQueue_) {
  350. // Reply
  351. NSNumber *key = [NSNumber numberWithUnsignedInt:packetTag];
  352. void(^requestCallback)(NSError*,NSDictionary*) = [self->responseQueue_ objectForKey:key];
  353. if (requestCallback) {
  354. [self->responseQueue_ removeObjectForKey:key];
  355. requestCallback(error, packet);
  356. } else {
  357. NSLog(@"Warning: Ignoring reply packet for which there is no registered callback. Packet => %@", packet);
  358. }
  359. }
  360. // Schedule reading another incoming package
  361. if (self->autoReadPackets_) {
  362. [self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
  363. }
  364. }];
  365. }
  366. - (void)scheduleReadPacketWithCallback:(void(^)(NSError*, NSDictionary*, uint32_t))callback {
  367. static usbmux_packet_t ref_upacket;
  368. isReadingPackets_ = YES;
  369. // Read the first `sizeof(ref_upacket.size)` bytes off the channel_
  370. dispatch_io_read(channel_, 0, sizeof(ref_upacket.size), queue_, ^(bool done, dispatch_data_t data, int error) {
  371. //NSLog(@"dispatch_io_read 0,4: done=%d data=%p error=%d", done, data, error);
  372. if (!done)
  373. return;
  374. if (error) {
  375. self->isReadingPackets_ = NO;
  376. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
  377. return;
  378. }
  379. // Read size of incoming usbmux_packet_t
  380. uint32_t upacket_len = 0;
  381. char *buffer = NULL;
  382. size_t buffer_size = 0;
  383. PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size); // objc_precise_lifetime guarantees 'map_data' isn't released before memcpy has a chance to do its thing
  384. assert(buffer_size == sizeof(ref_upacket.size));
  385. assert(sizeof(upacket_len) == sizeof(ref_upacket.size));
  386. memcpy((void *)&(upacket_len), (const void *)buffer, buffer_size);
  387. #if PT_DISPATCH_RETAIN_RELEASE
  388. dispatch_release(map_data);
  389. #endif
  390. // Allocate a new usbmux_packet_t for the expected size
  391. uint32_t payloadLength = upacket_len - (uint32_t)sizeof(usbmux_packet_t);
  392. usbmux_packet_t *upacket = usbmux_packet_alloc(payloadLength);
  393. // Read rest of the incoming usbmux_packet_t
  394. off_t offset = sizeof(ref_upacket.size);
  395. dispatch_io_read(self->channel_, offset, (size_t)(upacket->size - offset), self->queue_, ^(bool done, dispatch_data_t data, int error) {
  396. //NSLog(@"dispatch_io_read X,Y: done=%d data=%p error=%d", done, data, error);
  397. if (!done) {
  398. return;
  399. }
  400. self->isReadingPackets_ = NO;
  401. if (error) {
  402. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
  403. usbmux_packet_free(upacket);
  404. return;
  405. }
  406. if (upacket_len > kUsbmuxPacketMaxPayloadSize) {
  407. callback(
  408. [[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:1 userInfo:@{
  409. NSLocalizedDescriptionKey:@"Received a packet that is too large"}],
  410. nil,
  411. 0
  412. );
  413. usbmux_packet_free(upacket);
  414. return;
  415. }
  416. // Copy read bytes onto our usbmux_packet_t
  417. char *buffer = NULL;
  418. size_t buffer_size = 0;
  419. PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size);
  420. assert(buffer_size == upacket->size - offset);
  421. memcpy(((void *)(upacket))+offset, (const void *)buffer, buffer_size);
  422. #if PT_DISPATCH_RETAIN_RELEASE
  423. dispatch_release(map_data);
  424. #endif
  425. // We only support plist protocol
  426. if (upacket->protocol != USBMuxPacketProtocolPlist) {
  427. callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package protocol" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
  428. usbmux_packet_free(upacket);
  429. return;
  430. }
  431. // Only one type of packet in the plist protocol
  432. if (upacket->type != USBMuxPacketTypePlistPayload) {
  433. callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package type" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
  434. usbmux_packet_free(upacket);
  435. return;
  436. }
  437. // Try to decode any payload as plist
  438. NSError *err = nil;
  439. NSDictionary *dict = nil;
  440. if (usbmux_packet_payload_size(upacket)) {
  441. dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:usbmux_packet_payload(upacket) length:usbmux_packet_payload_size(upacket) freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:&err];
  442. }
  443. // Invoke callback
  444. callback(err, dict, upacket->tag);
  445. usbmux_packet_free(upacket);
  446. });
  447. });
  448. }
  449. - (void)sendPacketOfType:(USBMuxPacketType)type
  450. overProtocol:(USBMuxPacketProtocol)protocol
  451. tag:(uint32_t)tag
  452. payload:(NSData*)payload
  453. callback:(void(^)(NSError*))callback
  454. {
  455. assert(payload.length <= kUsbmuxPacketMaxPayloadSize);
  456. usbmux_packet_t *upacket = usbmux_packet_create(
  457. protocol,
  458. type,
  459. tag,
  460. payload ? payload.bytes : nil,
  461. (uint32_t)(payload ? payload.length : 0)
  462. );
  463. dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, queue_, ^{
  464. // Free packet when data is freed
  465. usbmux_packet_free(upacket);
  466. });
  467. //NSData *data1 = [NSData dataWithBytesNoCopy:(void*)upacket length:upacket->size freeWhenDone:NO];
  468. //[data1 writeToFile:[NSString stringWithFormat:@"/Users/rsms/c-packet-%u.data", tag] atomically:NO];
  469. [self sendDispatchData:data callback:callback];
  470. }
  471. - (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError*))callback {
  472. NSError *error = nil;
  473. // NSPropertyListBinaryFormat_v1_0
  474. NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packet format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
  475. if (!plistData) {
  476. callback(error);
  477. } else {
  478. [self sendPacketOfType:USBMuxPacketTypePlistPayload overProtocol:USBMuxPacketProtocolPlist tag:tag payload:plistData callback:callback];
  479. }
  480. }
  481. - (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback {
  482. off_t offset = 0;
  483. dispatch_io_write(channel_, offset, data, queue_, ^(bool done, dispatch_data_t data, int _errno) {
  484. //NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, error);
  485. if (!done)
  486. return;
  487. if (callback) {
  488. NSError *err = nil;
  489. if (_errno) err = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
  490. callback(err);
  491. }
  492. });
  493. #if PT_DISPATCH_RETAIN_RELEASE
  494. dispatch_release(data); // Release our ref. A ref is still held by dispatch_io_write
  495. #endif
  496. }
  497. #pragma clang diagnostic push
  498. #pragma clang diagnostic ignored "-Wunused-getter-return-value"
  499. - (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback {
  500. dispatch_data_t ddata = dispatch_data_create((const void*)data.bytes, data.length, queue_, ^{
  501. // trick to have the block capture and retain the data
  502. [data length];
  503. });
  504. [self sendDispatchData:ddata callback:callback];
  505. }
  506. #pragma clang diagnostic pop
  507. - (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback {
  508. dispatch_io_read(channel_, offset, length, queue_, ^(bool done, dispatch_data_t data, int _errno) {
  509. if (!done)
  510. return;
  511. NSError *error = nil;
  512. if (_errno != 0) {
  513. error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
  514. }
  515. callback(error, data);
  516. });
  517. }
  518. - (void)cancel {
  519. if (channel_) {
  520. dispatch_io_close(channel_, 0);
  521. }
  522. }
  523. - (void)stop {
  524. if (channel_) {
  525. dispatch_io_close(channel_, DISPATCH_IO_STOP);
  526. }
  527. }
  528. @end
  529. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */