fix communication with debuggserver on ios-14 (#471)

* fix communication with debuggserver on ios-14

* decrease read buffer size, create socket with correct flags

* fix regression on older iOS devices
diff --git a/src/ios-deploy/MobileDevice.h b/src/ios-deploy/MobileDevice.h
index 2646bcd..637890b 100644
--- a/src/ios-deploy/MobileDevice.h
+++ b/src/ios-deploy/MobileDevice.h
@@ -65,7 +65,14 @@
 
 typedef unsigned int afc_error_t;
 typedef unsigned int usbmux_error_t;
-typedef unsigned int service_conn_t;
+
+typedef struct {
+    char unknown[0x10];
+    int sockfd;
+    void * sslContext;
+    // ??
+} service_conn_t;
+
 typedef service_conn_t * ServiceConnRef;
 
 struct am_recovery_device;
diff --git a/src/ios-deploy/ios-deploy.m b/src/ios-deploy/ios-deploy.m
index 25ea073..31ad128 100644
--- a/src/ios-deploy/ios-deploy.m
+++ b/src/ios-deploy/ios-deploy.m
@@ -12,6 +12,8 @@
 #include <signal.h>
 #include <getopt.h>
 #include <pwd.h>
+#include <dlfcn.h>
+
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 
@@ -75,6 +77,9 @@
 mach_error_t AMDeviceSecureStartService(AMDeviceRef device, CFStringRef service_name, unsigned int *unknown, ServiceConnRef * handle);
 mach_error_t AMDeviceCreateHouseArrestService(AMDeviceRef device, CFStringRef identifier, CFDictionaryRef options, AFCConnectionRef * handle);
 CFSocketNativeHandle  AMDServiceConnectionGetSocket(ServiceConnRef con);
+void AMDServiceConnectionInvalidate(ServiceConnRef con);
+
+bool AMDeviceIsAtLeastVersionOnPlatform(AMDeviceRef device, CFDictionaryRef vers);
 int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
 int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
 int AMDeviceSecureInstallApplicationBundle(AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
@@ -82,6 +87,9 @@
 mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
 int AMDeviceGetInterfaceType(AMDeviceRef device);
 
+int AMDServiceConnectionSend(ServiceConnRef con, const void * data, size_t size);
+int AMDServiceConnectionReceive(ServiceConnRef con, void * data, size_t size);
+
 bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false;
 bool command_only = false;
 char *command = NULL;
@@ -104,7 +112,7 @@
 NSMutableArray *_file_meta_info = nil;
 int port = 0;    // 0 means "dynamically assigned"
 CFStringRef last_path = NULL;
-service_conn_t gdbfd;
+ServiceConnRef dbgServiceConnection = NULL;
 pid_t parent = 0;
 // PID of child process running lldb
 pid_t child = 0;
@@ -135,6 +143,22 @@
         }                                                                       \
     } while (false);
 
+
+void disable_ssl(ServiceConnRef con)
+{
+    // MobileDevice links with SSL, so function will be available;
+    typedef void (*SSL_free_t)(void*);
+    static SSL_free_t SSL_free = NULL;
+    if (SSL_free == NULL)
+    {
+        SSL_free = (SSL_free_t)dlsym(RTLD_DEFAULT, "SSL_free");
+    }
+
+    SSL_free(con->sslContext);
+    con->sslContext = NULL;
+}
+
+
 void on_error(NSString* format, ...)
 {
     va_list valist;
@@ -959,13 +983,24 @@
 void
 server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
 {
-    if (CFDataGetLength (data) == 0) {
+    char buffer[0x1000];
+    int bytesRead = AMDServiceConnectionReceive(dbgServiceConnection, buffer, sizeof(buffer));
+    if (bytesRead == 0)
+    {
         // close the socket on which we've got end-of-file, the server_socket.
         CFSocketInvalidate(s);
         CFRelease(s);
         return;
     }
-    write(CFSocketGetNative(lldb_socket), CFDataGetBytePtr(data), CFDataGetLength(data));
+    write(CFSocketGetNative (lldb_socket), buffer, bytesRead);
+    while (bytesRead == sizeof(buffer))
+    {
+        bytesRead = AMDServiceConnectionReceive(dbgServiceConnection, buffer, sizeof(buffer));
+        if (bytesRead > 0)
+        {
+            write(CFSocketGetNative (lldb_socket), buffer, bytesRead);
+        }
+    }
 }
 
 void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
@@ -978,7 +1013,8 @@
         CFRelease(s);
         return;
     }
-    write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data));
+    int sent = AMDServiceConnectionSend(dbgServiceConnection, CFDataGetBytePtr(data),  CFDataGetLength (data));
+    assert (CFDataGetLength (data) == sent);
 }
 
 void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
@@ -1012,8 +1048,19 @@
 
 void start_remote_debug_server(AMDeviceRef device) {
 
-    ServiceConnRef con = NULL;
-    int start_err = AMDeviceSecureStartService(device, CFSTR("com.apple.debugserver"), NULL, &con);
+    dbgServiceConnection = NULL;
+    CFStringRef serviceName = CFSTR("com.apple.debugserver");
+    CFStringRef keys[] = { CFSTR("MinIPhoneVersion"), CFSTR("MinAppleTVVersion") };
+    CFStringRef values[] = { CFSTR("14.0"), CFSTR("14.0")};
+    CFDictionaryRef version = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+    bool useSecureProxy = AMDeviceIsAtLeastVersionOnPlatform(device, version);
+    if (useSecureProxy)
+    {
+        serviceName = CFSTR("com.apple.debugserver.DVTSecureSocketProxy");
+    }
+
+    int start_err = AMDeviceSecureStartService(device, serviceName, NULL, &dbgServiceConnection);
     if (start_err != 0)
     {
         // After we mount the image, iOS needs to scan the image to register new services.
@@ -1037,15 +1084,20 @@
             default:
                 check_error(start_err);
         }
-        check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.debugserver"), NULL, &con));
+        check_error(AMDeviceSecureStartService(device, serviceName, NULL, &dbgServiceConnection));
     }
-    assert(con != NULL);
-    gdbfd = AMDServiceConnectionGetSocket(con);
+    assert(dbgServiceConnection != NULL);
+
+    if (!useSecureProxy)
+    {
+        disable_ssl(dbgServiceConnection);
+    }
+
     /*
      * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
      * socket to transfer data.
      */
-    server_socket = CFSocketCreateWithNative (NULL, gdbfd, kCFSocketDataCallBack, &server_callback, NULL);
+    server_socket = CFSocketCreateWithNative (NULL, AMDServiceConnectionGetSocket(dbgServiceConnection), kCFSocketReadCallBack, &server_callback, NULL);
     if (server_socket_runloop) {
         CFRelease(server_socket_runloop);
     }
@@ -2007,7 +2059,7 @@
           CFStringRef values[] = { CFSTR("Developer") };
           options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
           check_error(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0));
-          close(*afcFd);
+          AMDServiceConnectionInvalidate(afcFd);
 
           connect_and_start_session(device);
           check_error(AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0));