Skip to content
Snippets Groups Projects
Unverified Commit aaaa6b7c authored by Paul's avatar Paul :turtle:
Browse files

very rough but api is kinda working

- no authentication yet
- no fancy webinterface yes
- no actual dns yet
parent c7ba88f1
No related branches found
No related tags found
No related merge requests found
/target
*.sqlite
*.db
......@@ -148,6 +148,19 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "cookie"
version = "0.11.3"
......@@ -159,7 +172,7 @@ dependencies = [
"hkdf",
"hmac",
"percent-encoding 2.1.0",
"rand",
"rand 0.7.3",
"sha2",
"time",
]
......@@ -221,11 +234,26 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "ffddns-web"
version = "0.1.0"
dependencies = [
"chrono",
"rand 0.8.3",
"rocket",
"rusqlite",
]
[[package]]
......@@ -248,6 +276,17 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "ghash"
version = "0.2.3"
......@@ -344,6 +383,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "language-tags"
version = "0.2.2"
......@@ -356,6 +401,22 @@ version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]]
name = "libsqlite3-sys"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d90181c2904c287e5390186be820e5ef311a3c62edebb7d6ca3d6a48ce041d"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "log"
version = "0.3.9"
......@@ -374,6 +435,15 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "lru-cache"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "matches"
version = "0.1.8"
......@@ -395,6 +465,25 @@ dependencies = [
"log 0.3.9",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
......@@ -445,6 +534,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "polyval"
version = "0.3.3"
......@@ -485,11 +580,23 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
"rand_chacha 0.3.0",
"rand_core 0.6.1",
"rand_hc 0.3.0",
]
[[package]]
......@@ -499,7 +606,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core 0.6.1",
]
[[package]]
......@@ -508,7 +625,16 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
dependencies = [
"getrandom 0.2.2",
]
[[package]]
......@@ -517,7 +643,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core 0.6.1",
]
[[package]]
......@@ -573,6 +708,29 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "rusqlite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a656821bb6317a84b257737b7934f79c0dbb7eb694710475908280ebad3e64"
dependencies = [
"bitflags",
"chrono",
"fallible-iterator",
"fallible-streaming-iterator",
"libsqlite3-sys",
"lru-cache",
"memchr",
"serde_json",
"time",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "safemem"
version = "0.3.3"
......@@ -585,6 +743,17 @@ version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
[[package]]
name = "serde_json"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha2"
version = "0.8.2"
......@@ -739,6 +908,12 @@ dependencies = [
"percent-encoding 1.0.1",
]
[[package]]
name = "vcpkg"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
[[package]]
name = "version_check"
version = "0.1.5"
......
......@@ -20,6 +20,9 @@ Daten speichern müssen. Ein paar Stichpunkte:
- Token wird gelöscht
- Domain kann wieder registriert werden
Um Spam zu vermeiden sollten wir ein captcha in irgendeiner Form nutzen.
Über das Token könnte man auch zusätzliche Dinge realisieren, die Authentifizierung benötigen.
Z.B. ein simples Webinterface um einzusehen, wann die Domain zuletzt geupdatet wurde oder um sie
manuell freizugeben.
......@@ -8,3 +8,6 @@ edition = "2018"
[dependencies]
rocket = "0.4.7"
rusqlite = {version = "0.21", features = ["chrono", "serde_json"]}
chrono = "0.4.19"
rand = "0.8.3"
use rusqlite as sqlite;
use sqlite::params;
use std::path::PathBuf;
use chrono::{Utc, DateTime};
use std::net::{Ipv4Addr, Ipv6Addr};
pub struct Database {
conn: sqlite::Connection,
}
unsafe impl Send for Database {}
unsafe impl Sync for Database {}
impl Database {
pub fn new(path: PathBuf) -> Self {
let conn: sqlite::Connection = sqlite::Connection::open(&path).unwrap();
conn.execute_batch(include_str!("init.sql")).unwrap();
Database { conn }
}
pub fn get_all_domains(&self) -> Vec<Domain> {
let mut stmt: sqlite::Statement = self.conn.prepare("SELECT * FROM domains").unwrap();
stmt.query_map(
params![],
|row| Ok(Domain::from_row(row))
).unwrap().map(|x| x.unwrap()).collect()
}
pub fn insert_new_domain(&self, d: &Domain) {
self.conn.execute(
"INSERT INTO domains VALUES ($1, $2, $3, $4, $5)",
params![d.domainname, d.token, d.lastupdate, d.ipv4.map(|x| x.to_string()), d.ipv6.map(|x| x.to_string())]
).unwrap();
}
pub fn get_domain(&self, domain: &String) -> Option<Domain> {
let r: sqlite::Result<_> = self.conn.query_row_and_then(
"SELECT * FROM domains WHERE domainname=$1",
params![domain],
|row| Ok(Domain::from_row(row))
);
match r {
Err(_) => None,
Ok(o) => Some(o)
}
}
pub fn remove_domain(&self, d: String) {
self.conn.execute(
"DELETE FROM domains WHERE domainname=$1",
params![d]
).unwrap();
}
pub fn update_lastupdate(&self, d: &String, lastupdate: DateTime<Utc>) {
self.conn.execute(
"UPDATE domains SET lastupdate=$2 WHERE domainname=$1",
params![d, lastupdate]
).unwrap();
}
pub fn update_ipv4(&self, d: &String, addr: Ipv4Addr) {
self.conn.execute(
"UPDATE domains SET ipv4=$2 WHERE domainname=$1",
params![d, addr.to_string()]
).unwrap();
}
pub fn update_ipv6(&self, d: &String, addr: Ipv6Addr) {
self.conn.execute(
"UPDATE domains SET ipv6=$2 WHERE domainname=$1",
params![d, addr.to_string()]
).unwrap();
}
}
#[derive(Debug, Clone)]
pub struct Domain {
pub domainname: String,
pub token: String,
pub lastupdate: Option<DateTime<Utc>>,
pub ipv4: Option<Ipv4Addr>,
pub ipv6: Option<Ipv6Addr>,
}
impl Domain {
fn from_row(row: &sqlite::Row) -> Self {
Self {
domainname: row.get("domainname").unwrap(),
token: row.get("token").unwrap(),
lastupdate: row.get("lastupdate").unwrap(),
ipv4: row.get::<_, Option<String>>("ipv4").unwrap().map(|x| x.parse().unwrap()),
ipv6: row.get::<_, Option<String>>("ipv6").unwrap().map(|x| x.parse().unwrap()),
}
}
}
CREATE TABLE IF NOT EXISTS domains (
domainname TEXT PRIMARY KEY,
token TEXT NOT NULL,
lastupdate TEXT,
ipv4 TEXT,
ipv6 TEXT
);
#![feature(proc_macro_hygiene, decl_macro)]
use rocket;
use rocket::get;
use rocket::routes;
mod db;
use chrono::{DateTime, Utc};
use db::{
Database,
Domain,
};
use rocket::{
self,
get,
post,
routes,
State,
};
use rocket::request::{
Request,
FromRequest,
Outcome,
};
use std::fmt::{self, Display};
use std::net::IpAddr;
use rand;
#[get("/update?<key>&<address>")]
fn index(key: String, address: Option<String>) -> String {
format!("{}: updating ip address to {:?}", key, address)
pub struct ClientIp(IpAddr);
impl ClientIp {
pub fn inner(&self) -> &IpAddr {
let ClientIp(ip) = self;
ip
}
pub fn into_inner(self) -> IpAddr {
let ClientIp(ip) = self;
ip
}
}
impl Display for ClientIp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner().to_string())
}
}
impl<'a, 'r> FromRequest<'a, 'r> for ClientIp {
type Error = String;
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
let ip = request.client_ip().unwrap();
Outcome::Success(ClientIp(ip))
}
}
#[derive(Debug, Clone)]
pub struct DomainUpdate {
domain: String,
ip: IpAddr
}
#[get("/update?<token>&<domain>&<ip>")]
fn update(db: State<Database>, clientip: ClientIp, token: String, domain: String, ip: Option<String>) -> String {
let new_ip: IpAddr = {
if let Some(iip) = ip {
iip.parse::<IpAddr>().unwrap()
}
else {
clientip.into_inner()
}
};
let d = db.get_domain(&domain).unwrap();
if d.token != token {
return "not a valid token".to_string();
}
match new_ip {
IpAddr::V4(addr) => db.update_ipv4(&domain, addr),
IpAddr::V6(addr) => db.update_ipv6(&domain, addr),
}
db.update_lastupdate(&domain, Utc::now());
format!("{} updated to {:?}", domain, new_ip)
}
#[get("/create?<domain>")]
fn create(db: State<Database>, domain: String) -> String {
let token = generate_token();
let d = Domain {
domainname: domain.clone(),
token: token.clone(),
lastupdate: None,
ipv4: None,
ipv6: None
};
db.insert_new_domain(&d);
format!("your token for {}: {}", domain, token)
}
#[get("/status?<domain>")]
fn status(db: State<Database>, domain: String) -> String {
let domaininfo = match db.get_domain(&domain) {
None => return "domain not found".to_string(),
Some(r) => r,
};
format!("{:#?}", domaininfo)
}
fn main() {
let db = db::Database::new("./ffddns.sqlite".into());
rocket::ignite()
.mount("/", routes![index])
.mount("/", routes![
update,
create,
status
])
.manage(db)
.launch();
}
fn generate_token() -> String {
let mut token = String::new();
for _ in 0..8 {
token.push_str(&format!("{:02x}", rand::random::<u8>()));
}
token
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment