Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TCP-Based Client communication doesn't work in iOS 11?

I have an IoT application that uses Socket Programming to communicate with the device. All good up until iOS 11 released. It doesn't communicate in iOS 11 and above but it works in earlier versions ( Up to 10). Here is the code.

Establishing Socket Connection

(void)setUpSocketConnection {
    @try {
        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) VeranoHost,VeranoPort , &readStream, &writeStream);
        [self open];
    }
    @catch (NSException *exception) {
        NSLog(@"Open Exception:%@", exception.reason);
    }   
}

Opening Streams

(void)open {
    //NSLog(@"Opening streams.");
    _outputStream = (__bridge  NSOutputStream *)writeStream;
    _inputStream = (__bridge  NSInputStream *)readStream;
    [_outputStream setDelegate:self];
    [_inputStream setDelegate:self];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    dispatch_async(queue, ^ {
            [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop] run];
    });

    [_outputStream open];
    [_inputStream open];

    //[self disableNaglesAlgorithmForStream:_inputStream];
   // NSLog(@"Connected");
//    self.timeOutTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
//                                                         target:self
//                                                       selector:@selector(timerTimeOutAction:)
//                                                       userInfo:nil
//                                                        repeats:NO];
}

Writing to Output Stream

(void)writeData:(NSString *)message forSocketType:(SOCKETTTYPE)socketType {
    self.socketType = socketType;
    NSData *data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSASCIIStringEncoding]];
    [_outputStream write:[data bytes] maxLength:[data length]];
    [_outputStream close];

}

Event Handler

(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
   // NSLog(@"stream event %lu", (unsigned long)streamEvent);
    switch (streamEvent) {
        case NSStreamEventOpenCompleted:{

           // NSLog(@"NSStreamEventOpenCompleted :::: Stream opened and connected");
        }
            break;
        case NSStreamEventHasBytesAvailable:
            // NSLog(@"NSStreamEventHasBytesAvailable :::: Stream opened and connected");
            if (theStream == _inputStream) {
                uint8_t buffer[1024];
                NSInteger len;

                while ([_inputStream hasBytesAvailable])
                {
                    len = [_inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0)
                    {
                        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];

                        if (nil != output)
                        {
                            NSLog(@"server said: %@", output);
                            [self messageReceived:output];
                        }
                    }
                }
            }
            break;
        case NSStreamEventHasSpaceAvailable:
           // NSLog(@"NSStreamEventHasSpaceAvailable :::: Stream has space available now");
            break;

        case NSStreamEventErrorOccurred:{
           NSError *theError = [theStream streamError];
            NSLog(@"Error Description:%@",theError.localizedDescription);
            [self close];
            if(self.delegate &&[self.delegate respondsToSelector:@selector(socketHandlerItem:failureWithError:forSocketType:)]){
                [self.delegate socketHandlerItem:self failureWithError:[theStream streamError] forSocketType:self.socketType];
            }
             //NSLog(@"NSStreamEventErrorOccurred :::: %@",[theStream streamError].localizedDescription);
        }
            break;

        case NSStreamEventEndEncountered:
            [theStream close];
            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            if(self.delegate &&[self.delegate respondsToSelector:@selector(socketHandlerItem:eventStopedWithstatus:forSocketType:)]){
                [self.delegate socketHandlerItem:self eventStopedWithstatus:YES forSocketType:self.socketType];
            }
           // NSLog(@"NSStreamEventEndEncountered :::: close stream Disconnected");
            break;
        default:
           // NSLog(@"Unknown event");
            break;
    }
}

So, when connecting to the socket it enters to the NSStreamEventErrorOccurred in the event handler and logs - The operation couldn’t be completed. No route to host

Any help would be greatly appreciated.

Update Dec 19, 2017

  • Got a Reachability wrapper that allows to discover host address and I can confirm that it returns the NetworkStatus ReachableViaWiFi
  • Socket communication works fine, I tried by creating a sample server socket, able to send and receive data.

Update Dec 20, 2017 (1)

IoT device Details - USR-WIFI232-S Low Power WiFi Module

User Manual Here

Update Dec 20, 2017 (2)

Eurekaa, but the solution can't be applied in real time. While setting a nearest range of static IP address in iOS 11 corresponding to IoT module's host address the communication works fine. but it doesn't work with the dynamically allocated IP address.

like image 818
Stella Avatar asked Dec 18 '17 09:12

Stella


1 Answers

I Wonder if it App Transport Security (ATS), See: https://www.nowsecure.com/blog/2017/08/31/security-analysts-guide-nsapptransportsecurity-nsallowsarbitraryloads-app-transport-security-ats-exceptions/

For iOS 11 ATS updates some ATS updates are expected as a part of that:

  • TLSv1.3 will receive preliminary support
  • 3DES will be removed from the approved list of ciphers
  • Certificates signed with SHA1 will no longer be accepted
  • Certificates signed with RSA keys must have 2048-bit key lengths or larger

PLIST and Entitlements are you using in your app?

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>

        <key>creativecommons.org</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>

        <key>localhost</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>

    </dict>
</dict>

Also one more option, if you want to disable ATS you can use this :

<key>NSAppTransportSecurity</key>  
 <dict>  
      <key>NSAllowsArbitraryLoads</key><true/>  
 </dict>

But this is not recommended !

like image 172
Mark Avatar answered Nov 06 '22 06:11

Mark