#import static NSString * const SelfSignedHost = @"localhost"; // Point this at your self-signed server void readStreamCallback(CFReadStreamRef readStream, CFStreamEventType eventType, void *userInfo) { NSLog(@"read stream got event %td", eventType); if (eventType == kCFStreamEventHasBytesAvailable) { unsigned char buf[1024]; CFIndex bytesRead = CFReadStreamRead(readStream, buf, 1024); NSLog(@"read %tu bytes", bytesRead); char *readCString = memset(malloc((bytesRead + 1) * sizeof(char)), '\0', bytesRead+1); memcpy(readCString, buf, bytesRead); NSString *result = [[NSString alloc] initWithUTF8String:readCString]; NSLog(@"%@", result); } else if (eventType == kCFStreamEventErrorOccurred) { NSLog(@"read stream encountered error: %@", CFReadStreamCopyError(readStream)); } } void writeStreamCallback(CFWriteStreamRef writeStream, CFStreamEventType eventType, void *userInfo) { NSLog(@"write stream got event %td", eventType); static BOOL written; if (eventType == kCFStreamEventCanAcceptBytes && !written) { // Just once, write a terrible basic HTTP request across the secure stream NSString *HTTPRequest = @"GET / HTTP/1.0\n\n"; char *buf = memset(malloc(1024 * sizeof(char)), '\0', 1024); [HTTPRequest getCString:buf maxLength:1024 encoding:NSUTF8StringEncoding]; CFIndex bytesWritten = CFWriteStreamWrite(writeStream, (unsigned char *)buf, [HTTPRequest length]); NSLog(@"wrote %td bytes", bytesWritten); written = YES; } else if (eventType == kCFStreamEventErrorOccurred) { NSLog(@"write stream encountered error: %@", CFWriteStreamCopyError(writeStream)); } } int main(int argc, char *argv[]) { @autoreleasepool { // Open a pair of streams to a self-signed server CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)SelfSignedHost, 443, &readStream, &writeStream); // Don't validate the certificate chain. (This isn't circumventing ATS, but instead telling CFNetwork not to care about the self-signedness of the certificate.) CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)@{ (__bridge id)kCFStreamSSLValidatesCertificateChain : (__bridge id)kCFBooleanFalse }); // Get callbacks for stream events. We don't need any special app context. CFStreamClientContext context = (CFStreamClientContext){ .version = 0, .info = NULL, .retain = NULL, .release = NULL, .copyDescription = NULL, }; CFReadStreamSetClient(readStream, kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, readStreamCallback, &context); CFWriteStreamSetClient(writeStream, kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, writeStreamCallback, &context); // Run the streams alongside the main run loop. CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetMain(), kCFRunLoopCommonModes); CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetMain(), kCFRunLoopCommonModes); // Make the connection. BOOL readOpened = CFReadStreamOpen(readStream); BOOL writeOpened = CFWriteStreamOpen(writeStream); NSLog(@"opened streams: read=%@ write=%@", readOpened ? @"YES" : @"NO", writeOpened ? @"YES" : @"NO"); // Hang out waiting for stream events. CFRunLoopRun(); } }