fix up ci, build installer in actions

This commit is contained in:
Markus Unterwaditzer
2025-04-23 03:01:35 +02:00
committed by Cooper Quintin
parent 4a94545498
commit 64a87534ee
3 changed files with 116 additions and 27 deletions
+36
View File
@@ -88,6 +88,42 @@ jobs:
name: rayhunter-daemon-${{ matrix.device.name }} name: rayhunter-daemon-${{ matrix.device.name }}
path: target/armv7-unknown-linux-musleabihf/release/rayhunter-daemon path: target/armv7-unknown-linux-musleabihf/release/rayhunter-daemon
if-no-files-found: error 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: build_release_zip:
needs: needs:
- build_serial_and_check - build_serial_and_check
+3
View File
@@ -3,6 +3,9 @@ name = "installer"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[features]
vendor = []
[dependencies] [dependencies]
anyhow = "1.0.98" anyhow = "1.0.98"
clap = { version = "4.5.37", features = ["derive"] } clap = { version = "4.5.37", features = ["derive"] }
+77 -27
View File
@@ -2,12 +2,12 @@ use std::net::SocketAddr;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use anyhow::{Context, Error};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use anyhow::{Context, Error};
use tokio::net::{TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::{sleep, timeout}; use tokio::time::{sleep, timeout};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -15,13 +15,12 @@ use tokio::time::{sleep, timeout};
struct Args { struct Args {
#[command(subcommand)] #[command(subcommand)]
command: Command, command: Command,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum Command { enum Command {
/// Install rayhunter on the TP-Link M7350. /// Install rayhunter on the TP-Link M7350.
InstallTplink(InstallTpLink) InstallTplink(InstallTpLink),
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -55,21 +54,26 @@ async fn main() -> Result<(), Error> {
Ok(()) Ok(())
} }
async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { 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_auth_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_auth");
let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi"); let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi");
#[derive(Deserialize)] #[derive(Deserialize)]
struct NonceResponse { struct NonceResponse {
nonce: String nonce: String,
} }
let client = reqwest::Client::new(); 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}"#) .body(r#"{"module":"authenticator","action":0}"#)
.send() .send()
.await? .await?
@@ -83,10 +87,11 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> {
#[derive(Deserialize)] #[derive(Deserialize)]
struct TokenResponse { struct TokenResponse {
token: String token: String,
} }
let TokenResponse { token } = client.post(&qcmap_auth_endpoint) let TokenResponse { token } = client
.post(&qcmap_auth_endpoint)
.json(&json!({ .json(&json!({
"module": "authenticator", "module": "authenticator",
"action": 1, "action": 1,
@@ -102,7 +107,8 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> {
for language in &["$(busybox telnetd -l /bin/sh)", "en"] { for language in &["$(busybox telnetd -l /bin/sh)", "en"] {
println!("Setting language of device to {language}"); 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}")) .header("Cookie", format!("tpweb_token={token}"))
.json(&json!({ .json(&json!({
"token": token, "token": token,
@@ -110,7 +116,8 @@ async fn main_tplink(args: InstallTpLink) -> Result<(), Error> {
"action": 1, "action": 1,
"language": language "language": language
})) }))
.send().await? .send()
.await?
.error_for_status()?; .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, "mkdir -p /data", "exit code 0").await?;
telnet_send_command(addr, "ln -sf /mnt/card /data/rayhunter", "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(
telnet_send_file(addr, "/mnt/card/rayhunter-daemon", include_bytes!("../../dist/rayhunter-daemon-tplink")).await?; addr,
telnet_send_file(addr, "/etc/init.d/rayhunter_daemon", include_bytes!("../../dist/scripts/rayhunter_daemon")).await?; "/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?; #[cfg(feature = "vendor")]
telnet_send_command(addr, "chmod 755 /etc/init.d/rayhunter_daemon", "exit code 0").await?; 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?; 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?; 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??; handle.await??;
} }
let checksum = md5::compute(payload); let checksum = md5::compute(payload);
telnet_send_command( telnet_send_command(
addr, addr,
&format!("md5sum {filename}.tmp"), &format!("md5sum {filename}.tmp"),
&format!("{checksum:x} {filename}.tmp") &format!("{checksum:x} {filename}.tmp"),
).await?; )
.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(()) 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 stream = TcpStream::connect(addr).await?;
let (mut reader, mut writer) = stream.into_split(); 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]; let mut buf = [0];
reader.read(&mut buf).await?; reader.read(&mut buf).await?;
if buf[0] == b'#' { 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 _ = timeout(Duration::from_secs(5), async {
let mut buf = [0; 4096]; let mut buf = [0; 4096];
loop { 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]; let bytes = &buf[..bytes_read];
if bytes.is_empty() { if bytes.is_empty() {
continue; continue;
@@ -208,10 +257,11 @@ async fn telnet_send_command(addr: SocketAddr, command: &str, expected_output: &
read_buf.extend(bytes); read_buf.extend(bytes);
if read_buf.ends_with(b"/ # ") { if read_buf.ends_with(b"/ # ") {
break break;
} }
} }
}).await; })
.await;
let string = String::from_utf8_lossy(&read_buf); let string = String::from_utf8_lossy(&read_buf);