2021-07-01 18:45:49 +00:00
|
|
|
use std::convert::TryInto;
|
2022-03-03 23:44:29 +00:00
|
|
|
use serde_json::json;
|
2021-05-02 13:56:42 +00:00
|
|
|
use rocket::Response;
|
2021-04-02 20:09:51 +00:00
|
|
|
use rocket::http::Status;
|
|
|
|
|
|
|
|
use rocket_contrib::json::Json;
|
|
|
|
|
2022-03-04 12:08:03 +00:00
|
|
|
use trust_dns_client::client::ClientHandle;
|
2021-04-05 22:56:15 +00:00
|
|
|
use trust_dns_client::op::ResponseCode;
|
2021-04-05 21:21:40 +00:00
|
|
|
use trust_dns_client::rr::{DNSClass, RecordType};
|
2022-03-04 12:08:03 +00:00
|
|
|
|
|
|
|
use crate::{dns::{self, trust_dns_types}, DbConn};
|
2021-04-03 06:16:54 +00:00
|
|
|
use crate::models::errors::{ErrorResponse, make_500};
|
2022-03-04 12:08:03 +00:00
|
|
|
use crate::models::user::{LocalUser, UserInfo};
|
|
|
|
use crate::models::zone::{Zone, AddZoneMemberRequest, CreateZoneRequest};
|
|
|
|
use crate::dns::message::DnsMessage;
|
|
|
|
use crate::dns::message::MessageError;
|
2021-04-02 20:09:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
#[get("/zones/<zone>/records")]
|
2021-04-03 06:16:54 +00:00
|
|
|
pub async fn get_zone_records(
|
2022-03-04 12:08:03 +00:00
|
|
|
mut client: dns::client::DnsClient,
|
2021-04-05 22:56:15 +00:00
|
|
|
conn: DbConn,
|
2021-04-02 21:12:29 +00:00
|
|
|
user_info: Result<UserInfo, ErrorResponse>,
|
2022-03-04 12:08:03 +00:00
|
|
|
zone: dns::name::AbsoluteName
|
|
|
|
) -> Result<Json<Vec<dns::record::Record>>, ErrorResponse> {
|
2021-04-02 20:09:51 +00:00
|
|
|
|
2021-04-05 22:56:15 +00:00
|
|
|
let user_info = user_info?;
|
2021-05-02 15:19:32 +00:00
|
|
|
let zone_name = zone.to_string();
|
2021-04-05 22:56:15 +00:00
|
|
|
|
2021-05-02 15:19:32 +00:00
|
|
|
conn.run(move |c| {
|
|
|
|
if user_info.is_admin() {
|
|
|
|
Zone::get_by_name(c, &zone_name)
|
|
|
|
} else {
|
2021-05-02 13:56:42 +00:00
|
|
|
user_info.get_zone(c, &zone_name)
|
2021-05-02 15:19:32 +00:00
|
|
|
}
|
|
|
|
}).await?;
|
2021-04-05 22:56:15 +00:00
|
|
|
|
2022-03-04 12:50:57 +00:00
|
|
|
let records: Vec<_> = match client.get_records(zone.clone(), DNSClass::IN).await {
|
|
|
|
Ok(records) => records.into_iter().map(dns::record::Record::from).collect(),
|
2021-04-02 20:09:51 +00:00
|
|
|
|
2022-03-04 12:50:57 +00:00
|
|
|
Err(MessageError::ResponceNotOk(code)) => {
|
|
|
|
println!("Querrying AXFR of zone {} failed with code {}", *zone, code);
|
|
|
|
return ErrorResponse::new(
|
|
|
|
Status::NotFound,
|
|
|
|
"Zone could not be found".into()
|
|
|
|
).with_details(json!({
|
|
|
|
"zone_name": zone.to_utf8()
|
|
|
|
})).err();
|
|
|
|
},
|
|
|
|
Err(err) => { return make_500(err).err(); },
|
|
|
|
};
|
2021-04-02 20:09:51 +00:00
|
|
|
|
|
|
|
Ok(Json(records))
|
|
|
|
}
|
2021-05-02 13:56:42 +00:00
|
|
|
|
2021-07-01 18:45:49 +00:00
|
|
|
#[post("/zones/<zone>/records", data = "<new_records>")]
|
|
|
|
pub async fn create_zone_records(
|
2022-03-04 12:08:03 +00:00
|
|
|
mut client: dns::client::DnsClient,
|
2021-07-01 18:45:49 +00:00
|
|
|
conn: DbConn,
|
|
|
|
user_info: Result<UserInfo, ErrorResponse>,
|
2022-03-04 12:08:03 +00:00
|
|
|
zone: dns::name::AbsoluteName,
|
|
|
|
new_records: Json<Vec<dns::record::Record>>
|
2021-07-01 18:45:49 +00:00
|
|
|
) -> Result<Json<()>, ErrorResponse> {
|
|
|
|
|
|
|
|
let user_info = user_info?;
|
|
|
|
let zone_name = zone.to_utf8();
|
|
|
|
|
|
|
|
conn.run(move |c| {
|
|
|
|
if user_info.is_admin() {
|
|
|
|
Zone::get_by_name(c, &zone_name)
|
|
|
|
} else {
|
|
|
|
user_info.get_zone(c, &zone_name)
|
|
|
|
}
|
|
|
|
}).await?;
|
2022-03-03 23:44:29 +00:00
|
|
|
|
2021-07-01 18:45:49 +00:00
|
|
|
// TODO: What about relative names (also in cnames and stuff)
|
2022-03-03 23:44:29 +00:00
|
|
|
let mut bad_records = Vec::new();
|
|
|
|
let mut records: Vec<trust_dns_types::Record> = Vec::new();
|
|
|
|
|
|
|
|
for record in new_records.into_inner().into_iter() {
|
|
|
|
let this_record = record.clone();
|
|
|
|
if let Ok(record) = record.try_into() {
|
|
|
|
records.push(record);
|
|
|
|
} else {
|
|
|
|
bad_records.push(this_record.clone());
|
|
|
|
}
|
|
|
|
}
|
2021-07-01 18:45:49 +00:00
|
|
|
|
2022-03-03 23:44:29 +00:00
|
|
|
if !bad_records.is_empty() {
|
|
|
|
return ErrorResponse::new(
|
|
|
|
Status::BadRequest,
|
|
|
|
"Record list contains records that could not been parsed into DNS records".into()
|
|
|
|
).with_details(
|
|
|
|
json!({
|
|
|
|
"zone_name": zone.to_utf8(),
|
|
|
|
"records": bad_records
|
|
|
|
})
|
|
|
|
).err();
|
|
|
|
}
|
|
|
|
|
2022-03-04 12:08:03 +00:00
|
|
|
let response = match client.add_records(zone.clone(), DNSClass::IN, records) {
|
|
|
|
Ok(query) => query.await.map_err(make_500)?,
|
|
|
|
Err(MessageError::RecordNotInZone { zone, class, mismatched_class, mismatched_zone}) => {
|
|
|
|
return ErrorResponse::new(
|
|
|
|
Status::BadRequest,
|
|
|
|
"Record list contains records that do not belong to the zone".into()
|
|
|
|
).with_details(
|
|
|
|
json!({
|
|
|
|
"zone_name": zone.to_utf8(),
|
|
|
|
"class": dns::class::DNSClass::from(class),
|
|
|
|
"mismatched_class": mismatched_class.into_iter().map(|r| r.clone().into()).collect::<Vec<dns::record::Record>>(),
|
|
|
|
"mismatched_zone": mismatched_zone.into_iter().map(|r| r.clone().into()).collect::<Vec<dns::record::Record>>(),
|
|
|
|
})
|
|
|
|
).err();
|
2022-03-04 12:50:57 +00:00
|
|
|
},
|
|
|
|
Err(e) => return make_500(e).err()
|
2021-07-01 18:45:49 +00:00
|
|
|
};
|
|
|
|
|
2022-03-03 23:44:29 +00:00
|
|
|
// TODO: better error handling
|
|
|
|
if response.response_code() != ResponseCode::NoError {
|
|
|
|
println!("Update of zone {} failed with code {}", *zone, response.response_code());
|
|
|
|
return ErrorResponse::new(
|
|
|
|
Status::NotFound,
|
|
|
|
"Update of zone failed".into()
|
|
|
|
).with_details(json!({
|
|
|
|
"zone_name": zone.to_utf8()
|
|
|
|
})).err();
|
|
|
|
}
|
|
|
|
|
2021-07-01 18:45:49 +00:00
|
|
|
Ok(Json(()))
|
|
|
|
}
|
|
|
|
|
2021-05-02 13:56:42 +00:00
|
|
|
#[get("/zones")]
|
|
|
|
pub async fn get_zones(
|
|
|
|
conn: DbConn,
|
|
|
|
user_info: Result<UserInfo, ErrorResponse>,
|
|
|
|
) -> Result<Json<Vec<Zone>>, ErrorResponse> {
|
|
|
|
let user_info = user_info?;
|
|
|
|
|
|
|
|
let zones = conn.run(move |c| {
|
|
|
|
if user_info.is_admin() {
|
|
|
|
Zone::get_all(c)
|
|
|
|
} else {
|
|
|
|
user_info.get_zones(c)
|
|
|
|
}
|
|
|
|
}).await?;
|
|
|
|
|
|
|
|
Ok(Json(zones))
|
|
|
|
}
|
|
|
|
|
2021-05-02 15:19:32 +00:00
|
|
|
#[post("/zones", data = "<zone_request>")]
|
|
|
|
pub async fn create_zone(
|
|
|
|
conn: DbConn,
|
2022-03-04 12:08:03 +00:00
|
|
|
mut client: dns::client::DnsClient,
|
2021-05-02 15:19:32 +00:00
|
|
|
user_info: Result<UserInfo, ErrorResponse>,
|
|
|
|
zone_request: Json<CreateZoneRequest>,
|
|
|
|
) -> Result<Json<Zone>, ErrorResponse> {
|
|
|
|
user_info?.check_admin()?;
|
|
|
|
|
|
|
|
// Check if the zone exists in the DNS server
|
|
|
|
let response = {
|
|
|
|
let query = client.query(zone_request.name.clone(), DNSClass::IN, RecordType::SOA);
|
|
|
|
query.await.map_err(make_500)?
|
|
|
|
};
|
|
|
|
|
|
|
|
if response.response_code() != ResponseCode::NoError {
|
|
|
|
println!("Querrying SOA of zone {} failed with code {}", *zone_request.name, response.response_code());
|
|
|
|
return ErrorResponse::new(
|
|
|
|
Status::NotFound,
|
|
|
|
format!("Zone {} could not be found", *zone_request.name)
|
|
|
|
).err()
|
|
|
|
}
|
|
|
|
|
|
|
|
let zone = conn.run(move |c| {
|
|
|
|
Zone::create_zone(c, zone_request.into_inner())
|
|
|
|
}).await?;
|
|
|
|
|
|
|
|
Ok(Json(zone))
|
|
|
|
}
|
|
|
|
|
2021-05-02 13:56:42 +00:00
|
|
|
|
|
|
|
#[post("/zones/<zone>/members", data = "<zone_member_request>")]
|
|
|
|
pub async fn add_member_to_zone<'r>(
|
|
|
|
conn: DbConn,
|
2022-03-04 12:08:03 +00:00
|
|
|
zone: dns::name::AbsoluteName,
|
2021-05-02 13:56:42 +00:00
|
|
|
user_info: Result<UserInfo, ErrorResponse>,
|
|
|
|
zone_member_request: Json<AddZoneMemberRequest>
|
|
|
|
) -> Result<Response<'r>, ErrorResponse> {
|
|
|
|
let user_info = user_info?;
|
|
|
|
let zone_name = zone.to_utf8();
|
|
|
|
|
|
|
|
conn.run(move |c| {
|
|
|
|
let zone = if user_info.is_admin() {
|
|
|
|
Zone::get_by_name(c, &zone_name)
|
|
|
|
} else {
|
|
|
|
user_info.get_zone(c, &zone_name)
|
|
|
|
}?;
|
|
|
|
|
|
|
|
let new_member = LocalUser::get_user_by_uuid(c, &zone_member_request.id)?;
|
|
|
|
zone.add_member(&c, &new_member)
|
|
|
|
}).await?;
|
|
|
|
|
|
|
|
Response::build()
|
|
|
|
.status(Status::Created) // TODO: change this?
|
|
|
|
.ok()
|
|
|
|
}
|