1
This commit is contained in:
136
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTChannel.h
generated
Normal file
136
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTChannel.h
generated
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Represents a communication channel between two endpoints talking the same
|
||||
// Lookin_PTProtocol.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <dispatch/dispatch.h>
|
||||
#import <netinet/in.h>
|
||||
#import <sys/socket.h>
|
||||
|
||||
#import "Lookin_PTProtocol.h"
|
||||
#import "Lookin_PTUSBHub.h"
|
||||
|
||||
@class Lookin_PTData, Lookin_PTAddress;
|
||||
@protocol Lookin_PTChannelDelegate;
|
||||
|
||||
@interface Lookin_PTChannel : NSObject
|
||||
|
||||
// Delegate
|
||||
@property (strong) id<Lookin_PTChannelDelegate> delegate;
|
||||
|
||||
// Communication protocol. Must not be nil.
|
||||
@property Lookin_PTProtocol *protocol;
|
||||
|
||||
// YES if this channel is a listening server
|
||||
@property (readonly) BOOL isListening;
|
||||
|
||||
// YES if this channel is a connected peer
|
||||
@property (readonly) BOOL isConnected;
|
||||
|
||||
// Arbitrary attachment. Note that if you set this, the object will grow by
|
||||
// 8 bytes (64 bits).
|
||||
@property (strong) id userInfo;
|
||||
|
||||
@property(nonatomic, assign) int uniqueID;
|
||||
@property(nonatomic, assign) NSInteger targetPort;
|
||||
- (NSString *)debugTag;
|
||||
|
||||
// Create a new channel using the shared Lookin_PTProtocol for the current dispatch
|
||||
// queue, with *delegate*.
|
||||
+ (Lookin_PTChannel*)channelWithDelegate:(id<Lookin_PTChannelDelegate>)delegate;
|
||||
|
||||
|
||||
// Initialize a new frame channel, configuring it to use the calling queue's
|
||||
// protocol instance (as returned by [Lookin_PTProtocol sharedProtocolForQueue:
|
||||
// dispatch_get_current_queue()])
|
||||
- (id)init;
|
||||
|
||||
// Initialize a new frame channel with a specific protocol.
|
||||
- (id)initWithProtocol:(Lookin_PTProtocol*)protocol;
|
||||
|
||||
// Initialize a new frame channel with a specific protocol and delegate.
|
||||
- (id)initWithProtocol:(Lookin_PTProtocol*)protocol delegate:(id<Lookin_PTChannelDelegate>)delegate;
|
||||
|
||||
|
||||
// Connect to a TCP port on a device connected over USB
|
||||
- (void)connectToPort:(int)port overUSBHub:(Lookin_PTUSBHub*)usbHub deviceID:(NSNumber*)deviceID callback:(void(^)(NSError *error))callback;
|
||||
|
||||
// Connect to a TCP port at IPv4 address. Provided port must NOT be in network
|
||||
// byte order. Provided in_addr_t must NOT be in network byte order. A value returned
|
||||
// from inet_aton() will be in network byte order. You can use a value of inet_aton()
|
||||
// as the address parameter here, but you must flip the byte order before passing the
|
||||
// in_addr_t to this function.
|
||||
- (void)connectToPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error, Lookin_PTAddress *address))callback;
|
||||
|
||||
// Listen for connections on port and address, effectively starting a socket
|
||||
// server. Provided port must NOT be in network byte order. Provided in_addr_t
|
||||
// must NOT be in network byte order.
|
||||
// For this to make sense, you should provide a onAccept block handler
|
||||
// or a delegate implementing ioFrameChannel:didAcceptConnection:.
|
||||
- (void)listenOnPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error))callback;
|
||||
|
||||
// Send a frame with an optional payload and optional callback.
|
||||
// If *callback* is not NULL, the block is invoked when either an error occured
|
||||
// or when the frame (and payload, if any) has been completely sent.
|
||||
- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload callback:(void(^)(NSError *error))callback;
|
||||
|
||||
// Lower-level method to assign a connected dispatch IO channel to this channel
|
||||
- (BOOL)startReadingFromConnectedChannel:(dispatch_io_t)channel error:(__autoreleasing NSError**)error;
|
||||
|
||||
// Close the channel, preventing further reading and writing. Any ongoing and
|
||||
// queued reads and writes will be aborted.
|
||||
- (void)close;
|
||||
|
||||
// "graceful" close -- any ongoing and queued reads and writes will complete
|
||||
// before the channel ends.
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// Wraps a mapped dispatch_data_t object. The memory pointed to by *data* is
|
||||
// valid until *dispatchData* is deallocated (normally when the receiver is
|
||||
// deallocated).
|
||||
@interface Lookin_PTData : NSObject
|
||||
@property (readonly) dispatch_data_t dispatchData;
|
||||
@property (readonly) void *data;
|
||||
@property (readonly) size_t length;
|
||||
@end
|
||||
|
||||
|
||||
// Represents a peer's address
|
||||
@interface Lookin_PTAddress : NSObject
|
||||
// For network addresses, this is the IP address in textual format
|
||||
@property (readonly) NSString *name;
|
||||
// For network addresses, this is the port number. Otherwise 0 (zero).
|
||||
@property (readonly) NSInteger port;
|
||||
@end
|
||||
|
||||
|
||||
// Protocol for Lookin_PTChannel delegates
|
||||
@protocol Lookin_PTChannelDelegate <NSObject>
|
||||
|
||||
@required
|
||||
// Invoked when a new frame has arrived on a channel.
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(Lookin_PTData*)payload;
|
||||
|
||||
@optional
|
||||
// Invoked to accept an incoming frame on a channel. Reply NO ignore the
|
||||
// incoming frame. If not implemented by the delegate, all frames are accepted.
|
||||
- (BOOL)ioFrameChannel:(Lookin_PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize;
|
||||
|
||||
// Invoked when the channel closed. If it closed because of an error, *error* is
|
||||
// a non-nil NSError object.
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didEndWithError:(NSError*)error;
|
||||
|
||||
// For listening channels, this method is invoked when a new connection has been
|
||||
// accepted.
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didAcceptConnection:(Lookin_PTChannel*)otherChannel fromAddress:(Lookin_PTAddress*)address;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
675
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTChannel.m
generated
Normal file
675
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTChannel.m
generated
Normal file
@@ -0,0 +1,675 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
#import "Lookin_PTChannel.h"
|
||||
#import "Lookin_PTPrivate.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/un.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <arpa/inet.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Read member of sockaddr_in without knowing the family
|
||||
#define PT_SOCKADDR_ACCESS(ss, member4, member6) \
|
||||
(((ss)->ss_family == AF_INET) ? ( \
|
||||
((const struct sockaddr_in *)(ss))->member4 \
|
||||
) : ( \
|
||||
((const struct sockaddr_in6 *)(ss))->member6 \
|
||||
))
|
||||
|
||||
// Connection state (storage: uint8_t)
|
||||
#define kConnStateNone 0
|
||||
#define kConnStateConnecting 1
|
||||
#define kConnStateConnected 2
|
||||
#define kConnStateListening 3
|
||||
|
||||
// Delegate support optimization (storage: uint8_t)
|
||||
#define kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize 1
|
||||
#define kDelegateFlagImplements_ioFrameChannel_didEndWithError 2
|
||||
#define kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress 4
|
||||
|
||||
|
||||
static int ChannelInstanceCount = 0;
|
||||
static int ChannelUniqueID = 0;
|
||||
|
||||
#pragma mark -
|
||||
// Note: We are careful about the size of this struct as each connected peer
|
||||
// implies one allocation of this struct.
|
||||
@interface Lookin_PTChannel () {
|
||||
dispatch_io_t dispatchObj_channel_;
|
||||
dispatch_source_t dispatchObj_source_;
|
||||
NSError *endError_; // 64 bit
|
||||
@public // here be hacks
|
||||
id<Lookin_PTChannelDelegate> delegate_; // 64 bit
|
||||
uint8_t delegateFlags_; // 8 bit
|
||||
@private
|
||||
uint8_t connState_; // 8 bit
|
||||
//char padding_[6]; // 48 bit -- only if allocation speed is important
|
||||
}
|
||||
- (id)initWithProtocol:(Lookin_PTProtocol*)protocol delegate:(id<Lookin_PTChannelDelegate>)delegate;
|
||||
- (BOOL)acceptIncomingConnection:(dispatch_fd_t)serverSocketFD;
|
||||
@end
|
||||
static const uint8_t kUserInfoKey;
|
||||
|
||||
#pragma mark -
|
||||
@interface Lookin_PTData ()
|
||||
- (id)initWithMappedDispatchData:(dispatch_data_t)mappedContiguousData data:(void*)data length:(size_t)length;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@interface Lookin_PTAddress () {
|
||||
struct sockaddr_storage sockaddr_;
|
||||
}
|
||||
- (id)initWithSockaddr:(const struct sockaddr_storage*)addr;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@implementation Lookin_PTChannel
|
||||
|
||||
@synthesize protocol = protocol_;
|
||||
|
||||
|
||||
+ (Lookin_PTChannel*)channelWithDelegate:(id<Lookin_PTChannelDelegate>)delegate {
|
||||
return [[Lookin_PTChannel alloc] initWithProtocol:[Lookin_PTProtocol sharedProtocolForQueue:dispatch_get_main_queue()] delegate:delegate];
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithProtocol:(Lookin_PTProtocol*)protocol delegate:(id<Lookin_PTChannelDelegate>)delegate {
|
||||
if (!(self = [super init])) return nil;
|
||||
protocol_ = protocol;
|
||||
self.delegate = delegate;
|
||||
|
||||
[self didInit];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithProtocol:(Lookin_PTProtocol*)protocol {
|
||||
if (!(self = [super init])) return nil;
|
||||
protocol_ = protocol;
|
||||
|
||||
[self didInit];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (id)init {
|
||||
[self didInit];
|
||||
|
||||
return [self initWithProtocol:[Lookin_PTProtocol sharedProtocolForQueue:dispatch_get_main_queue()]];
|
||||
}
|
||||
|
||||
- (void)didInit {
|
||||
ChannelUniqueID++;
|
||||
ChannelInstanceCount++;
|
||||
self.uniqueID = ChannelUniqueID;
|
||||
// NSLog(@"LookinServer - Init channel(ID: %@). Total count: %@", @(self.uniqueID), @(ChannelInstanceCount));
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ChannelInstanceCount--;
|
||||
// NSLog(@"LookinServer - Dealloc channel%@. Still lives count: %@", self.debugTag, @(ChannelInstanceCount));
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchObj_channel_) dispatch_release(dispatchObj_channel_);
|
||||
else if (dispatchObj_source_) dispatch_release(dispatchObj_source_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isConnected {
|
||||
return connState_ == kConnStateConnecting || connState_ == kConnStateConnected;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isListening {
|
||||
return connState_ == kConnStateListening;
|
||||
}
|
||||
|
||||
|
||||
- (id)userInfo {
|
||||
return objc_getAssociatedObject(self, (void*)&kUserInfoKey);
|
||||
}
|
||||
|
||||
- (void)setUserInfo:(id)userInfo {
|
||||
objc_setAssociatedObject(self, (const void*)&kUserInfoKey, userInfo, OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
|
||||
|
||||
- (void)setConnState:(char)connState {
|
||||
connState_ = connState;
|
||||
}
|
||||
|
||||
|
||||
- (void)setDispatchChannel:(dispatch_io_t)channel {
|
||||
assert(connState_ == kConnStateConnecting || connState_ == kConnStateConnected || connState_ == kConnStateNone);
|
||||
dispatch_io_t prevChannel = dispatchObj_channel_;
|
||||
if (prevChannel != channel) {
|
||||
dispatchObj_channel_ = channel;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchObj_channel_) dispatch_retain(dispatchObj_channel_);
|
||||
if (prevChannel) dispatch_release(prevChannel);
|
||||
#endif
|
||||
if (!dispatchObj_channel_ && !dispatchObj_source_) {
|
||||
connState_ = kConnStateNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)setDispatchSource:(dispatch_source_t)source {
|
||||
assert(connState_ == kConnStateListening || connState_ == kConnStateNone);
|
||||
dispatch_source_t prevSource = dispatchObj_source_;
|
||||
if (prevSource != source) {
|
||||
dispatchObj_source_ = source;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchObj_source_) dispatch_retain(dispatchObj_source_);
|
||||
if (prevSource) dispatch_release(prevSource);
|
||||
#endif
|
||||
if (!dispatchObj_channel_ && !dispatchObj_source_) {
|
||||
connState_ = kConnStateNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id<Lookin_PTChannelDelegate>)delegate {
|
||||
return delegate_;
|
||||
}
|
||||
|
||||
|
||||
- (void)setDelegate:(id<Lookin_PTChannelDelegate>)delegate {
|
||||
delegate_ = delegate;
|
||||
delegateFlags_ = 0;
|
||||
if (!delegate_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([delegate respondsToSelector:@selector(ioFrameChannel:shouldAcceptFrameOfType:tag:payloadSize:)]) {
|
||||
delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize;
|
||||
}
|
||||
|
||||
if (delegate_ && [delegate respondsToSelector:@selector(ioFrameChannel:didEndWithError:)]) {
|
||||
delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_didEndWithError;
|
||||
}
|
||||
|
||||
if (delegate_ && [delegate respondsToSelector:@selector(ioFrameChannel:didAcceptConnection:fromAddress:)]) {
|
||||
delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)debugTag {
|
||||
NSString *state = @"";
|
||||
if (connState_ == kConnStateNone) {
|
||||
state = @"None";
|
||||
} else if (connState_ == kConnStateConnecting) {
|
||||
state = @"Connecting";
|
||||
} else if (connState_ == kConnStateConnected) {
|
||||
state = @"Connected";
|
||||
} else if (connState_ == kConnStateListening) {
|
||||
state = @"Listening";
|
||||
} else {
|
||||
state = @"Undefined";
|
||||
}
|
||||
return [NSString stringWithFormat:@"[%@-%@,%@]", @(self.uniqueID), @(self.targetPort), state];
|
||||
}
|
||||
|
||||
|
||||
//- (void)setFileDescriptor:(dispatch_fd_t)fd {
|
||||
// [self setDispatchChannel:dispatch_io_create(DISPATCH_IO_STREAM, fd, protocol_.queue, ^(int error) {
|
||||
// close(fd);
|
||||
// })];
|
||||
//}
|
||||
|
||||
|
||||
#pragma mark - Connecting
|
||||
|
||||
|
||||
- (void)connectToPort:(int)port overUSBHub:(Lookin_PTUSBHub*)usbHub deviceID:(NSNumber*)deviceID callback:(void(^)(NSError *error))callback {
|
||||
assert(protocol_ != NULL);
|
||||
if (connState_ != kConnStateNone) {
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
connState_ = kConnStateConnecting;
|
||||
[usbHub connectToDevice:deviceID port:port onStart:^(NSError *err, dispatch_io_t dispatchChannel) {
|
||||
NSError *error = err;
|
||||
if (!error) {
|
||||
[self startReadingFromConnectedChannel:dispatchChannel error:&error];
|
||||
} else {
|
||||
self->connState_ = kConnStateNone;
|
||||
}
|
||||
if (callback) callback(error);
|
||||
} onEnd:^(NSError *error) {
|
||||
if (self->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) {
|
||||
[self->delegate_ ioFrameChannel:self didEndWithError:error];
|
||||
}
|
||||
self->endError_ = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)connectToPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error, Lookin_PTAddress *address))callback {
|
||||
assert(protocol_ != NULL);
|
||||
if (connState_ != kConnStateNone) {
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
connState_ = kConnStateConnecting;
|
||||
|
||||
int error = 0;
|
||||
|
||||
// Create socket
|
||||
dispatch_fd_t fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket(AF_INET, SOCK_STREAM, 0) failed");
|
||||
error = errno;
|
||||
if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect socket
|
||||
struct sockaddr_in addr;
|
||||
bzero((char *)&addr, sizeof(addr));
|
||||
|
||||
addr.sin_len = sizeof(addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
//addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_addr.s_addr = htonl(address);
|
||||
|
||||
// prevent SIGPIPE
|
||||
int on = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
|
||||
|
||||
// int socket, const struct sockaddr *address, socklen_t address_len
|
||||
if (connect(fd, (const struct sockaddr *)&addr, addr.sin_len) == -1) {
|
||||
//perror("connect");
|
||||
error = errno;
|
||||
close(fd);
|
||||
if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// get actual address
|
||||
//if (getsockname(fd, (struct sockaddr*)&addr, (socklen_t*)&addr.sin_len) == -1) {
|
||||
// error = errno;
|
||||
// close(fd);
|
||||
// if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil);
|
||||
// return;
|
||||
//}
|
||||
|
||||
dispatch_io_t dispatchChannel = dispatch_io_create(DISPATCH_IO_STREAM, fd, protocol_.queue, ^(int error) {
|
||||
close(fd);
|
||||
if (self->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) {
|
||||
NSError *err = error == 0 ? self->endError_ : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil];
|
||||
[self->delegate_ ioFrameChannel:self didEndWithError:err];
|
||||
self->endError_ = nil;
|
||||
}
|
||||
});
|
||||
|
||||
if (!dispatchChannel) {
|
||||
close(fd);
|
||||
if (callback) callback([[NSError alloc] initWithDomain:@"PTError" code:0 userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
NSError *err = nil;
|
||||
Lookin_PTAddress *ptAddr = [[Lookin_PTAddress alloc] initWithSockaddr:(struct sockaddr_storage*)&addr];
|
||||
[self startReadingFromConnectedChannel:dispatchChannel error:&err];
|
||||
if (callback) callback(err, ptAddr);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Listening and serving
|
||||
|
||||
|
||||
- (void)listenOnPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error))callback {
|
||||
assert(dispatchObj_source_ == nil);
|
||||
|
||||
// Create socket
|
||||
dispatch_fd_t fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect socket
|
||||
struct sockaddr_in addr;
|
||||
bzero((char *)&addr, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
//addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_addr.s_addr = htonl(address);
|
||||
|
||||
socklen_t socklen = sizeof(addr);
|
||||
|
||||
int on = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
close(fd);
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
close(fd);
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&addr, socklen) != 0) {
|
||||
close(fd);
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(fd, 512) != 0) {
|
||||
close(fd);
|
||||
if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
[self setDispatchSource:dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, protocol_.queue)];
|
||||
|
||||
dispatch_source_set_event_handler(dispatchObj_source_, ^{
|
||||
unsigned long nconns = dispatch_source_get_data(self->dispatchObj_source_);
|
||||
while ([self acceptIncomingConnection:fd] && --nconns);
|
||||
});
|
||||
|
||||
dispatch_source_set_cancel_handler(dispatchObj_source_, ^{
|
||||
// Captures *self*, effectively holding a reference to *self* until cancelled.
|
||||
self->dispatchObj_source_ = nil;
|
||||
close(fd);
|
||||
if (self->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) {
|
||||
[self->delegate_ ioFrameChannel:self didEndWithError:self->endError_];
|
||||
self->endError_ = nil;
|
||||
}
|
||||
});
|
||||
|
||||
dispatch_resume(dispatchObj_source_);
|
||||
//NSLog(@"%@ opened on fd #%d", self, fd);
|
||||
|
||||
connState_ = kConnStateListening;
|
||||
if (callback) callback(nil);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)acceptIncomingConnection:(dispatch_fd_t)serverSocketFD {
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
dispatch_fd_t clientSocketFD = accept(serverSocketFD, (struct sockaddr*)&addr, &addrLen);
|
||||
|
||||
if (clientSocketFD == -1) {
|
||||
perror("accept()");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// prevent SIGPIPE
|
||||
int on = 1;
|
||||
setsockopt(clientSocketFD, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
|
||||
|
||||
if (fcntl(clientSocketFD, F_SETFL, O_NONBLOCK) == -1) {
|
||||
perror("fcntl(.. O_NONBLOCK)");
|
||||
close(clientSocketFD);
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress) {
|
||||
Lookin_PTChannel *peerChannel = [[Lookin_PTChannel alloc] initWithProtocol:protocol_ delegate:delegate_];
|
||||
__block Lookin_PTChannel *localChannelRef = self;
|
||||
dispatch_io_t dispatchChannel = dispatch_io_create(DISPATCH_IO_STREAM, clientSocketFD, protocol_.queue, ^(int error) {
|
||||
// Important note: This block captures *self*, thus a reference is held to
|
||||
// *self* until the fd is truly closed.
|
||||
localChannelRef = nil;
|
||||
|
||||
close(clientSocketFD);
|
||||
|
||||
if (peerChannel->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) {
|
||||
NSError *err = error == 0 ? peerChannel->endError_ : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil];
|
||||
[peerChannel->delegate_ ioFrameChannel:peerChannel didEndWithError:err];
|
||||
peerChannel->endError_ = nil;
|
||||
}
|
||||
});
|
||||
|
||||
[peerChannel setConnState:kConnStateConnected];
|
||||
[peerChannel setDispatchChannel:dispatchChannel];
|
||||
|
||||
assert(((struct sockaddr_storage*)&addr)->ss_len == addrLen);
|
||||
Lookin_PTAddress *address = [[Lookin_PTAddress alloc] initWithSockaddr:(struct sockaddr_storage*)&addr];
|
||||
[delegate_ ioFrameChannel:self didAcceptConnection:peerChannel fromAddress:address];
|
||||
|
||||
NSError *err = nil;
|
||||
if (![peerChannel startReadingFromConnectedChannel:dispatchChannel error:&err]) {
|
||||
// NSLog(@"startReadingFromConnectedChannel failed in accept: %@", err);
|
||||
}
|
||||
} else {
|
||||
close(clientSocketFD);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Closing the channel
|
||||
|
||||
|
||||
- (void)close {
|
||||
// NSLog(@"LookinServer - Will close chanel: %@", self.debugTag);
|
||||
|
||||
if ((connState_ == kConnStateConnecting || connState_ == kConnStateConnected) && dispatchObj_channel_) {
|
||||
dispatch_io_close(dispatchObj_channel_, DISPATCH_IO_STOP);
|
||||
[self setDispatchChannel:NULL];
|
||||
} else if (connState_ == kConnStateListening && dispatchObj_source_) {
|
||||
dispatch_source_cancel(dispatchObj_source_);
|
||||
}
|
||||
}
|
||||
|
||||
/// 曾经连接上 Client,然后 Client 端关闭时,Peertalk 内部会对之前 connect 的 channel 调用该方法
|
||||
- (void)cancel {
|
||||
// NSLog(@"LookinServer - Will cancel chanel: %@", self.debugTag);
|
||||
|
||||
if ((connState_ == kConnStateConnecting || connState_ == kConnStateConnected) && dispatchObj_channel_) {
|
||||
dispatch_io_close(dispatchObj_channel_, 0);
|
||||
[self setDispatchChannel:NULL];
|
||||
} else if (connState_ == kConnStateListening && dispatchObj_source_) {
|
||||
dispatch_source_cancel(dispatchObj_source_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Reading
|
||||
|
||||
|
||||
- (BOOL)startReadingFromConnectedChannel:(dispatch_io_t)channel error:(__autoreleasing NSError**)error {
|
||||
if (connState_ != kConnStateNone && connState_ != kConnStateConnecting && connState_ != kConnStateConnected) {
|
||||
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil];
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (dispatchObj_channel_ != channel) {
|
||||
[self close];
|
||||
[self setDispatchChannel:channel];
|
||||
}
|
||||
|
||||
connState_ = kConnStateConnected;
|
||||
|
||||
// helper
|
||||
BOOL(^handleError)(NSError*,BOOL) = ^BOOL(NSError *error, BOOL isEOS) {
|
||||
if (error) {
|
||||
//NSLog(@"Error while communicating: %@", error);
|
||||
self->endError_ = error;
|
||||
[self close];
|
||||
return YES;
|
||||
} else if (isEOS) {
|
||||
[self cancel];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
};
|
||||
|
||||
[protocol_ readFramesOverChannel:channel onFrame:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize, dispatch_block_t resumeReadingFrames) {
|
||||
if (handleError(error, type == PTFrameTypeEndOfStream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL accepted = (channel == self->dispatchObj_channel_);
|
||||
if (accepted && (self->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize)) {
|
||||
accepted = [self->delegate_ ioFrameChannel:self shouldAcceptFrameOfType:type tag:tag payloadSize:payloadSize];
|
||||
}
|
||||
|
||||
if (payloadSize == 0) {
|
||||
if (accepted && self->delegate_) {
|
||||
[self->delegate_ ioFrameChannel:self didReceiveFrameOfType:type tag:tag payload:nil];
|
||||
} else {
|
||||
// simply ignore the frame
|
||||
}
|
||||
resumeReadingFrames();
|
||||
} else {
|
||||
// has payload
|
||||
if (!accepted) {
|
||||
// Read and discard payload, ignoring frame
|
||||
[self->protocol_ readAndDiscardDataOfSize:payloadSize overChannel:channel callback:^(NSError *error, BOOL endOfStream) {
|
||||
if (!handleError(error, endOfStream)) {
|
||||
resumeReadingFrames();
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self->protocol_ readPayloadOfSize:payloadSize overChannel:channel callback:^(NSError *error, dispatch_data_t contiguousData, const uint8_t *buffer, size_t bufferSize) {
|
||||
if (handleError(error, bufferSize == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->delegate_) {
|
||||
Lookin_PTData *payload = [[Lookin_PTData alloc] initWithMappedDispatchData:contiguousData data:(void*)buffer length:bufferSize];
|
||||
[self->delegate_ ioFrameChannel:self didReceiveFrameOfType:type tag:tag payload:payload];
|
||||
}
|
||||
|
||||
resumeReadingFrames();
|
||||
}];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Sending
|
||||
|
||||
- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload callback:(void(^)(NSError *error))callback {
|
||||
if (connState_ == kConnStateConnecting || connState_ == kConnStateConnected) {
|
||||
[protocol_ sendFrameOfType:frameType tag:tag withPayload:payload overChannel:dispatchObj_channel_ callback:callback];
|
||||
} else if (callback) {
|
||||
callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (NSString*)description {
|
||||
id userInfo = objc_getAssociatedObject(self, (void*)&kUserInfoKey);
|
||||
return [NSString stringWithFormat:@"<Lookin_PTChannel: %p (%@)%s%@>", self, ( connState_ == kConnStateConnecting ? @"connecting"
|
||||
: connState_ == kConnStateConnected ? @"connected"
|
||||
: connState_ == kConnStateListening ? @"listening"
|
||||
: @"closed"),
|
||||
userInfo ? " " : "", userInfo ? userInfo : @""];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
@implementation Lookin_PTAddress
|
||||
|
||||
- (id)initWithSockaddr:(const struct sockaddr_storage*)addr {
|
||||
if (!(self = [super init])) return nil;
|
||||
assert(addr);
|
||||
memcpy((void*)&sockaddr_, (const void*)addr, addr->ss_len);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (NSString*)name {
|
||||
if (sockaddr_.ss_len) {
|
||||
const void *sin_addr = NULL;
|
||||
size_t bufsize = 0;
|
||||
if (sockaddr_.ss_family == AF_INET6) {
|
||||
bufsize = INET6_ADDRSTRLEN;
|
||||
sin_addr = (const void *)&((const struct sockaddr_in6*)&sockaddr_)->sin6_addr;
|
||||
} else {
|
||||
bufsize = INET_ADDRSTRLEN;
|
||||
sin_addr = (const void *)&((const struct sockaddr_in*)&sockaddr_)->sin_addr;
|
||||
}
|
||||
char *buf = CFAllocatorAllocate(kCFAllocatorDefault, bufsize+1, 0);
|
||||
if (inet_ntop(sockaddr_.ss_family, sin_addr, buf, (unsigned int)bufsize-1) == NULL) {
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, buf);
|
||||
return nil;
|
||||
}
|
||||
return [[NSString alloc] initWithBytesNoCopy:(void*)buf length:strlen(buf) encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSInteger)port {
|
||||
if (sockaddr_.ss_len) {
|
||||
return ntohs(PT_SOCKADDR_ACCESS(&sockaddr_, sin_port, sin6_port));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSString*)description {
|
||||
if (sockaddr_.ss_len) {
|
||||
return [NSString stringWithFormat:@"%@:%u", self.name, (unsigned)self.port];
|
||||
} else {
|
||||
return @"(?)";
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
@implementation Lookin_PTData
|
||||
|
||||
@synthesize dispatchData = dispatchData_;
|
||||
@synthesize data = data_;
|
||||
@synthesize length = length_;
|
||||
|
||||
- (id)initWithMappedDispatchData:(dispatch_data_t)mappedContiguousData data:(void*)data length:(size_t)length {
|
||||
if (!(self = [super init])) return nil;
|
||||
dispatchData_ = mappedContiguousData;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchData_) dispatch_retain(dispatchData_);
|
||||
#endif
|
||||
data_ = data;
|
||||
length_ = length;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchData_) dispatch_release(dispatchData_);
|
||||
#endif
|
||||
data_ = NULL;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<Lookin_PTData: %p (%zu bytes)>", self, length_];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
20
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTPrivate.h
generated
Normal file
20
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTPrivate.h
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
|
||||
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && (!defined(__IPHONE_6_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0)) || \
|
||||
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (!defined(__MAC_10_8) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8))
|
||||
#define PT_DISPATCH_RETAIN_RELEASE 1
|
||||
#else
|
||||
#define PT_DISPATCH_RETAIN_RELEASE 0
|
||||
#endif
|
||||
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
#define PT_PRECISE_LIFETIME
|
||||
#define PT_PRECISE_LIFETIME_UNUSED
|
||||
#else
|
||||
#define PT_PRECISE_LIFETIME __attribute__((objc_precise_lifetime))
|
||||
#define PT_PRECISE_LIFETIME_UNUSED __attribute__((objc_precise_lifetime, unused))
|
||||
#endif
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
122
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.h
generated
Normal file
122
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.h
generated
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
|
||||
|
||||
//
|
||||
// A universal frame-based communication protocol which can be used to exchange
|
||||
// arbitrary structured data.
|
||||
//
|
||||
// In short:
|
||||
//
|
||||
// - Each transmission is comprised by one fixed-size frame.
|
||||
// - Each frame contains a protocol version number.
|
||||
// - Each frame contains an application frame type.
|
||||
// - Each frame can contain an identifying tag.
|
||||
// - Each frame can have application-specific data of up to UINT32_MAX size.
|
||||
// - Transactions style messaging can be modeled on top using frame tags.
|
||||
// - Lightweight API on top of libdispatch (aka GCD) -- close to the metal.
|
||||
//
|
||||
#include <dispatch/dispatch.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Special frame tag that signifies "no tag". Your implementation should never
|
||||
// create a reply for a frame with this tag.
|
||||
static const uint32_t PTFrameNoTag = 0;
|
||||
|
||||
// Special frame type that signifies that the stream has ended.
|
||||
static const uint32_t PTFrameTypeEndOfStream = 0;
|
||||
|
||||
// NSError domain
|
||||
FOUNDATION_EXPORT NSString * const Lookin_PTProtocolErrorDomain;
|
||||
|
||||
|
||||
@interface Lookin_PTProtocol : NSObject
|
||||
|
||||
// Queue on which to run data processing blocks.
|
||||
@property dispatch_queue_t queue;
|
||||
|
||||
// Get the shared protocol object for *queue*
|
||||
+ (Lookin_PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue;
|
||||
|
||||
// Initialize a new protocol object to use a specific queue.
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue;
|
||||
|
||||
// Initialize a new protocol object to use the current calling queue.
|
||||
- (id)init;
|
||||
|
||||
#pragma mark Sending frames
|
||||
|
||||
// Generate a new tag that is unique within this protocol object.
|
||||
- (uint32_t)newTag;
|
||||
|
||||
// Send a frame over *channel* with an optional payload and optional callback.
|
||||
// If *callback* is not NULL, the block is invoked when either an error occured
|
||||
// or when the frame (and payload, if any) has been completely sent.
|
||||
- (void)sendFrameOfType:(uint32_t)frameType
|
||||
tag:(uint32_t)tag
|
||||
withPayload:(dispatch_data_t)payload
|
||||
overChannel:(dispatch_io_t)channel
|
||||
callback:(void(^)(NSError *error))callback;
|
||||
|
||||
#pragma mark Receiving frames
|
||||
|
||||
// Read frames over *channel* as they arrive.
|
||||
// The onFrame handler is responsible for reading (or discarding) any payload
|
||||
// and call *resumeReadingFrames* afterwards to resume reading frames.
|
||||
// To stop reading frames, simply do not invoke *resumeReadingFrames*.
|
||||
// When the stream ends, a frame of type PTFrameTypeEndOfStream is received.
|
||||
- (void)readFramesOverChannel:(dispatch_io_t)channel
|
||||
onFrame:(void(^)(NSError *error,
|
||||
uint32_t type,
|
||||
uint32_t tag,
|
||||
uint32_t payloadSize,
|
||||
dispatch_block_t resumeReadingFrames))onFrame;
|
||||
|
||||
// Read a single frame over *channel*. A frame of type PTFrameTypeEndOfStream
|
||||
// denotes the stream has ended.
|
||||
- (void)readFrameOverChannel:(dispatch_io_t)channel
|
||||
callback:(void(^)(NSError *error,
|
||||
uint32_t frameType,
|
||||
uint32_t frameTag,
|
||||
uint32_t payloadSize))callback;
|
||||
|
||||
#pragma mark Receiving frame payloads
|
||||
|
||||
// Read a complete payload. It's the callers responsibility to make sure
|
||||
// payloadSize is not too large since memory will be automatically allocated
|
||||
// where only payloadSize is the limit.
|
||||
// The returned dispatch_data_t object owns *buffer* and thus you need to call
|
||||
// dispatch_retain on *contiguousData* if you plan to keep *buffer* around after
|
||||
// returning from the callback.
|
||||
- (void)readPayloadOfSize:(size_t)payloadSize
|
||||
overChannel:(dispatch_io_t)channel
|
||||
callback:(void(^)(NSError *error,
|
||||
dispatch_data_t contiguousData,
|
||||
const uint8_t *buffer,
|
||||
size_t bufferSize))callback;
|
||||
|
||||
// Discard data of *size* waiting on *channel*. *callback* can be NULL.
|
||||
- (void)readAndDiscardDataOfSize:(size_t)size
|
||||
overChannel:(dispatch_io_t)channel
|
||||
callback:(void(^)(NSError *error, BOOL endOfStream))callback;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSData (Lookin_PTProtocol)
|
||||
// Creates a new dispatch_data_t object which references the receiver and uses
|
||||
// the receivers bytes as its backing data. The returned dispatch_data_t object
|
||||
// holds a reference to the recevier. It's the callers responsibility to call
|
||||
// dispatch_release on the returned object when done.
|
||||
- (dispatch_data_t)createReferencingDispatchData;
|
||||
+ (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data;
|
||||
@end
|
||||
|
||||
@interface NSDictionary (Lookin_PTProtocol)
|
||||
// See description of -[NSData(Lookin_PTProtocol) createReferencingDispatchData]
|
||||
- (dispatch_data_t)createReferencingDispatchData;
|
||||
|
||||
// Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure.
|
||||
+ (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data;
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
428
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.m
generated
Normal file
428
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.m
generated
Normal file
@@ -0,0 +1,428 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
#import "Lookin_PTProtocol.h"
|
||||
#import "Lookin_PTPrivate.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static const uint32_t PTProtocolVersion1 = 1;
|
||||
|
||||
NSString * const Lookin_PTProtocolErrorDomain = @"PTProtocolError";
|
||||
|
||||
// This is what we send as the header for each frame.
|
||||
typedef struct _PTFrame {
|
||||
// The version of the frame and protocol.
|
||||
uint32_t version;
|
||||
|
||||
// Type of frame
|
||||
uint32_t type;
|
||||
|
||||
// Unless zero, a tag is retained in frames that are responses to previous
|
||||
// frames. Applications can use this to build transactions or request-response
|
||||
// logic.
|
||||
uint32_t tag;
|
||||
|
||||
// If payloadSize is larger than zero, *payloadSize* number of bytes are
|
||||
// following, constituting application-specific data.
|
||||
uint32_t payloadSize;
|
||||
|
||||
} PTFrame;
|
||||
|
||||
|
||||
@interface Lookin_PTProtocol () {
|
||||
uint32_t nextFrameTag_;
|
||||
@public
|
||||
dispatch_queue_t queue_;
|
||||
}
|
||||
- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload;
|
||||
@end
|
||||
|
||||
|
||||
static void _release_queue_local_protocol(void *objcobj) {
|
||||
if (objcobj) {
|
||||
Lookin_PTProtocol *protocol = (__bridge_transfer id)objcobj;
|
||||
protocol->queue_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@interface Lookin_RQueueLocalIOFrameProtocol : Lookin_PTProtocol
|
||||
@end
|
||||
@implementation Lookin_RQueueLocalIOFrameProtocol
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation Lookin_PTProtocol
|
||||
|
||||
|
||||
+ (Lookin_PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue {
|
||||
static const char currentQueueFrameProtocolKey;
|
||||
//dispatch_queue_t queue = dispatch_get_current_queue();
|
||||
Lookin_PTProtocol *currentQueueFrameProtocol = (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey);
|
||||
if (!currentQueueFrameProtocol) {
|
||||
currentQueueFrameProtocol = [[Lookin_RQueueLocalIOFrameProtocol alloc] initWithDispatchQueue:NULL];
|
||||
currentQueueFrameProtocol->queue_ = queue; // reference, no retain, since we would create cyclic references
|
||||
dispatch_queue_set_specific(queue, ¤tQueueFrameProtocolKey, (__bridge_retained void*)currentQueueFrameProtocol, &_release_queue_local_protocol);
|
||||
return (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey); // to avoid race conds
|
||||
} else {
|
||||
return currentQueueFrameProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue {
|
||||
if (!(self = [super init])) return nil;
|
||||
queue_ = queue;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (queue_) dispatch_retain(queue_);
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return [self initWithDispatchQueue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (queue_) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(queue_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)queue {
|
||||
return queue_;
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_queue_t prev_queue = queue_;
|
||||
queue_ = queue;
|
||||
if (queue_) dispatch_retain(queue_);
|
||||
if (prev_queue) dispatch_release(prev_queue);
|
||||
#else
|
||||
queue_ = queue;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (uint32_t)newTag {
|
||||
return ++nextFrameTag_;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Creating frames
|
||||
|
||||
|
||||
- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload {
|
||||
PTFrame *frame = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(PTFrame), 0);
|
||||
frame->version = htonl(PTProtocolVersion1);
|
||||
frame->type = htonl(type);
|
||||
frame->tag = htonl(frameTag);
|
||||
|
||||
if (payload) {
|
||||
size_t payloadSize = dispatch_data_get_size(payload);
|
||||
assert(payloadSize <= UINT32_MAX);
|
||||
frame->payloadSize = htonl((uint32_t)payloadSize);
|
||||
} else {
|
||||
frame->payloadSize = 0;
|
||||
}
|
||||
|
||||
dispatch_data_t frameData = dispatch_data_create((const void*)frame, sizeof(PTFrame), queue_, ^{
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, (void*)frame);
|
||||
});
|
||||
|
||||
if (payload && frame->payloadSize != 0) {
|
||||
// chain frame + payload
|
||||
dispatch_data_t data = dispatch_data_create_concat(frameData, payload);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(frameData);
|
||||
#endif
|
||||
frameData = data;
|
||||
}
|
||||
|
||||
return frameData;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Sending frames
|
||||
|
||||
|
||||
- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*))callback {
|
||||
dispatch_data_t frame = [self createDispatchDataWithFrameOfType:frameType frameTag:tag payload:payload];
|
||||
dispatch_io_write(channel, 0, frame, queue_, ^(bool done, dispatch_data_t data, int _errno) {
|
||||
if (done && callback) {
|
||||
callback(_errno == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]);
|
||||
}
|
||||
});
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Receiving frames
|
||||
|
||||
|
||||
- (void)readFrameOverChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, uint32_t frameType, uint32_t frameTag, uint32_t payloadSize))callback {
|
||||
__block dispatch_data_t allData = NULL;
|
||||
|
||||
dispatch_io_read(channel, 0, sizeof(PTFrame), queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
|
||||
size_t dataSize = data ? dispatch_data_get_size(data) : 0;
|
||||
|
||||
if (dataSize) {
|
||||
if (!allData) {
|
||||
allData = data;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(allData);
|
||||
#endif
|
||||
} else {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_data_t allDataPrev = allData;
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
dispatch_release(allDataPrev);
|
||||
#else
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
if (error != 0) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSize == 0) {
|
||||
callback(nil, PTFrameTypeEndOfStream, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allData || dispatch_data_get_size(allData) < sizeof(PTFrame)) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
PTFrame *frame = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(allData, (const void **)&frame, &size); // precise lifetime guarantees bytes in frame will stay valid till the end of scope
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(allData);
|
||||
#endif
|
||||
if (!contiguousData) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
frame->version = ntohl(frame->version);
|
||||
if (frame->version != PTProtocolVersion1) {
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
|
||||
} else {
|
||||
frame->type = ntohl(frame->type);
|
||||
frame->tag = ntohl(frame->tag);
|
||||
frame->payloadSize = ntohl(frame->payloadSize);
|
||||
callback(nil, frame->type, frame->tag, frame->payloadSize);
|
||||
}
|
||||
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readPayloadOfSize:(size_t)payloadSize overChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, dispatch_data_t contiguousData, const uint8_t *buffer, size_t bufferSize))callback {
|
||||
__block dispatch_data_t allData = NULL;
|
||||
dispatch_io_read(channel, 0, payloadSize, queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
|
||||
size_t dataSize = dispatch_data_get_size(data);
|
||||
|
||||
if (dataSize) {
|
||||
if (!allData) {
|
||||
allData = data;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(allData);
|
||||
#endif
|
||||
} else {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_data_t allDataPrev = allData;
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
dispatch_release(allDataPrev);
|
||||
#else
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
if (error != 0) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSize == 0) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback(nil, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = NULL;
|
||||
|
||||
if (allData) {
|
||||
contiguousData = dispatch_data_create_map(allData, (const void **)&buffer, &bufferSize);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(allData); allData = NULL;
|
||||
#endif
|
||||
if (!contiguousData) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback(nil, contiguousData, buffer, bufferSize);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (contiguousData) dispatch_release(contiguousData);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readAndDiscardDataOfSize:(size_t)size overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*, BOOL))callback {
|
||||
dispatch_io_read(channel, 0, size, queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
if (done && callback) {
|
||||
size_t dataSize = data ? dispatch_data_get_size(data) : 0;
|
||||
callback(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], dataSize == 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readFramesOverChannel:(dispatch_io_t)channel onFrame:(void(^)(NSError*, uint32_t, uint32_t, uint32_t, dispatch_block_t))onFrame {
|
||||
[self readFrameOverChannel:channel callback:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize) {
|
||||
onFrame(error, type, tag, payloadSize, ^{
|
||||
if (type != PTFrameTypeEndOfStream) {
|
||||
[self readFramesOverChannel:channel onFrame:onFrame];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface Lookin_PTDispatchData : NSObject {
|
||||
dispatch_data_t dispatchData_;
|
||||
}
|
||||
@end
|
||||
@implementation Lookin_PTDispatchData
|
||||
- (id)initWithDispatchData:(dispatch_data_t)dispatchData {
|
||||
if (!(self = [super init])) return nil;
|
||||
dispatchData_ = dispatchData;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(dispatchData_);
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchData_) dispatch_release(dispatchData_);
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSData (Lookin_PTProtocol)
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-getter-return-value"
|
||||
|
||||
- (dispatch_data_t)createReferencingDispatchData {
|
||||
// Note: The queue is used to submit the destructor. Since we only perform an
|
||||
// atomic release of self, it doesn't really matter which queue is used, thus
|
||||
// we use the current calling queue.
|
||||
return dispatch_data_create((const void*)self.bytes, self.length, dispatch_get_main_queue(), ^{
|
||||
// trick to have the block capture the data, thus retain/releasing
|
||||
[self length];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
+ (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
|
||||
if (!contiguousData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
Lookin_PTDispatchData *dispatchDataRef = [[Lookin_PTDispatchData alloc] initWithDispatchData:contiguousData];
|
||||
NSData *newData = [NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO];
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
static const bool kDispatchDataRefKey;
|
||||
objc_setAssociatedObject(newData, (const void*)kDispatchDataRefKey, dispatchDataRef, OBJC_ASSOCIATION_RETAIN);
|
||||
|
||||
return newData;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSDictionary (Lookin_PTProtocol)
|
||||
|
||||
- (dispatch_data_t)createReferencingDispatchData {
|
||||
NSError *error = nil;
|
||||
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
|
||||
if (!plistData) {
|
||||
NSLog(@"Failed to serialize property list: %@", error);
|
||||
return nil;
|
||||
} else {
|
||||
return [plistData createReferencingDispatchData];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure.
|
||||
+ (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
|
||||
if (!contiguousData) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:nil];
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
88
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTUSBHub.h
generated
Normal file
88
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTUSBHub.h
generated
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Lookin_PTUSBDeviceDidAttachNotification
|
||||
// Posted when a device has been attached. Also posted for each device that is
|
||||
// already attached when the Lookin_PTUSBHub starts listening.
|
||||
//
|
||||
// .userInfo = {
|
||||
// DeviceID = 3;
|
||||
// MessageType = Attached;
|
||||
// Properties = {
|
||||
// ConnectionSpeed = 480000000;
|
||||
// ConnectionType = USB;
|
||||
// DeviceID = 3;
|
||||
// LocationID = 1234567890;
|
||||
// ProductID = 1234;
|
||||
// SerialNumber = 0123456789abcdef0123456789abcdef01234567;
|
||||
// };
|
||||
// }
|
||||
//
|
||||
FOUNDATION_EXPORT NSString * const Lookin_PTUSBDeviceDidAttachNotification;
|
||||
|
||||
// Lookin_PTUSBDeviceDidDetachNotification
|
||||
// Posted when a device has been detached.
|
||||
//
|
||||
// .userInfo = {
|
||||
// DeviceID = 3;
|
||||
// MessageType = Detached;
|
||||
// }
|
||||
//
|
||||
FOUNDATION_EXPORT NSString * const Lookin_PTUSBDeviceDidDetachNotification;
|
||||
|
||||
// NSError domain
|
||||
FOUNDATION_EXPORT NSString * const Lookin_PTUSBHubErrorDomain;
|
||||
|
||||
// Error codes returned with NSError.code for NSError domain Lookin_PTUSBHubErrorDomain
|
||||
typedef enum {
|
||||
PTUSBHubErrorBadDevice = 2,
|
||||
PTUSBHubErrorConnectionRefused = 3,
|
||||
} PTUSBHubError;
|
||||
|
||||
@interface Lookin_PTUSBHub : NSObject
|
||||
|
||||
// Shared, implicitly opened hub.
|
||||
+ (Lookin_PTUSBHub*)sharedHub;
|
||||
|
||||
// Connect to a TCP *port* on a device, while the actual transport is over USB.
|
||||
// Upon success, *error* is nil and *channel* is a duplex I/O channel.
|
||||
// You can retrieve the underlying file descriptor using
|
||||
// dispatch_io_get_descriptor(channel). The dispatch_io_t channel behaves just
|
||||
// like any stream type dispatch_io_t, making it possible to use the same logic
|
||||
// for both USB bridged connections and e.g. ethernet-based connections.
|
||||
//
|
||||
// *onStart* is called either when a connection failed, in which case the error
|
||||
// argument is non-nil, or when the connection was successfully established (the
|
||||
// error argument is nil). Must not be NULL.
|
||||
//
|
||||
// *onEnd* is called when a connection was open and just did close. If the error
|
||||
// argument is non-nil, the channel closed because of an error. Pass NULL for no
|
||||
// callback.
|
||||
//
|
||||
- (void)connectToDevice:(NSNumber*)deviceID
|
||||
port:(int)port
|
||||
onStart:(void(^)(NSError *error, dispatch_io_t channel))onStart
|
||||
onEnd:(void(^)(NSError *error))onEnd;
|
||||
|
||||
// Start listening for devices. You only need to invoke this method on custom
|
||||
// instances to start receiving notifications. The shared instance returned from
|
||||
// +sharedHub is always in listening mode.
|
||||
//
|
||||
// *onStart* is called either when the system failed to start listening, in
|
||||
// which case the error argument is non-nil, or when the receiver is listening.
|
||||
// Pass NULL for no callback.
|
||||
//
|
||||
// *onEnd* is called when listening stopped. If the error argument is non-nil,
|
||||
// listening stopped because of an error. Pass NULL for no callback.
|
||||
//
|
||||
- (void)listenOnQueue:(dispatch_queue_t)queue
|
||||
onStart:(void(^)(NSError*))onStart
|
||||
onEnd:(void(^)(NSError*))onEnd;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
674
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTUSBHub.m
generated
Normal file
674
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTUSBHub.m
generated
Normal file
@@ -0,0 +1,674 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
#import "Lookin_PTUSBHub.h"
|
||||
|
||||
|
||||
|
||||
#import "Lookin_PTPrivate.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/un.h>
|
||||
#include <err.h>
|
||||
|
||||
NSString * const Lookin_PTUSBHubErrorDomain = @"PTUSBHubError";
|
||||
|
||||
typedef uint32_t USBMuxPacketType;
|
||||
enum {
|
||||
USBMuxPacketTypeResult = 1,
|
||||
USBMuxPacketTypeConnect = 2,
|
||||
USBMuxPacketTypeListen = 3,
|
||||
USBMuxPacketTypeDeviceAdd = 4,
|
||||
USBMuxPacketTypeDeviceRemove = 5,
|
||||
// ? = 6,
|
||||
// ? = 7,
|
||||
USBMuxPacketTypePlistPayload = 8,
|
||||
};
|
||||
|
||||
typedef uint32_t USBMuxPacketProtocol;
|
||||
enum {
|
||||
USBMuxPacketProtocolBinary = 0,
|
||||
USBMuxPacketProtocolPlist = 1,
|
||||
};
|
||||
|
||||
typedef uint32_t USBMuxReplyCode;
|
||||
enum {
|
||||
USBMuxReplyCodeOK = 0,
|
||||
USBMuxReplyCodeBadCommand = 1,
|
||||
USBMuxReplyCodeBadDevice = 2,
|
||||
USBMuxReplyCodeConnectionRefused = 3,
|
||||
// ? = 4,
|
||||
// ? = 5,
|
||||
USBMuxReplyCodeBadVersion = 6,
|
||||
};
|
||||
|
||||
|
||||
typedef struct usbmux_packet {
|
||||
uint32_t size;
|
||||
USBMuxPacketProtocol protocol;
|
||||
USBMuxPacketType type;
|
||||
uint32_t tag;
|
||||
char data[0];
|
||||
} __attribute__((__packed__)) usbmux_packet_t;
|
||||
|
||||
static const uint32_t kUsbmuxPacketMaxPayloadSize = UINT32_MAX - (uint32_t)sizeof(usbmux_packet_t);
|
||||
|
||||
|
||||
static uint32_t usbmux_packet_payload_size(usbmux_packet_t *upacket) {
|
||||
return upacket->size - sizeof(usbmux_packet_t);
|
||||
}
|
||||
|
||||
|
||||
static void *usbmux_packet_payload(usbmux_packet_t *upacket) {
|
||||
return (void*)upacket->data;
|
||||
}
|
||||
|
||||
|
||||
static void usbmux_packet_set_payload(usbmux_packet_t *upacket,
|
||||
const void *payload,
|
||||
uint32_t payloadLength)
|
||||
{
|
||||
memcpy(usbmux_packet_payload(upacket), payload, payloadLength);
|
||||
}
|
||||
|
||||
|
||||
static usbmux_packet_t *usbmux_packet_alloc(uint32_t payloadSize) {
|
||||
assert(payloadSize <= kUsbmuxPacketMaxPayloadSize);
|
||||
uint32_t upacketSize = sizeof(usbmux_packet_t) + payloadSize;
|
||||
usbmux_packet_t *upacket = CFAllocatorAllocate(kCFAllocatorDefault, upacketSize, 0);
|
||||
memset(upacket, 0, sizeof(usbmux_packet_t));
|
||||
upacket->size = upacketSize;
|
||||
return upacket;
|
||||
}
|
||||
|
||||
|
||||
static usbmux_packet_t *usbmux_packet_create(USBMuxPacketProtocol protocol,
|
||||
USBMuxPacketType type,
|
||||
uint32_t tag,
|
||||
const void *payload,
|
||||
uint32_t payloadSize)
|
||||
{
|
||||
usbmux_packet_t *upacket = usbmux_packet_alloc(payloadSize);
|
||||
if (!upacket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
upacket->protocol = protocol;
|
||||
upacket->type = type;
|
||||
upacket->tag = tag;
|
||||
|
||||
if (payload && payloadSize) {
|
||||
usbmux_packet_set_payload(upacket, payload, (uint32_t)payloadSize);
|
||||
}
|
||||
|
||||
return upacket;
|
||||
}
|
||||
|
||||
|
||||
static void usbmux_packet_free(usbmux_packet_t *upacket) {
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, upacket);
|
||||
}
|
||||
|
||||
|
||||
NSString * const Lookin_PTUSBDeviceDidAttachNotification = @"Lookin_PTUSBDeviceDidAttachNotification";
|
||||
NSString * const Lookin_PTUSBDeviceDidDetachNotification = @"Lookin_PTUSBDeviceDidDetachNotification";
|
||||
|
||||
static NSString *kPlistPacketTypeListen = @"Listen";
|
||||
static NSString *kPlistPacketTypeConnect = @"Connect";
|
||||
|
||||
|
||||
// Represents a channel of communication between the host process and a remote
|
||||
// (device) system. In practice, a Lookin_PTUSBChannel is connected to a usbmuxd
|
||||
// endpoint which is configured to either listen for device changes (the
|
||||
// PTUSBHub's channel is usually configured as a device notification listener) or
|
||||
// configured as a TCP bridge (e.g. channels returned from PTUSBHub's
|
||||
// connectToDevice:port:callback:). You should not create channels yourself, but
|
||||
// let Lookin_PTUSBHub provide you with already configured channels.
|
||||
@interface Lookin_PTUSBChannel : NSObject {
|
||||
dispatch_io_t channel_;
|
||||
dispatch_queue_t queue_;
|
||||
uint32_t nextPacketTag_;
|
||||
NSMutableDictionary *responseQueue_;
|
||||
BOOL autoReadPackets_;
|
||||
BOOL isReadingPackets_;
|
||||
}
|
||||
|
||||
// The underlying dispatch I/O channel. This is handy if you want to handle your
|
||||
// own I/O logic without Lookin_PTUSBChannel. Remember to dispatch_retain() the channel
|
||||
// if you plan on using it as it might be released from the Lookin_PTUSBChannel at any
|
||||
// point in time.
|
||||
@property (readonly) dispatch_io_t dispatchChannel;
|
||||
|
||||
// The underlying file descriptor.
|
||||
@property (readonly) dispatch_fd_t fileDescriptor;
|
||||
|
||||
// Send data
|
||||
- (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback;
|
||||
- (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback;
|
||||
|
||||
// Read data
|
||||
- (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback;
|
||||
|
||||
// Close the channel, preventing further reads and writes, but letting currently
|
||||
// queued reads and writes finish.
|
||||
- (void)cancel;
|
||||
|
||||
// Close the channel, preventing further reads and writes, immediately
|
||||
// terminating any ongoing reads and writes.
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface Lookin_PTUSBChannel (Private)
|
||||
|
||||
+ (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload;
|
||||
- (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError *error))onEnd;
|
||||
- (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback;
|
||||
- (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error;
|
||||
- (uint32_t)nextPacketTag;
|
||||
- (void)sendPacketOfType:(USBMuxPacketType)type overProtocol:(USBMuxPacketProtocol)protocol tag:(uint32_t)tag payload:(NSData*)payload callback:(void(^)(NSError*))callback;
|
||||
- (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError *error))callback;
|
||||
- (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError *error, NSDictionary *responsePacket))callback;
|
||||
- (void)scheduleReadPacketWithCallback:(void(^)(NSError *error, NSDictionary *packet, uint32_t packetTag))callback;
|
||||
- (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler;
|
||||
- (void)setNeedsReadingPacket;
|
||||
@end
|
||||
|
||||
|
||||
@interface Lookin_PTUSBHub () {
|
||||
Lookin_PTUSBChannel *channel_;
|
||||
}
|
||||
- (void)handleBroadcastPacket:(NSDictionary*)packet;
|
||||
@end
|
||||
|
||||
|
||||
@implementation Lookin_PTUSBHub
|
||||
|
||||
|
||||
+ (Lookin_PTUSBHub*)sharedHub {
|
||||
static Lookin_PTUSBHub *gSharedHub;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
gSharedHub = [Lookin_PTUSBHub new];
|
||||
[gSharedHub listenOnQueue:dispatch_get_main_queue() onStart:^(NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Lookin_PTUSBHub failed to initialize: %@", error);
|
||||
}
|
||||
} onEnd:nil];
|
||||
});
|
||||
return gSharedHub;
|
||||
}
|
||||
|
||||
|
||||
- (id)init {
|
||||
if (!(self = [super init])) return nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)listenOnQueue:(dispatch_queue_t)queue onStart:(void(^)(NSError*))onStart onEnd:(void(^)(NSError*))onEnd {
|
||||
if (channel_) {
|
||||
if (onStart) onStart(nil);
|
||||
return;
|
||||
}
|
||||
channel_ = [Lookin_PTUSBChannel new];
|
||||
NSError *error = nil;
|
||||
if ([channel_ openOnQueue:queue error:&error onEnd:onEnd]) {
|
||||
[channel_ listenWithBroadcastHandler:^(NSDictionary *packet) { [self handleBroadcastPacket:packet]; } callback:onStart];
|
||||
} else if (onStart) {
|
||||
onStart(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)connectToDevice:(NSNumber*)deviceID port:(int)port onStart:(void(^)(NSError*, dispatch_io_t))onStart onEnd:(void(^)(NSError*))onEnd {
|
||||
Lookin_PTUSBChannel *channel = [Lookin_PTUSBChannel new];
|
||||
NSError *error = nil;
|
||||
|
||||
if (![channel openOnQueue:dispatch_get_main_queue() error:&error onEnd:onEnd]) {
|
||||
onStart(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
port = ((port<<8) & 0xFF00) | (port>>8); // limit
|
||||
|
||||
NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeConnect
|
||||
payload:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
deviceID, @"DeviceID",
|
||||
[NSNumber numberWithInt:port], @"PortNumber",
|
||||
nil]];
|
||||
|
||||
[channel sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
|
||||
NSError *error = error_;
|
||||
[channel errorFromPlistResponse:responsePacket error:&error];
|
||||
onStart(error, (error ? nil : channel.dispatchChannel) );
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)handleBroadcastPacket:(NSDictionary*)packet {
|
||||
NSString *messageType = [packet objectForKey:@"MessageType"];
|
||||
|
||||
if ([@"Attached" isEqualToString:messageType]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidAttachNotification object:self userInfo:packet];
|
||||
} else if ([@"Detached" isEqualToString:messageType]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidDetachNotification object:self userInfo:packet];
|
||||
} else {
|
||||
NSLog(@"Warning: Unhandled broadcast message: %@", packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation Lookin_PTUSBChannel
|
||||
|
||||
+ (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload {
|
||||
NSDictionary *packet = nil;
|
||||
|
||||
static NSString *bundleName = nil;
|
||||
static NSString *bundleVersion = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
|
||||
if (infoDict) {
|
||||
bundleName = [infoDict objectForKey:@"CFBundleName"];
|
||||
bundleVersion = [[infoDict objectForKey:@"CFBundleVersion"] description];
|
||||
}
|
||||
});
|
||||
|
||||
if (bundleName) {
|
||||
packet = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
messageType, @"MessageType",
|
||||
bundleName, @"ProgName",
|
||||
bundleVersion, @"ClientVersionString",
|
||||
nil];
|
||||
} else {
|
||||
packet = [NSDictionary dictionaryWithObjectsAndKeys:messageType, @"MessageType", nil];
|
||||
}
|
||||
|
||||
if (payload) {
|
||||
NSMutableDictionary *mpacket = [NSMutableDictionary dictionaryWithDictionary:payload];
|
||||
[mpacket addEntriesFromDictionary:packet];
|
||||
packet = mpacket;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
- (id)init {
|
||||
if (!(self = [super init])) return nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc {
|
||||
//NSLog(@"dealloc %@", self);
|
||||
if (channel_) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(channel_);
|
||||
#endif
|
||||
channel_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)valid {
|
||||
return !!channel_;
|
||||
}
|
||||
|
||||
|
||||
- (dispatch_io_t)dispatchChannel {
|
||||
return channel_;
|
||||
}
|
||||
|
||||
|
||||
- (dispatch_fd_t)fileDescriptor {
|
||||
return dispatch_io_get_descriptor(channel_);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError*))onEnd {
|
||||
assert(queue != nil);
|
||||
assert(channel_ == nil);
|
||||
queue_ = queue;
|
||||
|
||||
// Create socket
|
||||
dispatch_fd_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
||||
return NO;
|
||||
}
|
||||
|
||||
// prevent SIGPIPE
|
||||
int on = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
|
||||
|
||||
// Connect socket
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, "/var/run/usbmuxd");
|
||||
socklen_t socklen = sizeof(addr);
|
||||
if (connect(fd, (struct sockaddr*)&addr, socklen) == -1) {
|
||||
if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
||||
return NO;
|
||||
}
|
||||
|
||||
channel_ = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue_, ^(int error) {
|
||||
close(fd);
|
||||
if (onEnd) {
|
||||
onEnd(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]);
|
||||
}
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback {
|
||||
autoReadPackets_ = YES;
|
||||
[self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
|
||||
|
||||
NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeListen payload:nil];
|
||||
|
||||
[self sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
NSError *error = error_;
|
||||
[self errorFromPlistResponse:responsePacket error:&error];
|
||||
|
||||
callback(error);
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error {
|
||||
if (!*error) {
|
||||
NSNumber *n = [packet objectForKey:@"Number"];
|
||||
|
||||
if (!n) {
|
||||
*error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:(n ? n.integerValue : 0) userInfo:nil];
|
||||
return NO;
|
||||
}
|
||||
|
||||
USBMuxReplyCode replyCode = (USBMuxReplyCode)n.integerValue;
|
||||
if (replyCode != 0) {
|
||||
NSString *errmessage = @"Unspecified error";
|
||||
switch (replyCode) {
|
||||
case USBMuxReplyCodeBadCommand: errmessage = @"illegal command"; break;
|
||||
case USBMuxReplyCodeBadDevice: errmessage = @"unknown device"; break;
|
||||
case USBMuxReplyCodeConnectionRefused: errmessage = @"connection refused"; break;
|
||||
case USBMuxReplyCodeBadVersion: errmessage = @"invalid version"; break;
|
||||
default: break;
|
||||
}
|
||||
*error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:replyCode userInfo:[NSDictionary dictionaryWithObject:errmessage forKey:NSLocalizedDescriptionKey]];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (uint32_t)nextPacketTag {
|
||||
return ++nextPacketTag_;
|
||||
}
|
||||
|
||||
|
||||
- (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError*, NSDictionary*))callback {
|
||||
uint32_t tag = [self nextPacketTag];
|
||||
[self sendPacket:packet tag:tag callback:^(NSError *error) {
|
||||
if (error) {
|
||||
callback(error, nil);
|
||||
return;
|
||||
}
|
||||
// TODO: timeout un-triggered callbacks in responseQueue_
|
||||
if (!self->responseQueue_) self->responseQueue_ = [NSMutableDictionary new];
|
||||
[self->responseQueue_ setObject:callback forKey:[NSNumber numberWithUnsignedInt:tag]];
|
||||
}];
|
||||
|
||||
// We are awaiting a response
|
||||
[self setNeedsReadingPacket];
|
||||
}
|
||||
|
||||
|
||||
- (void)setNeedsReadingPacket {
|
||||
if (!isReadingPackets_) {
|
||||
[self scheduleReadPacketWithBroadcastHandler:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler {
|
||||
assert(isReadingPackets_ == NO);
|
||||
|
||||
[self scheduleReadPacketWithCallback:^(NSError *error, NSDictionary *packet, uint32_t packetTag) {
|
||||
// Interpret the package we just received
|
||||
if (packetTag == 0) {
|
||||
// Broadcast message
|
||||
//NSLog(@"Received broadcast: %@", packet);
|
||||
if (broadcastHandler) broadcastHandler(packet);
|
||||
} else if (self->responseQueue_) {
|
||||
// Reply
|
||||
NSNumber *key = [NSNumber numberWithUnsignedInt:packetTag];
|
||||
void(^requestCallback)(NSError*,NSDictionary*) = [self->responseQueue_ objectForKey:key];
|
||||
if (requestCallback) {
|
||||
[self->responseQueue_ removeObjectForKey:key];
|
||||
requestCallback(error, packet);
|
||||
} else {
|
||||
NSLog(@"Warning: Ignoring reply packet for which there is no registered callback. Packet => %@", packet);
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule reading another incoming package
|
||||
if (self->autoReadPackets_) {
|
||||
[self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)scheduleReadPacketWithCallback:(void(^)(NSError*, NSDictionary*, uint32_t))callback {
|
||||
static usbmux_packet_t ref_upacket;
|
||||
isReadingPackets_ = YES;
|
||||
|
||||
// Read the first `sizeof(ref_upacket.size)` bytes off the channel_
|
||||
dispatch_io_read(channel_, 0, sizeof(ref_upacket.size), queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read 0,4: done=%d data=%p error=%d", done, data, error);
|
||||
|
||||
if (!done)
|
||||
return;
|
||||
|
||||
if (error) {
|
||||
self->isReadingPackets_ = NO;
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read size of incoming usbmux_packet_t
|
||||
uint32_t upacket_len = 0;
|
||||
char *buffer = NULL;
|
||||
size_t buffer_size = 0;
|
||||
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
|
||||
assert(buffer_size == sizeof(ref_upacket.size));
|
||||
assert(sizeof(upacket_len) == sizeof(ref_upacket.size));
|
||||
memcpy((void *)&(upacket_len), (const void *)buffer, buffer_size);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(map_data);
|
||||
#endif
|
||||
|
||||
// Allocate a new usbmux_packet_t for the expected size
|
||||
uint32_t payloadLength = upacket_len - (uint32_t)sizeof(usbmux_packet_t);
|
||||
usbmux_packet_t *upacket = usbmux_packet_alloc(payloadLength);
|
||||
|
||||
// Read rest of the incoming usbmux_packet_t
|
||||
off_t offset = sizeof(ref_upacket.size);
|
||||
dispatch_io_read(self->channel_, offset, (size_t)(upacket->size - offset), self->queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read X,Y: done=%d data=%p error=%d", done, data, error);
|
||||
|
||||
if (!done) {
|
||||
return;
|
||||
}
|
||||
|
||||
self->isReadingPackets_ = NO;
|
||||
|
||||
if (error) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
|
||||
usbmux_packet_free(upacket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (upacket_len > kUsbmuxPacketMaxPayloadSize) {
|
||||
callback(
|
||||
[[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:1 userInfo:@{
|
||||
NSLocalizedDescriptionKey:@"Received a packet that is too large"}],
|
||||
nil,
|
||||
0
|
||||
);
|
||||
usbmux_packet_free(upacket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy read bytes onto our usbmux_packet_t
|
||||
char *buffer = NULL;
|
||||
size_t buffer_size = 0;
|
||||
PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size);
|
||||
assert(buffer_size == upacket->size - offset);
|
||||
memcpy(((void *)(upacket))+offset, (const void *)buffer, buffer_size);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(map_data);
|
||||
#endif
|
||||
|
||||
// We only support plist protocol
|
||||
if (upacket->protocol != USBMuxPacketProtocolPlist) {
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package protocol" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
|
||||
usbmux_packet_free(upacket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only one type of packet in the plist protocol
|
||||
if (upacket->type != USBMuxPacketTypePlistPayload) {
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package type" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
|
||||
usbmux_packet_free(upacket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to decode any payload as plist
|
||||
NSError *err = nil;
|
||||
NSDictionary *dict = nil;
|
||||
if (usbmux_packet_payload_size(upacket)) {
|
||||
dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:usbmux_packet_payload(upacket) length:usbmux_packet_payload_size(upacket) freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:&err];
|
||||
}
|
||||
|
||||
// Invoke callback
|
||||
callback(err, dict, upacket->tag);
|
||||
usbmux_packet_free(upacket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)sendPacketOfType:(USBMuxPacketType)type
|
||||
overProtocol:(USBMuxPacketProtocol)protocol
|
||||
tag:(uint32_t)tag
|
||||
payload:(NSData*)payload
|
||||
callback:(void(^)(NSError*))callback
|
||||
{
|
||||
assert(payload.length <= kUsbmuxPacketMaxPayloadSize);
|
||||
usbmux_packet_t *upacket = usbmux_packet_create(
|
||||
protocol,
|
||||
type,
|
||||
tag,
|
||||
payload ? payload.bytes : nil,
|
||||
(uint32_t)(payload ? payload.length : 0)
|
||||
);
|
||||
dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, queue_, ^{
|
||||
// Free packet when data is freed
|
||||
usbmux_packet_free(upacket);
|
||||
});
|
||||
//NSData *data1 = [NSData dataWithBytesNoCopy:(void*)upacket length:upacket->size freeWhenDone:NO];
|
||||
//[data1 writeToFile:[NSString stringWithFormat:@"/Users/rsms/c-packet-%u.data", tag] atomically:NO];
|
||||
[self sendDispatchData:data callback:callback];
|
||||
}
|
||||
|
||||
|
||||
- (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError*))callback {
|
||||
NSError *error = nil;
|
||||
// NSPropertyListBinaryFormat_v1_0
|
||||
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packet format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
|
||||
if (!plistData) {
|
||||
callback(error);
|
||||
} else {
|
||||
[self sendPacketOfType:USBMuxPacketTypePlistPayload overProtocol:USBMuxPacketProtocolPlist tag:tag payload:plistData callback:callback];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback {
|
||||
off_t offset = 0;
|
||||
dispatch_io_write(channel_, offset, data, queue_, ^(bool done, dispatch_data_t data, int _errno) {
|
||||
//NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, error);
|
||||
if (!done)
|
||||
return;
|
||||
if (callback) {
|
||||
NSError *err = nil;
|
||||
if (_errno) err = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(data); // Release our ref. A ref is still held by dispatch_io_write
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-getter-return-value"
|
||||
|
||||
- (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback {
|
||||
dispatch_data_t ddata = dispatch_data_create((const void*)data.bytes, data.length, queue_, ^{
|
||||
// trick to have the block capture and retain the data
|
||||
[data length];
|
||||
});
|
||||
[self sendDispatchData:ddata callback:callback];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback {
|
||||
dispatch_io_read(channel_, offset, length, queue_, ^(bool done, dispatch_data_t data, int _errno) {
|
||||
if (!done)
|
||||
return;
|
||||
|
||||
NSError *error = nil;
|
||||
if (_errno != 0) {
|
||||
error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
|
||||
}
|
||||
|
||||
callback(error, data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)cancel {
|
||||
if (channel_) {
|
||||
dispatch_io_close(channel_, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)stop {
|
||||
if (channel_) {
|
||||
dispatch_io_close(channel_, DISPATCH_IO_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
28
Pods/LookinServer/Src/Main/Shared/Peertalk/Peertalk.h
generated
Normal file
28
Pods/LookinServer/Src/Main/Shared/Peertalk/Peertalk.h
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Peertalk.h
|
||||
// Peertalk
|
||||
//
|
||||
// Created by Marek Cirkos on 12/04/2016.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for Peertalk.
|
||||
FOUNDATION_EXPORT double PeertalkVersionNumber;
|
||||
|
||||
//! Project version string for Peertalk.
|
||||
FOUNDATION_EXPORT const unsigned char PeertalkVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Peertalk/PublicHeader.h>
|
||||
|
||||
|
||||
#import "Lookin_PTChannel.h"
|
||||
#import "Lookin_PTProtocol.h"
|
||||
#import "Lookin_PTUSBHub.h"
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
Reference in New Issue
Block a user