From 64a87534eec3c7e44ba2123c3f35d7c0043debdc Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 23 Apr 2025 03:01:35 +0200 Subject: [PATCH] fix up ci, build installer in actions --- .github/workflows/build-release.yml | 36 ++++++++++ installer/Cargo.toml | 3 + installer/src/main.rs | 104 ++++++++++++++++++++-------- 3 files changed, 116 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index e9388f3..e2129ac 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -88,6 +88,42 @@ jobs: name: rayhunter-daemon-${{ matrix.device.name }} path: target/armv7-unknown-linux-musleabihf/release/rayhunter-daemon if-no-files-found: error + build_rust_installer: + needs: + - build_rayhunter + strategy: + matrix: + platform: + - name: ubuntu-24 + os: ubuntu-latest + target: x86_64-unknown-linux-musl + - name: ubuntu-24-aarch64 + os: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + - name: macos-arm + os: macos-latest + target: aarch64-apple-darwin + - name: macos-intel + os: macos-13 + target: x86_64-apple-darwin + - name: windows-x86_64 + os: windows-latest + target: x86_64-pc-windows-gnu + runs-on: ${{ matrix.platform.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.platform.target }} + + - run: cargo build --bin installer --release --target ${{ matrix.platform.target }} --features vendor + - uses: actions/upload-artifact@v4 + with: + name: tplink-installer-${{ matrix.platform.name }} + path: target/${{ matrix.platform.target }}/release/installer${{ matrix.platform.os == 'windows-latest' && '.exe' || '' }} + if-no-files-found: error + build_release_zip: needs: - build_serial_and_check diff --git a/installer/Cargo.toml b/installer/Cargo.toml index a247755..96f16e4 100644 --- a/installer/Cargo.toml +++ b/installer/Cargo.toml @@ -3,6 +3,9 @@ name = "installer" version = "0.1.0" edition = "2024" +[features] +vendor = [] + [dependencies] anyhow = "1.0.98" clap = { version = "4.5.37", features = ["derive"] } diff --git a/installer/src/main.rs b/installer/src/main.rs index 01d37ff..d4b9227 100644 --- a/installer/src/main.rs +++ b/installer/src/main.rs @@ -2,12 +2,12 @@ use std::net::SocketAddr; use std::str::FromStr; use std::time::Duration; +use anyhow::{Context, Error}; use clap::{Parser, Subcommand}; use serde::Deserialize; use serde_json::json; -use anyhow::{Context, Error}; -use tokio::net::{TcpStream}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; use tokio::time::{sleep, timeout}; #[derive(Parser, Debug)] @@ -15,13 +15,12 @@ use tokio::time::{sleep, timeout}; struct Args { #[command(subcommand)] command: Command, - } #[derive(Subcommand, Debug)] enum Command { /// Install rayhunter on the TP-Link M7350. - InstallTplink(InstallTpLink) + InstallTplink(InstallTpLink), } #[derive(Parser, Debug)] @@ -55,21 +54,26 @@ async fn main() -> Result<(), Error> { Ok(()) } - async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { - let InstallTpLink { skip_sdcard, username, password, admin_ip } = args; + let InstallTpLink { + skip_sdcard, + username, + password, + admin_ip, + } = args; let qcmap_auth_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_auth"); let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi"); #[derive(Deserialize)] struct NonceResponse { - nonce: String + nonce: String, } let client = reqwest::Client::new(); - let NonceResponse { nonce } = client.post(&qcmap_auth_endpoint) + let NonceResponse { nonce } = client + .post(&qcmap_auth_endpoint) .body(r#"{"module":"authenticator","action":0}"#) .send() .await? @@ -83,10 +87,11 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { #[derive(Deserialize)] struct TokenResponse { - token: String + token: String, } - let TokenResponse { token } = client.post(&qcmap_auth_endpoint) + let TokenResponse { token } = client + .post(&qcmap_auth_endpoint) .json(&json!({ "module": "authenticator", "action": 1, @@ -102,7 +107,8 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { for language in &["$(busybox telnetd -l /bin/sh)", "en"] { println!("Setting language of device to {language}"); - client.post(&qcmap_web_cgi_endpoint) + client + .post(&qcmap_web_cgi_endpoint) .header("Cookie", format!("tpweb_token={token}")) .json(&json!({ "token": token, @@ -110,7 +116,8 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { "action": 1, "language": language })) - .send().await? + .send() + .await? .error_for_status()?; } @@ -128,15 +135,46 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { telnet_send_command(addr, "mkdir -p /data", "exit code 0").await?; telnet_send_command(addr, "ln -sf /mnt/card /data/rayhunter", "exit code 0").await?; - telnet_send_file(addr, "/mnt/card/config.toml", include_bytes!("../../dist/config.toml.example")).await?; - telnet_send_file(addr, "/mnt/card/rayhunter-daemon", include_bytes!("../../dist/rayhunter-daemon-tplink")).await?; - telnet_send_file(addr, "/etc/init.d/rayhunter_daemon", include_bytes!("../../dist/scripts/rayhunter_daemon")).await?; + telnet_send_file( + addr, + "/mnt/card/config.toml", + include_bytes!("../../dist/config.toml.example"), + ) + .await?; - telnet_send_command(addr, "chmod ugo+x /mnt/card/rayhunter-daemon", "exit code 0").await?; - telnet_send_command(addr, "chmod 755 /etc/init.d/rayhunter_daemon", "exit code 0").await?; + #[cfg(feature = "vendor")] + let rayhunter_daemon_bin = + include_bytes!("../../rayhunter-daemon-tplink/rayhunter-daemon-tplink"); + + #[cfg(not(feature = "vendor"))] + let rayhunter_daemon_bin = + &tokio::fs::read("target/armv7-unknown-linux-gnueabihf/release/rayhunter-daemon").await?; + + telnet_send_file(addr, "/mnt/card/rayhunter-daemon", rayhunter_daemon_bin).await?; + telnet_send_file( + addr, + "/etc/init.d/rayhunter_daemon", + include_bytes!("../../dist/scripts/rayhunter_daemon"), + ) + .await?; + + telnet_send_command( + addr, + "chmod ugo+x /mnt/card/rayhunter-daemon", + "exit code 0", + ) + .await?; + telnet_send_command( + addr, + "chmod 755 /etc/init.d/rayhunter_daemon", + "exit code 0", + ) + .await?; telnet_send_command(addr, "update-rc.d rayhunter_daemon defaults", "exit code 0").await?; - println!("Done. Rebooting device. After it's started up again, check out the web interface at http://{admin_ip}:8080"); + println!( + "Done. Rebooting device. After it's started up again, check out the web interface at http://{admin_ip}:8080" + ); telnet_send_command(addr, "reboot", "exit code 0").await?; @@ -165,21 +203,30 @@ async fn telnet_send_file(addr: SocketAddr, filename: &str, payload: &[u8]) -> R handle.await??; } - let checksum = md5::compute(payload); telnet_send_command( addr, &format!("md5sum {filename}.tmp"), - &format!("{checksum:x} {filename}.tmp") - ).await?; + &format!("{checksum:x} {filename}.tmp"), + ) + .await?; - telnet_send_command(addr, &format!("mv {filename}.tmp {filename}"), "exit code 0").await?; + telnet_send_command( + addr, + &format!("mv {filename}.tmp {filename}"), + "exit code 0", + ) + .await?; Ok(()) } -async fn telnet_send_command(addr: SocketAddr, command: &str, expected_output: &str) -> Result<(), Error> { +async fn telnet_send_command( + addr: SocketAddr, + command: &str, + expected_output: &str, +) -> Result<(), Error> { let stream = TcpStream::connect(addr).await?; let (mut reader, mut writer) = stream.into_split(); @@ -187,7 +234,7 @@ async fn telnet_send_command(addr: SocketAddr, command: &str, expected_output: & let mut buf = [0]; reader.read(&mut buf).await?; if buf[0] == b'#' { - break + break; } } @@ -199,7 +246,9 @@ async fn telnet_send_command(addr: SocketAddr, command: &str, expected_output: & let _ = timeout(Duration::from_secs(5), async { let mut buf = [0; 4096]; loop { - let Ok(bytes_read) = reader.read(&mut buf).await else { break }; + let Ok(bytes_read) = reader.read(&mut buf).await else { + break; + }; let bytes = &buf[..bytes_read]; if bytes.is_empty() { continue; @@ -208,10 +257,11 @@ async fn telnet_send_command(addr: SocketAddr, command: &str, expected_output: & read_buf.extend(bytes); if read_buf.ends_with(b"/ # ") { - break + break; } } - }).await; + }) + .await; let string = String::from_utf8_lossy(&read_buf);