3

I have implemented the NEHotspotHelper so that I can perform authentication in the background for networks with a captive portal.

I need to perform a web request in the 'authenticating' state so that I can retrieve the Wispr and also access an API.

However, when I try to use URLSession to send a web request, the request fails. This is the error:

[594:88737] dnssd_clientstub read_all(7) DEFUNCT
[594:88783] TIC TCP Conn Failed [4:0x1c0177a00]: 12:8 Err(-65554)
[594:88783] Task <FFD0DAE6-4864-437D-94F2-C9ED5D5748E2>.<1> HTTP load failed (error code: -1003 [12:8])
[594:88783] Task <FFD0DAE6-4864-437D-94F2-C9ED5D5748E2>.<1> finished with error - code: -1003

See a snippet of my code:


 let registered = NEHotspotHelper.register(options: options, queue: queue) { (cmd: NEHotspotHelperCommand) in
    print("Received command: \(cmd.commandType.rawValue)")
    if cmd.commandType == NEHotspotHelperCommandType.filterScanList {
        //Get all available hotspots
        print("filter scan list")
        var list: [NEHotspotNetwork] = cmd.networkList!
        var response: NEHotspotHelperResponse
        for l in list {
            if (l.ssid=="my-ssid") {
                response = cmd.createResponse(NEHotspotHelperResult.success)
            } else {
                response = cmd.createResponse(NEHotspotHelperResult.failure)                       
            }
            response.setNetworkList([chosenNetwork])
            response.deliver()
        }   
    } else if cmd.commandType == NEHotspotHelperCommandType.evaluate {
        if let network = cmd.network {            
            if (network.ssid=="my-ssid") {            
                network.setConfidence(NEHotspotHelperConfidence.high)
                let response = cmd.createResponse(NEHotspotHelperResult.success)
                response.setNetwork(network)                 
                response.deliver() //Respond back
            } else {
                let response = cmd.createResponse(NEHotspotHelperResult.failure)
                response.deliver()
            }
        }
    } else if cmd.commandType == NEHotspotHelperCommandType.authenticate {
        print("authenticate")
        var response = cmd.createResponse(NEHotspotHelperResult.unsupportedNetwork)
        if let network = cmd.network{
            if network.ssid == "my-ssid"{
                self.queryUrl()
                response = cmd.createResponse(NEHotspotHelperResult.success)
            }
        }
        response.deliver() //Respond back                    
    }
}

func queryUrl(){
    let config = URLSessionConfiguration.default
    config.allowsCellularAccess = false;

    let session = URLSession.init(configuration: config)

    let url = URL(string: "https://172.217.20.35")

    let semaphore = DispatchSemaphore(value: 0)
    let task = session.dataTask(with: url!){(data, response, error) in
        if  data==nil {
            print(data as Any)            
        }
        else{
            print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue) as Any)
        }
        semaphore.signal()
    }

    task.resume()
    _ = semaphore.wait(timeout: .distantFuture)    
}
Stunner
  • 12,025
  • 12
  • 86
  • 145
user3344429
  • 160
  • 5
  • You should always deliver. But only once per command received. – Pierre Jan 26 '18 at 11:39
  • Change your web request to a synchronous call, and only deliver once after you have finished logging in via the request. If your request times out, still deliver success. Don't make the OS wait too long for you to return something either. I'd say, 1 minute max – Pierre Jan 26 '18 at 12:04
  • Thank you @Pierre, I have used an a synchronous call by using semaphores but it still does not work. (See queryUrl method) I should add that I am very new to Swift and iOS Development. This is the error: [284:19580] TIC TCP Conn Failed [1:0x1c416b340]: 1:50 Err(50) [284:19580] Task <30A63185-9A5A-4F94-ACE7-EC63504C8B05>.<1> HTTP load failed (error code: -1009 [1:50]) [284:19580] Task <30A63185-9A5A-4F94-ACE7-EC63504C8B05>.<1> finished with error - code: -1009 – user3344429 Jan 30 '18 at 11:25
  • If you try and implement what i've said, you will get it to work. Try your auth request, but if it fails with the above, return success result. iOS will finish the connection to the SSID and see that it needs some sort of authentication, the Authenticate|Maintain command will be posted again, the second time around the web request should work fine. Implement a switch just like my answer below – Pierre Jan 30 '18 at 13:21
  • Hey Pierre, once iOS is in the Authenticated state, a Maintaining timer is scheduled to run 300 secs after. I think that's too long to wait for a successful authentication. I wish there was a command for the state 'Authenticated'. https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/Hotspot_Network_Subsystem_Guide/Contents/AuthStateMachine.html#//apple_ref/doc/uid/TP40016639-CH2-SW1 – user3344429 Jan 30 '18 at 14:30
  • Hey @Pierre, did you use a URLSession datatask for the web request or did you use NWTCPConnection? – user3344429 Jan 31 '18 at 12:56
  • I actually use `Xamarin`, but try `NSMutableURLRequest`. See this https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/WorkingWithHTTPAndHTTPSRequests/WorkingWithHTTPAndHTTPSRequests.html – Pierre Feb 01 '18 at 10:39
  • Thanks @Pierre, I'll try it. So in your code, did you only get the web request to work in the 'Maintain' state and/or also in the 'Authenticating' state? – user3344429 Feb 01 '18 at 11:57
  • In Authenticate state – Pierre Feb 01 '18 at 12:03
  • ok @Pierre, MutableURLRequest is deprecated it seems. – user3344429 Feb 01 '18 at 12:47

3 Answers3

1

I was also facing the similar issue. However, I found that developer need to bind the request with the received command before making web request to the connected network. All you need to do is to make NSMutableURLRequest and then call hitTestURLRequest.bind(to: command) because bind function is defined in the category of NSMutableURLRequest.

Muhammad Zeeshan
  • 2,441
  • 22
  • 33
0

This is the just of it:

if cmd.commandType
    filterScanList
        - SetConfidence to each of your networks in cmd.networkList - High and add to a local list variable.
        - create Response.Success
        - set Response.NetworkList to your list of confident networks
        ----
        - deliver Response
    Evaluate or PresentUI
        //If it's your network
        - SetConfidence to cmd.network - High
        - create Response.Success
        - set Response.Network to cmd.network
        //If it's not your network
        - create Response.UnsupportedNetwork
        ----
        - deliver Response
    Authenticate or Maintain
        //If it's your network
        - SetConfidence to cmd.network - High
        - create Response.Success
        - set Response.Network to cmd.network
        - TRY AUTHENTICATE HERE SYNCHRONOUSLY
        //If it's not your network
        - create Response.UnsupportedNetwork
        ----
        - deliver Response
    Logoff or None
        - create Response.Success
        ----
        - deliver Response
Pierre
  • 8,397
  • 4
  • 64
  • 80
0

Others have reported problem with name server resolution from NEHotspotHelper callback. Try using an IP address to make the call.

Also don't forget URLSession works asynchronously. You will need to call response.deliver() only after the web service calls finishes.

RajV
  • 6,860
  • 8
  • 44
  • 62
  • Hey @RajV, Thank you. I have tried an IP address and used semaphores to create a synchronous call. It still does not work. I am also new to Swift. I still get the following error: [284:19580] TIC TCP Conn Failed [1:0x1c416b340]: 1:50 Err(50) [284:19580] Task <30A63185-9A5A-4F94-ACE7-EC63504C8B05>.<1> HTTP load failed (error code: -1009 [1:50]) [284:19580] Task <30A63185-9A5A-4F94-ACE7-EC63504C8B05>.<1> finished with error - code: -1009 – user3344429 Jan 30 '18 at 11:27