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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
use std::net::IpAddr;
use axum::{
extract::Query,
response::Response,
routing::{get, post, put, delete},
Extension, Router,
};
use moka::future::Cache;
use rand::distributions::{Alphanumeric, DistString};
use serde::Deserialize;
use tower_cookies::{Cookie, Cookies};
use crate::{config::Config, database::Database, dns::packet::record::DnsRecord};
use super::{
extract::{Authorized, Body, RequestIp},
http::{json, text},
};
pub fn router() -> Router {
Router::new()
.route("/login", post(login))
.route("/domains", get(list_domains))
.route("/domains", delete(delete_domain))
.route("/records", get(get_domain))
.route("/records", put(add_record))
}
async fn list_domains(_: Authorized, Extension(database): Extension<Database>) -> Response {
let domains = match database.get_domains().await {
Ok(domains) => domains,
Err(err) => return text(500, &format!("{err}")),
};
let Ok(domains) = serde_json::to_string(&domains) else {
return text(500, "Failed to fetch domains")
};
json(200, &domains)
}
#[derive(Deserialize)]
struct DomainRequest {
domain: String,
}
async fn get_domain(
_: Authorized,
Extension(database): Extension<Database>,
Query(query): Query<DomainRequest>,
) -> Response {
let records = match database.get_domain(&query.domain).await {
Ok(records) => records,
Err(err) => return text(500, &format!("{err}")),
};
let Ok(records) = serde_json::to_string(&records) else {
return text(500, "Failed to fetch records")
};
json(200, &records)
}
async fn delete_domain(
_: Authorized,
Extension(database): Extension<Database>,
Body(body): Body,
) -> Response {
let Ok(request) = serde_json::from_str::<DomainRequest>(&body) else {
return text(400, "Missing request parameters")
};
let Ok(domains) = database.get_domains().await else {
return text(500, "Failed to delete domain")
};
if !domains.contains(&request.domain) {
return text(400, "Domain does not exist")
}
if database.delete_domain(request.domain).await.is_err() {
return text(500, "Failed to delete domain")
};
return text(204, "Successfully deleted domain")
}
async fn add_record(
_: Authorized,
Extension(database): Extension<Database>,
Body(body): Body,
) -> Response {
let Ok(record) = serde_json::from_str::<DnsRecord>(&body) else {
return text(400, "Invalid DNS record")
};
let allowed = record.get_qtype().allowed_actions();
if !allowed.1 {
return text(400, "Not allowed to create record")
}
let Ok(records) = database.get_records(&record.get_domain(), record.get_qtype()).await else {
return text(500, "Failed to complete record check");
};
if !records.is_empty() && !allowed.0 {
return text(400, "Not allowed to create duplicate record")
};
if records.contains(&record) {
return text(400, "Not allowed to create duplicate record")
}
if let Err(err) = database.add_record(record).await {
return text(500, &format!("{err}"));
}
return text(201, "Added record to database successfully");
}
#[derive(Deserialize)]
struct LoginRequest {
user: String,
pass: String,
}
async fn login(
Extension(config): Extension<Config>,
Extension(cache): Extension<Cache<String, IpAddr>>,
RequestIp(ip): RequestIp,
cookies: Cookies,
Body(body): Body,
) -> Response {
let Ok(request) = serde_json::from_str::<LoginRequest>(&body) else {
return text(400, "Missing request parameters")
};
if request.user != config.web_user || request.pass != config.web_pass {
return text(400, "Invalid credentials");
};
let token = Alphanumeric.sample_string(&mut rand::thread_rng(), 128);
cache.insert(token.clone(), ip).await;
let mut cookie = Cookie::new("auth", token);
cookie.set_secure(true);
cookie.set_http_only(true);
cookie.set_path("/");
cookies.add(cookie);
text(200, "Successfully logged in")
}
|