I was asked to describe my setup
#31
I'd also like to try to test / reproduce in my environment first. Could you summarise (or link to a post) with reproduction steps? I haven't read all messages here too attentively tbh.

anyway, I believe that switching to NSURLSession and playing with a few of its configuration settings should solve this.
Reply
#32
Reproduction:
- Setup a WiFi hotspot
- Connect your Mac to it
- Change the Mac’s WiFi setting to manual IP and leave the router empty
- Now connection attempts via App simulator result in „No internet connection available“

Same happens on an iOS device when connecting to the hotspot and using manual IP without router.
Reply
#33
If you necessarily must use the simulator then whatever works I guess, bit I'll just add this:
  •  simulator testing isn't a replacement for device testing
  •  I've not confirmed the issue on a simulator, so can't say whether it's the same issue
  •  the described simulator environment isn't necessarily equivalent, since the mock cellular connection will reasonably now also miss a default router
  • The last bullet point in the last message doesn't mention .local addresses — if IP addresses also fail, then it's not equivalent. IP addresses should reasonably still work though, from the sound of it
  • cellular permissions don't seem to play a part (?) in the simulation scenario, suggesting possible further environmental differences
Reply
#34
(2022-11-29, 10:12)kambala Wrote: I'd also like to try to test / reproduce in my environment first. Could you summarise (or link to a post) with reproduction steps? I haven't read all messages here too attentively tbh.

I think my second post just about covers the steps. As I see it, nothing has changed since then.

To get specific instructions we'd need to know your iOS version. It also seems to manifest in slightly different ways in iOS 16 (I've seen more specific error messages actually mentioning failing to look up the host name)
Reply
#35
@Appstore-temp thanks a lot. I have devices running 9, 10, 12, 15 and 16, will try to test on all of them and post results.
Reply
#36
Any luck?
Reply
#37
I'll assume there's been no progress. If you require no additional info then this account will be deleted within the next few days.
Reply
#38
Thank you for your efforts, I was really hoping to find a solution but I am at the end of my possibilities.

@kambala, were you able to look into this?
Reply
#39
Well, you can't win them all.

I did try some clunky manual resolution of addresses last year and plugged it into `selectServerAtIndexPath` to see if different things happened. Long story short: it made me more confused since it worked partially (as in "yellow dot" in-app), but always only on the second launch of the app — not the first. Since I realized that I don't even know the significance of that yellow light in the GUI I decided to not continue with the guesswork. 

I'll leave it here if anyone wants to have a go:

mDnsTest.swift:


import Foundation

@objc
public class mDNS : NSObject {
    
    class func inet(addr: sockaddr_in) -> String {
        let ipv4addr = (0...3).map { UInt8(truncatingIfNeeded: addr.sin_addr.s_addr >> ($0 * 8)) }
        let addrString = ipv4addr.map(String.init).joined(separator: ".")
        print(addrString)
        return addrString
    }
    
    class func inet6(addr: sockaddr_in6) -> String {
        let ipv6addr = [
            addr.sin6_addr.__u6_addr.__u6_addr32.0,
            addr.sin6_addr.__u6_addr.__u6_addr32.1,
            addr.sin6_addr.__u6_addr.__u6_addr32.2,
            addr.sin6_addr.__u6_addr.__u6_addr32.3
        ]
            .flatMap { uint32 in
                (0...3).map { UInt8(truncatingIfNeeded: uint32 >> ($0 * 8)) }
            }
        
        var byteStrings = ipv6addr.map { String(format: "%02X", $0) }
        stride(from: 14, through: 2, by: -2).forEach { byteStrings.insert(":", at: $0) }
        
        var addrString = byteStrings.joined()
        
        print(addrString)
        
        return addrString
    }
    
    @objc
    class func lookup(_ hostname: String) -> String? {
        print("resolving: \(hostname)")
        let name = hostname as CFString
        let host = CFHostCreateWithName(kCFAllocatorDefault, name).takeUnretainedValue()
        let error: UnsafeMutablePointer<CFStreamError>? = nil
        let resolutionStatus = CFHostStartInfoResolution(host, CFHostInfoType.addresses, error)
        
        print("Resolution returned: \(resolutionStatus)")
        
        //typealias LayoutA = MemoryLayout<sockaddr>
        //typealias Layout4 = MemoryLayout<sockaddr_in>
        //typealias Layout6 = MemoryLayout<sockaddr_in6>
        //
        //assert(LayoutA.size == Layout4.size)
        //assert(LayoutA.size == Layout6.size) // nope
        //assert(LayoutA.stride == Layout4.stride)
        //assert(LayoutA.stride == Layout6.stride) // nope
        
        guard error == nil else { fatalError("\(error!.pointee)") }
        
        let addresses = CFHostGetAddressing(host, nil)?.takeRetainedValue() as? [Data] ?? []
        print(addresses)
        var addressStrings = [String]()
        for data in addresses {
            
            let umbp = UnsafeMutableBufferPointer<sockaddr>.allocate(capacity: 1)
            data.copyBytes(to: umbp)
            
            guard let sock = umbp.first else { continue }
            print("length: \(sock.sa_len), family: \(sock.sa_family)")
            
            switch(Int32(sock.sa_family)) {
            case AF_INET: // IPv4
                print("inet")
                let umbp = UnsafeMutableBufferPointer<sockaddr_in>.allocate(capacity: 1)
                data.copyBytes(to: umbp)
                guard let addr = umbp.first else { continue }
                addressStrings.append(inet(addr: addr))
                
            case AF_INET6: // IPv6
                print("inet6")
                let umbp = UnsafeMutableBufferPointer<sockaddr_in6>.allocate(capacity: 1)
                data.copyBytes(to: umbp)
                guard let addr = umbp.first else { continue }
                addressStrings.append(inet6(addr: addr))

            default:
                print("unhandled family: \(sock.sa_family)")
            }
        }
        let result = addressStrings.sorted(by: { $0.count < $1.count }).first
        print("address \(hostname) resolved to \(result)")
        return result
    }
}

HostManagementeViewController:

- (void)selectServerAtIndexPathSadNSIndexPath*)indexPath {
    NSDictionary *item = AppDelegate.instance.arrayServerList[indexPath.row];
    AppDelegate.instance.obj.serverDescription = IsEmpty(item[@"serverDescription"]) ? @"" : item[@"serverDescription"];
    AppDelegate.instance.obj.serverUser = IsEmpty(item[@"serverUser"]) ? @"" : item[@"serverUser"];
    AppDelegate.instance.obj.serverPass = IsEmpty(item[@"serverPass"]) ? @"" : item[@"serverPass"];
    
    BOOL useMdnsTest = YES;
    
    if (!useMdnsTest) {
        AppDelegate.instance.obj.serverIP = IsEmpty(item[@"serverIP"]) ? @"" : item[@"serverIP"];
    } else {
        NSString *resolvedIp = [mDNS lookup: item[@"serverIP"]];
        AppDelegate.instance.obj.serverIP = IsEmpty(resolvedIp) ? @"" : [NSString stringWithFormat:@"[%@]", resolvedIp];
    }
    
    AppDelegate.instance.obj.serverPort = IsEmpty(item[@"serverPort"]) ? @"" : item[@"serverPort"];
    AppDelegate.instance.obj.serverHWAddr = IsEmpty(item[@"serverMacAddress"]) ? @"" : item[@"serverMacAddress"];
    AppDelegate.instance.obj.tcpPort = [item[@"tcpPort"] intValue];
}
Reply
#40
Hmm, your code let me start to look at netServiceDidResolveAddress which did not resolve ipv6 yet. I now added this in a hacky way (inspired by https://stackoverflow.com/questions/7072...-ios-macos). This now connects to Kodi server with via an ipv6 address, and it does only show "yellow" connection status, which means the http/json connection works, but no tcp connection (needed to receive updates from Kodi without polling) is established. Does this work for you as well?

Code:
- (void)netServiceDidResolveAddress: (NSNetService*)service {
    
    for (NSData *data in [service addresses]) {
        char addressBuffer[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
        struct sockaddr_in *socketAddress = (struct sockaddr_in*)[data bytes];
        int sockFamily = socketAddress->sin_family;
        if (sockFamily == AF_INET) {
            const char *addressStr = inet_ntop(sockFamily,
                                               &(socketAddress->sin_addr),
                                               addressBuffer,
                                               INET_ADDRSTRLEN);
            int port = ntohs(socketAddress->sin_port);
            if (addressStr && port) {
                descriptionUI.text = service.name;
                ipUI.text = [NSString stringWithFormat:@"%s", addressStr];
                portUI.text = [NSString stringWithFormat:@"%d", port];
                descriptionUI.textColor = [Utilities getSystemBlue];
                ipUI.textColor = [Utilities getSystemBlue];
                portUI.textColor = [Utilities getSystemBlue];
                NSString *serverJSON = [NSString stringWithFormat:@"http://%@:%@/jsonrpc", ipUI.text, portUI.text];
                NSURL *url = [[NSURL alloc] initWithString: serverJSON];
                NSURLSession *pingSession = [NSURLSession sharedSession];
                NSURLSessionDataTask *pingConnection = [pingSession dataTaskWithURL:url
                                                                  completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self fillMacAddressInfo];
                    });
                }];
                [pingConnection resume];
                [Utilities AnimView:discoveredInstancesView AnimDuration:0.3 Alpha:1.0 XPos: self.view.frame.size.width];
            }
        }
        else if (sockFamily == AF_INET6) {
            const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)[data bytes];
            const char *addressStr = inet_ntop(AF_INET6,
                                               &addr6->sin6_addr,
                                               addressBuffer,
                                               INET6_ADDRSTRLEN);
            int port = ntohs(socketAddress->sin_port);
            if (addressStr && port) {
                descriptionUI.text = service.name;
                ipUI.text = [NSString stringWithFormat:@"[%s]", addressStr];
                portUI.text = [NSString stringWithFormat:@"%d", port];
                descriptionUI.textColor = [Utilities getSystemBlue];
                ipUI.textColor = [Utilities getSystemBlue];
                portUI.textColor = [Utilities getSystemBlue];
                NSString *serverJSON = [NSString stringWithFormat:@"http://%@:%@/jsonrpc", ipUI.text, portUI.text];
                NSURL *url = [[NSURL alloc] initWithString: serverJSON];
                NSURLSession *pingSession = [NSURLSession sharedSession];
                NSURLSessionDataTask *pingConnection = [pingSession dataTaskWithURL:url
                                                                  completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self fillMacAddressInfo];
                    });
                }];
                [pingConnection resume];
                [Utilities AnimView:discoveredInstancesView AnimDuration:0.3 Alpha:1.0 XPos: self.view.frame.size.width];
            }
        }
    }
}
Reply
#41
And "green" status is reached when adding another hack to startNetworkCommunicationWithServer.

Code:
- (void)startNetworkCommunicationWithServer: (NSString*)server serverPort: (int)port {
    if (port == 0) {
        port = 9090;
    }
    if ([server isEqualToString:@""]) {
        return;
    }
    // remove brackets for ipv6
    server = [server stringByReplacingOccurrencesOfString:@"[" withString:@""];
    server = [server stringByReplacingOccurrencesOfString:@"]" withString:@""];
    
    CFReadStreamRef readStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)CFBridgingRetain(server), port, &readStream, NULL);
    inStream = (__bridge NSInputStream*)readStream;
    // outStream = (__bridge NSOutputStream*)writeStream;
    inStream.delegate = self;
    // outStream.delegate = self;
    [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    // [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream open];
    // [outStream open];
    CFRelease((__bridge CFTypeRef)server);
}

Does this work for your setup with these hacks?
Reply
#42
https://github.com/wutschel/Official-Kod...ature_ipv6 -> The settings now have a user selectable preference for service discovery ("Find Kodi"): ipv4, ipv6 and localhost. Would be great, if you can let me know, if this helps in your case.
Reply
#43
I can take a look, if you could first clarify your mention of service discovery.

This issue has never been about service discovery, so from my perspective it will only complicate things to involve it without a good reason. I can't be sure you're testing the right thing as long as it's mentioned, and since it's also been mentioned previously in this thread I think we need to clear it up (I couldn't find an answer in previous posts). Have I missed something?
Reply
#44
Sure, with service discovery I meant the process triggered by pressing "Find Kodi". This calls [netServiceBrowser searchForServicesOfType:inDomain:] to identify the addresses which support the Kodi remote API service. Finally this will fill the server IP address which is input to the method [mDNS lookup:] you were adding. If I understood correctly, but I might be wrong, you again look up the IP (v4 and v6) for a given item[@"serverIP"]. I suspected you might have the trouble because you run ipv6-only?

Now I changed the service discovery itself to handle v4/v6 and also enable the TCP socket properly (green instead of yellow connection status).
Reply
#45
Thank you.

I understand what you mean by service discovery, but I still don't understand why it's mentioned in the context of this issue. Please read my previous post https://forum.kodi.tv/showthread.php?tid...pid3119323 and see if you've missed something; I couldn't find any subsequent replies that addressed my concerns about involving zeroconf service discovery in that post.

This issue is about .local-address resolution, which isn't the same as service discovery. Unless there's a good reason for it, we should not involve "find Kodi".
Reply

Logout Mark Read Team Forum Stats Members Help
I was asked to describe my setup0