From cf281281836563f18b3e3ea77238f6d733f45f59 Mon Sep 17 00:00:00 2001 From: Pierluigi D'Andrea Date: Fri, 9 Aug 2024 10:07:24 +0200 Subject: [PATCH 1/2] Reserve HTTP server port --- .../SBTUITestTunnelClient.m | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m b/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m index 7c10e8e..86dff87 100755 --- a/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m +++ b/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m @@ -1062,26 +1062,60 @@ - (int)findOpenPort addr.sin_family = AF_INET; addr.sin_port = 0; inet_aton("0.0.0.0", &addr.sin_addr); - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { + int server_sock = socket(AF_INET, SOCK_STREAM, 0); + if (server_sock < 0) { return -1; } - if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) { + if (bind(server_sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) { + close(server_sock); return -2; } - if (getsockname(sock, (struct sockaddr*) &addr, &len) != 0) { + if (getsockname(server_sock, (struct sockaddr*) &addr, &len) != 0) { + close(server_sock); return -3; } - - if (addr.sin_port > 1023) { - return addr.sin_port; - } else { + + in_port_t port = addr.sin_port; + + if (port <= 1023) { + close(server_sock); NSLog(@"[SBTUITestTunnel] Invalid port assigned, trying again"); - close(sock); + continue; + } + + // Put the port in the TIME_WAIT state, to temporarily reserve it + if (listen(server_sock, 1)) { + close(server_sock); + return -4; + } + + int client_sock = socket(AF_INET, SOCK_STREAM, 0); + if (client_sock < 0) { + close(server_sock); + return -5; + } + + if (connect(client_sock, (struct sockaddr*) &addr, sizeof(addr))) { + close(server_sock); + close(client_sock); + return -6; } + + int accept_sock = accept(server_sock, nil, nil); + if (accept_sock < 0) { + close(server_sock); + close(client_sock); + return -7; + } + + close(server_sock); + close(client_sock); + close(accept_sock); + + return port; } - return -4; + return -8; } #pragma mark - Error Helpers From b2c7835cd79c3f32c5d00778267fd24c1fdd304e Mon Sep 17 00:00:00 2001 From: Pierluigi D'Andrea Date: Fri, 9 Aug 2024 20:12:37 +0200 Subject: [PATCH 2/2] Explain the logic behind port reservation more in detail --- Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m b/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m index 86dff87..a1cfd77 100755 --- a/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m +++ b/Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m @@ -1083,7 +1083,12 @@ - (int)findOpenPort continue; } - // Put the port in the TIME_WAIT state, to temporarily reserve it + // Attempt to reserve the port by putting it in TIME_WAIT state. During this time, + // the system prevents other applications from binding to the same port, + // to prevent packets meant for the recently closed connection from being + // misdirected to the new application. Since SBTWebServer is utilizing SO_REUSEADDR on the + // server socket, we can bind to the port even though it's in TIME_WAIT state, + // effectively reserving it for our own use until we close the server socket. if (listen(server_sock, 1)) { close(server_sock); return -4;