mirror of
https://github.com/SeriousBug/gandi-live-dns-rust
synced 2024-12-27 07:39:57 -06:00
almost done!
This commit is contained in:
parent
cab2ecbdf1
commit
093b99d69d
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"gandi",
|
"gandi",
|
||||||
|
"rrset",
|
||||||
"structopt"
|
"structopt"
|
||||||
]
|
]
|
||||||
}
|
}
|
98
Cargo.lock
generated
98
Cargo.lock
generated
|
@ -156,42 +156,92 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures"
|
||||||
version = "0.3.18"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27"
|
checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.18"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445"
|
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-executor"
|
||||||
version = "0.3.18"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af"
|
checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -199,6 +249,8 @@ name = "gandi-rust-dns-updater"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
|
"futures",
|
||||||
|
"json",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
@ -382,6 +434,12 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "json"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
|
|
@ -9,7 +9,9 @@ edition = "2021"
|
||||||
|
|
||||||
reqwest = { version = "0.11.7", features = ["json"] }
|
reqwest = { version = "0.11.7", features = ["json"] }
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
|
json = "0.12.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
directories = "4.0.1"
|
directories = "4.0.1"
|
||||||
structopt = "0.3.25"
|
structopt = "0.3.25"
|
||||||
tokio = { version = "1.14.0", features = ["full"] }
|
tokio = { version = "1.14.0", features = ["full"] }
|
||||||
|
futures = "0.3.17"
|
26
example.toml
26
example.toml
|
@ -1,2 +1,26 @@
|
||||||
fqdn = "my-website.example.com"
|
# Set the domain that you want to configure.
|
||||||
|
fqdn = "example.com"
|
||||||
|
# The API key to use. To get your API key, log in to Gandi, click on your user
|
||||||
|
# on the top right and click settings. Then select the "Security" tab, and
|
||||||
|
# generate an API key under "Production API Key". Paste the key here.
|
||||||
|
#
|
||||||
|
# Make sure this file config file is not readable by your user! Anyone who can
|
||||||
|
# read this key can change your domain configuration, transfer your domains, or
|
||||||
|
# otherwise do things that will cause you to be charged money.
|
||||||
api_key = "xxxxxxxxxxxxxxxxxxxxxxxx"
|
api_key = "xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
|
# For every domain or subdomain you want to update, create an entry below.
|
||||||
|
|
||||||
|
[[entry]]
|
||||||
|
# Updates A (IPv4) entry for example.com
|
||||||
|
name = "@"
|
||||||
|
|
||||||
|
[[entry]]
|
||||||
|
# Updates both A (IPv4) and AAA (IPv6) entries for other.example.com
|
||||||
|
name = "other"
|
||||||
|
types = ["A", "AAA"]
|
||||||
|
|
||||||
|
[[entry]]
|
||||||
|
# Updates A for some.example.net
|
||||||
|
name = "some"
|
||||||
|
fqdn = "example.net" # Overrides top level setting
|
||||||
|
|
|
@ -5,10 +5,32 @@ use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use crate::opts;
|
use crate::opts;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Entry {
|
||||||
|
pub name: String,
|
||||||
|
types: Option<Vec<String>>,
|
||||||
|
fqdn: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub fqdn: String,
|
fqdn: String,
|
||||||
pub api_key: String,
|
pub api_key: String,
|
||||||
|
pub entry: Vec<Entry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TYPES: Vec<&str> = vec!["A"];
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn fqdn<'c>(entry: &'c Entry, config: &'c Config) -> &'c str {
|
||||||
|
return entry.fqdn.as_ref().unwrap_or(&config.fqdn).as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> {
|
||||||
|
return entry.types.as_ref().and_then(
|
||||||
|
|ts| Some(ts.iter().map(|t| t.as_str()).collect())
|
||||||
|
).unwrap_or(DEFAULT_TYPES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> {
|
pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> {
|
||||||
|
@ -19,6 +41,17 @@ pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> {
|
||||||
return Ok(config);
|
return Ok(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validate_config(config: &Config) -> Result<(), String> {
|
||||||
|
for entry in &config.entry {
|
||||||
|
for entry_type in Config::types(&entry) {
|
||||||
|
if entry_type != "A" && entry_type != "AAA" {
|
||||||
|
return Err(format!("Entry {} has invalid type {}", entry.name, entry_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn config_path(opts: &opts::Opts) -> PathBuf {
|
pub fn config_path(opts: &opts::Opts) -> PathBuf {
|
||||||
return opts
|
return opts
|
||||||
.config
|
.config
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -1,14 +1,21 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::config::Config;
|
||||||
use reqwest::{header, Client, ClientBuilder};
|
use reqwest::{header, Client, ClientBuilder};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use tokio;
|
use tokio;
|
||||||
|
use futures;
|
||||||
mod config;
|
mod config;
|
||||||
mod opts;
|
mod opts;
|
||||||
|
|
||||||
fn gandi_api(fqdn: &str) -> String {
|
fn gandi_api_get(fqdn: &str) -> String {
|
||||||
return format!("https://api.gandi.net/v5/livedns/domains/{}/records", fqdn);
|
return format!("https://api.gandi.net/v5/livedns/domains/{}/records", fqdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gandi_api_url(fqdn: &str, rrset_name: &str, rrset_type: &str) -> String {
|
||||||
|
return format!(" https://api.gandi.net/v5/livedns/domains/{}/records/{}/{}", fqdn, rrset_name, rrset_type);
|
||||||
|
}
|
||||||
|
|
||||||
fn api_client(api_key: &str) -> Result<Client, Box<dyn Error>> {
|
fn api_client(api_key: &str) -> Result<Client, Box<dyn Error>> {
|
||||||
let client_builder = ClientBuilder::new();
|
let client_builder = ClientBuilder::new();
|
||||||
|
|
||||||
|
@ -29,13 +36,36 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let conf_path = config::config_path(&opts);
|
let conf_path = config::config_path(&opts);
|
||||||
println!("Loading config from {:#?}", conf_path);
|
println!("Loading config from {:#?}", conf_path);
|
||||||
let conf = config::load_config(conf_path)?;
|
let conf = config::load_config(conf_path)?;
|
||||||
println!("Checking domain {:#?}", conf.fqdn);
|
config::validate_config(&conf)?;
|
||||||
let url = gandi_api(&conf.fqdn);
|
|
||||||
let client = api_client(&conf.api_key)?;
|
let client = api_client(&conf.api_key)?;
|
||||||
|
|
||||||
let out = client.get(url).send().await?;
|
let ipv4 = String::from("173.89.215.91");
|
||||||
println!("Output: {:#?}", out);
|
let ipv6 = String::from("2603:6011:be07:302:79f4:50dd:6abe:be38");
|
||||||
println!("Output: {:#?}", out.json().await?);
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
|
||||||
|
for entry in &conf.entry {
|
||||||
|
for entry_type in Config::types(entry) {
|
||||||
|
let fqdn = Config::fqdn(&entry, &conf);
|
||||||
|
let url = gandi_api_url(fqdn, entry.name.as_str(), entry_type);
|
||||||
|
let ip = if entry_type.eq("A") { ipv4.as_str() } else { ipv6.as_str() };
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("rrset_values", ip);
|
||||||
|
let req = client.put(url).json(&map);
|
||||||
|
let task = tokio::task::spawn(async move {
|
||||||
|
let response = req.send().await?;
|
||||||
|
return (response.status(), response.text().await?);
|
||||||
|
});
|
||||||
|
results.push(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = futures::future::try_join_all(results).await?;
|
||||||
|
|
||||||
|
for (status, body) in results {
|
||||||
|
println!("{} - {}", status, body);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue