diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 73c64bc8..6f8efbc7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -225,7 +225,10 @@ jobs: if-no-files-found: error build_rayhunter: - if: needs.files_changed.outputs.daemon_changed != '0' + # build_rust_installer needs this step. so when installer_changed, we need + # to build this step too. if we skip this step because only the installer + # changed, the build_rust_installer step will be skipped too. + if: needs.files_changed.outputs.daemon_changed != '0' || needs.files_changed.outputs.installer_changed != '0' needs: - check_and_test - files_changed diff --git a/installer/src/tplink.rs b/installer/src/tplink.rs index f7049399..f4c5a377 100644 --- a/installer/src/tplink.rs +++ b/installer/src/tplink.rs @@ -272,9 +272,19 @@ async fn handler(state: State, mut req: Request) -> Result { - Globals.models.PTModel.add({applicationName: "rayhunter-root", enableState: 1, entryId: 1, openPort: "2300-2400", openProtocol: "TCP", triggerPort: "$(busybox telnetd -l /bin/sh)", triggerProtocol: "TCP"}); + // Intentionally register rayhunter-daemon before rayhunter-root so that we are less + // likely to run into race conditions where rayhunter-root is launched, and the + // installer kills the server. In practice both HTTP requests may execute concurrently + // anyway. Globals.models.PTModel.add({applicationName: "rayhunter-daemon", enableState: 1, entryId: 2, openPort: "2400-2500", openProtocol: "TCP", triggerPort: "$(/etc/init.d/rayhunter_daemon start)", triggerProtocol: "TCP"}); - alert("Success! You can go back to the rayhunter installer."); + Globals.models.PTModel.add({applicationName: "rayhunter-root", enableState: 1, entryId: 1, openPort: "2300-2400", openProtocol: "TCP", triggerPort: "$(busybox telnetd -l /bin/sh)", triggerProtocol: "TCP"}); + + // Do not use alert(), instead replace page with success message. Using alert() will + // block the event loop in such a way that any background promises are blocked from + // progress too. For example: The HTTP requests to register our port triggers! + document.body.innerHTML = "

Success! You can go back to the rayhunter installer.

"; + + // We can stop polling now, presumably both requests are already inflight. window.clearInterval(window.rayhunterPoll); }, 1000);"#); response = Response::from_parts(parts, Body::from(Bytes::from(data))); @@ -285,6 +295,16 @@ async fn handler(state: State, mut req: Request) -> Result Result<(), Error> { + let addr = SocketAddr::from_str(&format!("{admin_ip}:23")).unwrap(); + + if telnet_send_command(addr, "true", "exit code 0", true) + .await + .is_ok() + { + println!("telnet already appears to be running"); + return Ok(()); + } + let client: HttpProxyClient = hyper_util::client::legacy::Client::<(), ()>::builder(TokioExecutor::new()) .build(HttpConnector::new()); @@ -306,8 +326,6 @@ async fn tplink_launch_telnet_v5(admin_ip: &str) -> Result<(), Error> { let handle = tokio::spawn(async move { axum::serve(listener, app).await }); - let addr = SocketAddr::from_str(&format!("{admin_ip}:23")).unwrap(); - while telnet_send_command(addr, "true", "exit code 0", true) .await .is_err() @@ -315,6 +333,9 @@ async fn tplink_launch_telnet_v5(admin_ip: &str) -> Result<(), Error> { sleep(Duration::from_millis(1000)).await; } + // give the JavaScript code some additional time to run and persist the port triggers. + sleep(Duration::from_millis(1000)).await; + handle.abort(); Ok(())