NWConnection not connecting with QUIC

In an iOS network extension setup as a Packet Tunnel. I am accepting packets in using NEPacketTunnelFlow and open a NWConnection up to the destination like so

let parameters = .quic(alpn: [])
let connection = NWConnection(to: .hostPort(host: host, port: port!), using: parameters)

but my state handler is never receiving the .ready state. My NWConnection code works fine for TCP and DNS using

let parameters = .tcp // OR
let parameters = .udp

On my NWConnection state handler I am receiving .waiting with an error of dns. I am testing quic by navigating onto the Facebook app and with wireshark intercepting the traffic I can see the DNS queries resolving

I have NEDNSSettings applied to my NEPacketTunnelNetworkSettings

Is my issue to do with not passing in the alpn value or is there an extra permission/setting I am missing? I have not found a value which changes the outcome at all.

I have tried following [https://developer.apple.com/videos/play/wwdc2021/10094/) but no success.

Is this specific to the NE provider context? Does the same connection work if you run it from a small test app?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo Hi thank you for your reply.

Potentially, I have not tried to replicate in a test app due to the setup time to replicate a similar environment. Since TCP and UDP were working as expected I was hoping to avoid creating a test app.

If that seems the most logical solution then I'm willing to try it out.

I've currently got my ALPN values as NWProtocolQUIC.Options(alpn: ["h3", "h2", "h1", "h3-29"]) and have tried using the decoded sni to create the NWConnection rather than raw IP but that hasn't changed the outcome.

After creating a separate app I see no difference in the output. Still getting the connection state stuck on 'waiting' with error 'DNS'.

I have tried copying some code from https://developer.apple.com/documentation/network/nwconnection/collecting_network_connection_metrics to alter how the NWParameters are created and the options used but no luck.

I can at least give you the information on why this might be happening; for nw_connection to be able to setup a QUIC connection to any remote endpoint TLS needs to be able to negotiate an ALPN, and in this case for Facebook, I can see that at my desk it cannot negotiate the ALPN for any test ALPN that may be set. Looking at the Network tab in Safari for Facebook it gives us a clue as to what this might be when it advertises Alt-Svc. For example: Alt-Svc: h3=":443"; ma=86400.

So adding h3 as the ALPN can at least give us a successful connection. For example:

dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", NULL);
char url[128] = {};
snprintf(url, sizeof(url), "https://www.facebook.com");
nw_endpoint_t endpoint = nw_endpoint_create_url(url);

nw_parameters_t parameters = nw_parameters_create_quic(^(nw_protocol_options_t _Nonnull options) {
    nw_quic_add_tls_application_protocol(options, "h3");
    sec_protocol_options_t sec_options = nw_quic_copy_sec_protocol_options(options);
    sec_protocol_options_set_peer_authentication_required(sec_options, false);
});

nw_connection_t connection = nw_connection_create(endpoint, parameters);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state,
                                                      __unused nw_error_t _Nonnull error) {
    if (state == nw_connection_state_ready) {
        NSLog(@"Connection ready");
        dispatch_semaphore_signal(semaphore);
    } else if (state == nw_connection_state_cancelled) {
        NSLog(@"Connection cancelled");
        dispatch_semaphore_signal(semaphore);
    }
});
nw_connection_set_queue(connection, queue);
nw_connection_start(connection);
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));

NWConnection not connecting with QUIC
 
 
Q