NSStream with SSL - Cocoa Echo Server/Client - CFNetwork SSLHandshake failed (-9800) - ios

I'm trying to configure and iOS device to connect to a macOS device over a WiFi network using Bonjour and NSStreams. I'd like to secure the connection via SSL. I started from Apple's Cocoa Echo example code here. It works beautifully without SSL, but when I try to configure a secure connection, I'm getting CFNetwork SSLHandshake failed (-9800) on both the client and server side. Here's my configuration.
macOS (server)
// establish connection
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
SQConnection * connection = [[SQConnection alloc] initWithInputStream:(__bridge NSInputStream *)readStream outputStream:(__bridge NSOutputStream *)writeStream];
[self.connections addObject:connection];
[connection open];
[(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(SQConnectionDidCloseNotification:) name:SQConnectionDidCloseNotification object:connection];
NSLog(#"Added connection.");
}
// open streams (in SQConnection.open)
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
// SSL
[self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
#(FALSE), kCFStreamSSLValidatesCertificateChain,
kCFNull, kCFStreamSSLPeerName,
nil];
CFReadStreamSetProperty((CFReadStreamRef)self.inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)self.outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
iOS (client)
NSInputStream * istream;
NSOutputStream * ostream;
if ([netService qNetworkAdditions_getInputStream:&istream outputStream:&ostream]) {
NSLog(#"OPEN STREAMS");
self.inputStream = istream;
self.outputStream = ostream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
}
My best guess is that I haven't actually configured a certificate to be used for SSL negotiation. This is something I was hoping macOS would take care of automatically, but Apple's docs are woefully incomplete on this. The best I've found is a couple paragraphs here on "Securing and Configuring the Connection."

Related

iOS: App is hanging when trying to close the stream

I created NSStreams to transfer something using the below code:
dispatch_async(dispatch_get_main_queue(), ^{
// open input
[self.inputStream setDelegate:controller];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
// open output
[self.outputStream setDelegate:controller];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream open];
});
At some point, I want to close the streams using the below code:
// Shut down input
[self.inputStream close];
self.inputStream = nil;
// Shut down output
[self.outputStream close];
self.outputStream = nil;
NSLog(#"input streams status:%i", [self.inputStream streamStatus]);
NSLog(#"output streams status:%i", [self.outputStream streamStatus]);
NSLog(#"input streams status:%#", [self.inputStream streamError]);
NSLog(#"output streams status:%#", [self.outputStream streamError]);
All input/output streams are strong properties.
Stream status return zero, but my app hangs. When I put device in debug mode, the monitor shows that my device CPU start running in 98+% after close method called. It seems like it's waiting for something to finish, but I still don't know what that is.
Does anyone know what may cause this issues?
Try removing the streams from the run loop.
// Shut down input
[self.inputStream close];
[self.inputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
self.inputStream = nil;
// Shut down output
[self.outputStream close];
[self.outputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
self.outputStream = nil;
More information can be found here.

call function on background thread

I am newbie in ios development, I am doing an application using tcp socket, and followed several tutorials and everything related to the connection works great. but the ui freezes until my authentication is completed. for this use "dispatch_queue" follows
-(BOOL)Connect {
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^ {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)IP,PORT, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream =(__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[NSRunLoop currentRunLoop] run];
});
return true;
}
up here, the connection works fine and does not lock. now my problem is that when I write something on the outputstream not send anything.
from another class by pressing a button I need to send something to the server, the function is called but does not send nothing, because it is in another thread I think.
-(void)Send:(NSMutableData *)_output{
[outputStream write:[_output bytes] maxLength:[_output length]];
}
How I can send something in outputstream if it is in another thread?
excuse my spelling, I do not speak much English
reading some forums, I found that NSThreads can pass data between them with:
performSelector:onThread:withObject:waitUntilDone:
but i don't know how I can create an instance of thread that is already running. Someone can help me?
for NS Thread just add all the above code in a function and call it like this:
[self performSelectorOnMainThread:#selector(TcpSocketFunction) withObject:nil waitUntilDone:NO];
Where TcpSocketFunction is the function name with a defination like.
- (void) TcpSocketFunction {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)IP,PORT, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream =(__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[NSRunLoop currentRunLoop] run];
}

VoIP app don't maintain connection in 3G

Currently I'm developing a VoIP application. In wifi connection keeps me perfectamente. The problem is when I use 3g. After about 6 minutes in the background, and VoIP socket doesn't receive any call. But apparently it's connected. The problem isn't SIP server as this allows me to renew the registration after 10 minutes. Any idea?
Thanks.
I add code:
AppDelegate *app = [[AppDelegate alloc] init];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
int misocket = zObtenerSocket();
CFStreamCreatePairWithSocket(NULL, misocket, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType,kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
inputStream = ( NSInputStream *)readStream;
outputStream = ( NSOutputStream *)writeStream;
[inputStream setDelegate:app];
[inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
[outputStream setDelegate:app];
[outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
cierra_extra();
zbCerrar();
[inputStream close];
[outputStream close];
registrarse_bg();
}];
registrarse_bg() reopens the socket and create the again

How to schedule event in runloop in GCD

I'm trying to connect to a server in a custom GCD queue. This is how I'm doing it.
- (void) initNetworkCommunication{
if(!self.connQueue){
self.connQueue = dispatch_queue_create("connection_queue", NULL);
}
dispatch_async(self.connQueue, ^(void) {
if(self.inputStream ==nil && self.outputStream ==nil) {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSString *host= [[NSUserDefaults standardUserDefaults] objectForKey:#"ip_preference"];
NSNumber *portNum = [[NSUserDefaults standardUserDefaults] objectForKey:#"port_preference"];
int port = [portNum intValue];
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
self.inputStream = (__bridge NSInputStream *)readStream;
self.outputStream = (__bridge NSOutputStream *)writeStream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream open];
[self.inputStream open];
} else {
NSLog(#"persistant connection alerady opened");
return;
}
});
}
Now, if I write this piece of code in dispatch_sync, it calls delegate function correctly.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
But, when I use dispatch_asynch, which is what I want to do, it does not call my delegate function.
From what I've read on SO so far, GCD queues have a runloop associated with them but these are not run by the system and we need to do so ourselves. I understand this in theory, but but how is it done. Dispatch sources associated with this somehow?
Thank You in advance.
Add this method after [self.inputStream open];
[[NSRunLoop currentRunLoop]run];
This puts the receiver into a permanent loop, during which time it processes data from all attached input sources.
See apple docs about RunLoops
The other way let run on dispatch_get_main_queue when use dispatch_async();

Good Example of network-based NSStreams on a Separate Thread?

Does anyone know of a simple example that creates (network-based) NSStreams on a separate thread?
What I am actually trying to do is unschedule (from the main thread) and reschedule (to a helper/network thread) an open NSInputStream and NSOutputStream that I am receiving from a third party framework (see Can an open, but inactive, NSStream that is scheduled on the main thread be moved to a different thread?). Nobody has answered that question thus far, so I am trying to do this myself to see if it could be made to work.
To test what is possible, I am trying to modify the code here (which includes a iOS client and a very short, python-based server [awesome!]):
http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server
so that after the NSInputStream and NSOutputStream is created and opened I attempt to move it onto a helper thread.
The challenge that I am experiencing is that the helper thread does not seem to be responding to delegate messages from the streams or any messages that I am sending via:
performSelector:onThread:withObject:waitUntilDone:modes:. I suspect that I am doing something wrong with setting up the helper thread's NSRunLoop (see networkThreadMain below).
So, [ChatClientViewController viewDidLoad] now looks like:
- (void)viewDidLoad {
[super viewDidLoad];
[self initNetworkCommunication];
[self decoupleStreamsFromMainThread];
[self spoolUpNetworkThread];
inputNameField.text = #"cesare";
messages = [[NSMutableArray alloc] init];
self.tView.delegate = self;
self.tView.dataSource = self;
}
With these implementations:
- (void) initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"localhost", 80, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void) decoupleStreamsFromMainThread
{
inputStream.delegate = nil;
outputStream.delegate = nil;
[inputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
[outputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
- (void) spoolUpNetworkThread
{
[NSThread detachNewThreadSelector: #selector(networkThreadMain) toTarget: self withObject: nil];
}
- (void) networkThreadMain
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
static dispatch_once_t once;
dispatch_once(&once, ^{
[self rescheduleThreads];
((ChatClientAppDelegate * )[[UIApplication sharedApplication] delegate]).networkThread = [NSThread currentThread];
[inputStream setDelegate:self];
[outputStream setDelegate:self];
NSLog(#"inputStream status is: %#", [NSInputStream streamStatusCodeDescription: [inputStream streamStatus]]);
NSLog(#"outputStream status is: %#", [NSOutputStream streamStatusCodeDescription: [outputStream streamStatus]]);
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantFuture]];
});
[pool release]; // Release the objects in the pool.
}
- (void) rescheduleThreads
{
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
Any ideas on what might be wrong? Thanks in advance!
Something along the lines of the code in the question below works well ( I have similar code in my app):
How to properly open and close a NSStream on another thread

Resources