arcam / CocoaUPnP

CocoaUPnP is a logical progression of upnpx; designed to be easy, modern and block-based.
MIT License
85 stars 40 forks source link

Can't discovery UPnP devices connected to iPhone's personal hotspot. #38

Closed debugholic closed 5 years ago

debugholic commented 7 years ago

Hello.

I found this issue to previous release but it is still remained so I thought to need to notify it.

Before reading, I'm not good at English. Therefore, although my wording is some strange, please understand.

The issue is occurred as following situation :

  1. Turn on "Personal hotspot" in iPhone.
  2. Connect UPnP Root device to iPhone's hotspot.
  3. Start demo app in iPhone.
  4. Can't see any UPnP device at tableView. (NOT discovered)

I think it is because of the problem that interface name of personal hotspot is bridge, not en.

Original Code

if( (ifa->ifa_addr->sa_family == AF_INET) && !(ifa->ifa_flags & IFF_LOOPBACK) && !strncmp(ifa->ifa_name, "en", 2)) {
    NSData *data = [NSData dataWithBytes:ifa->ifa_addr length:sizeof(struct sockaddr_in)];
    NSString *if_name = [NSString stringWithUTF8String:ifa->ifa_name];
    [addresses setObject:data forKey:if_name];
}

So, I ask to modify the method availableNetworkInterfaces of SSDPServiceBrowser for all network environment. For example :

Modified Code

if (((ifa->ifa_addr->sa_family == AF_INET) || (ifa->ifa_addr->sa_family == AF_INET6)) && !(ifa->ifa_flags & IFF_LOOPBACK)) {
    if (!strncmp(ifa->ifa_name, "en", 2) || !strncmp(ifa->ifa_name, "bridge", 6)) {
        NSData *data = [NSData dataWithBytes:ifa->ifa_addr length:sizeof(struct sockaddr_in)];
        NSString *if_name = [NSString stringWithUTF8String:ifa->ifa_name];

        int addressFamily = [[GCDAsyncUdpSocket class] familyFromAddress:data];
        if (addressFamily != AF_UNSPEC) {
            [addresses setObject:data forKey:if_name];
        }
    }
}

cf) The method GCDWebServerGetPrimaryIPAddress in GCDWebServerFunctions of GCDWebServer has to be modified to subscribe event.

Also, there is another issue only in iOS 10. It is phone and devices are not in same network(only bridge).

I don't know why it is occurred only in iOS 10, but anyway it seems phone and device get different addresses in bridge network.

This issue was solved by modifying setupMulticastSocket.

Original Code

if (![self.multicastSocket joinMulticastGroup:SSDPMulticastGroupAddress error:&err]) {
    [self _notifyDelegateWithError:err];
    return;
}

Modified Code

if (![self.multicastSocket joinMulticastGroup:SSDPMulticastGroupAddress onInterface:[[GCDAsyncUdpSocket class] hostFromAddress:sourceAddress] error:&err]) {
    [self _notifyDelegateWithError:err];
    return;
}

Oh, Sorry it was too long to read. :-)

I always appreciate and respect your effort. Thank you.

squarefrog commented 7 years ago

@DebugHolic certainly living up to your name!

Thank you very much for such a detailed issue. I will definitely look into this tomorrow. I hadn't even considered someone may want to use this on a hotspot :+1:

Any other issues let me know.

squarefrog commented 7 years ago

Hi @DebugHolic

I had a look into this today, unfortunately I couldn't get it to work correctly. I added the two changes in, setup a hotspot on an iPhone 6S (iOS 10.1.1), then ran the Example app. No connected devices would show up for me.

debugholic commented 7 years ago

Hi @squarefrog

Thank you for your consideration.

I'm so sorry. I've been too busy to see your comment. I MISSED two important corrections. You should also modify GCDWebServerFunctions in GCDWebServer and GCDAsyncUdpSocket in CocoaAsyncSocket.

The corrections are as following.

GCDWebServerFunctions - GCDWebServerGetPrimaryIPAddress

  1. Make primaryInterface disable.

// const char* primaryInterface = "en0"; // WiFi interface on iOS

  1. Make statement disable in about line 274.

// if (strcmp(ifap->ifa_name, primaryInterface))

  1. Add following code instead.

if (strncmp(ifap->ifa_name, "en", 2) && strncmp(ifap->ifa_name, "bridge", 6))

It helps you to receive events from DLNA device.


GCDWebServer - performMulticastRequest: forGroup: onInterface: error

       if ((socket4FD != SOCKET_NULL) && groupAddr4 && interfaceAddr4) { 
           ......

ADD following codes

              status = setsockopt(socket4FD, IPPROTO_IP, IP_MULTICAST_IF, &nativeIface->sin_addr, sizeof(nativeIface->sin_addr)); 
              if (status != 0) {
                    err = [self errnoErrorWithReason:@"Error in setsockopt() function"];
                    return_from_block;
              }
          // Using IPv4 only
          [self closeSocket6];

          result = YES;
      }

OK, it's over. It works for me.


I'm developing an APP using your library and unfortunately I found another issue.

I want to get volume range from UPnP Renderer Device and it is in RenderingControlService description. But, CocoaUPnP doesn't parse the service description, so I am adding the codes to my project.

I ask for you to look this issue. :-)

vinidiktov-edsd commented 6 years ago

Thank you @DebugHolic,

You've saved my day!