@@ -2922,6 +2922,62 @@ mod tests {
29222922 Ok ( ( ) )
29232923 }
29242924
2925+ /// Regression test: Don't fail connections with dead relays on Windows.
2926+ ///
2927+ /// A single client connecting to a single server over a usable direct path
2928+ /// must succeed even when both are configured with an unreachable home relay
2929+ /// (`https://127.0.0.1:1`, nothing listening). The dead relay should be irrelevant:
2930+ /// the direct path works and the connection comes up in milliseconds.
2931+ ///
2932+ /// This was broken on Windows because QaD sends over the same socket to the dead
2933+ /// relay, and the socket would return recv errors on the next recv to report ICMP
2934+ /// errors for the previous send. We now skip over these errors, implemented in
2935+ /// https://github.com/n0-computer/net-tools/pull/166, so this no longer fails.
2936+ #[ tokio:: test]
2937+ async fn endpoint_unreachable_relay_direct_connect_succeeds ( ) -> Result {
2938+ // An unreachable relay Nothing listens on 127.0.0.1:1, and its QADv4 probe target
2939+ // at 127.0.0.1:7842 is closed too, so probing it draws the ICMP port-unreachable
2940+ // that is emitted from the Windows socket on recv.
2941+ let dead_relay: RelayUrl = "https://127.0.0.1:1" . parse ( ) . expect ( "valid relay url" ) ;
2942+
2943+ let bind_endpoint = async || {
2944+ Endpoint :: builder ( presets:: Minimal )
2945+ // Use the broken relay to trigger the ICMP errors from the QaD sends.
2946+ . relay_mode ( RelayMode :: Custom ( RelayMap :: from_iter ( [ dead_relay. clone ( ) ] ) ) )
2947+ . ca_tls_config ( CaTlsConfig :: insecure_skip_verify ( ) )
2948+ . alpns ( vec ! [ TEST_ALPN . to_vec( ) ] )
2949+ // Bind on IPv4 only to ensure a single socket to not have spurious polls.
2950+ . bind_addr ( ( Ipv4Addr :: LOCALHOST , 0 ) )
2951+ . expect ( "valid addr" )
2952+ . bind ( )
2953+ . await
2954+ } ;
2955+
2956+ let server = bind_endpoint ( ) . await ?;
2957+ let server_addr = server. addr ( ) . with_relay_url ( dead_relay. clone ( ) ) ;
2958+ let client = bind_endpoint ( ) . await ?;
2959+
2960+ // Server accepts the incoming connection and holds it open until the test ends.
2961+ let accept = tokio:: spawn ( async move {
2962+ let incoming = server. accept ( ) . await . anyerr ( ) ?;
2963+ let conn = incoming. await . anyerr ( ) ?;
2964+ conn. closed ( ) . await ;
2965+ server. close ( ) . await ;
2966+ n0_error:: Ok ( ( ) )
2967+ } ) ;
2968+
2969+ // The connect must complete over the direct loopback path despite the dead relay.
2970+ let _conn = tokio:: time:: timeout (
2971+ Duration :: from_secs ( 10 ) ,
2972+ client. connect ( server_addr, TEST_ALPN ) ,
2973+ )
2974+ . await
2975+ . expect ( "connection should succeed" ) ?;
2976+ client. close ( ) . await ;
2977+ accept. await . anyerr ( ) ??;
2978+ Ok ( ( ) )
2979+ }
2980+
29252981 #[ tokio:: test]
29262982 #[ traced_test]
29272983 async fn test_direct_addresses_no_qad_relay ( ) -> Result {
0 commit comments