summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: abffc91541b6db7c913537fb0e1a6b458f5a6b28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use anyhow::anyhow;
use scraper::{Html, Selector};

async fn form_get(
    client: &reqwest::Client,
    addr: &str,
    form: &str,
) -> anyhow::Result<Vec<(String, String)>> {
    Ok(
        Html::parse_document(&client.get(addr).send().await?.text().await?)
            .select(&Selector::parse(form).unwrap())
            .next()
            .ok_or(anyhow!("form for selector {form} not found"))?
            .select(&Selector::parse("input").unwrap())
            .map(|item| item.value())
            .flat_map(|item| {
                if matches!(item.attr("type"), Some(x) if x == "checkbox" || x == "submit") {
                    return None;
                }

                Some((
                    item.attr("name")?.into(),
                    item.attr("value").unwrap_or_default().into(),
                ))
            })
            .collect(),
    )
}

// this is dumb but it works
// we don't use a hashmap because keys may be duplicated
// this overwrites the entry with a certain key
fn form_set(form: &mut Vec<(String, String)>, key: &str, value: String) {
    for entry in form.iter_mut() {
        if entry.0 == key {
            entry.1 = value;
            return;
        }
    }

    form.push((key.into(), value));
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenv::dotenv()?;

    use std::env;

    let target_host = env::var("DDNS_TARGET_HOST")?;
    let username = env::var("DDNS_USERNAME")?;
    let password = env::var("DDNS_PASSWORD")?;
    let domain_id = env::var("DDNS_DOMAIN_ID")?;

    let target_ip = *dns_lookup::lookup_host(&target_host)?
        .first()
        .ok_or(anyhow!("no ip found for {target_host}"))?;

    let client = reqwest::ClientBuilder::new().cookie_store(true).build()?;

    let mut login = form_get(
        &client,
        "https://cp.istanco.net/clientarea.php",
        r#"form[action="https://cp.istanco.net/dologin.php"]"#,
    )
    .await?;

    form_set(&mut login, "username", username);
    form_set(&mut login, "password", password);

    client
        .post("https://cp.istanco.net/dologin.php")
        .form(&login)
        .send()
        .await?;

    let manage_url = format!("https://cp.istanco.net/index.php?m=br_dnsmanager&id={domain_id}");

    let mut manage = form_get(&client, &manage_url, r#"form[name="br-dnsrecord-manager"]"#).await?;

    form_set(&mut manage, "value[]", target_ip.to_string());
    form_set(&mut manage, "btnSave", "Save Changes".into());

    client.post(&manage_url).form(&manage).send().await?;

    Ok(())
}