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

View File

@@ -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);