main.rs 1.74 KB
Newer Older
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
1
2
3
4
#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use] extern crate rocket;
use rocket::State;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
5
use rocket::http::Status;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
6
7
8
9
use rocket_contrib::json::Json;

use trust_dns_client::client::{Client, SyncClient};
use trust_dns_client::tcp::TcpClientConnection;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
10
use trust_dns_client::op::{DnsResponse, ResponseCode};
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
11
12
use trust_dns_client::rr::{DNSClass, Name, Record, RecordType};

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
13
mod models;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
14
15
mod config;

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
16
17
use models::errors::ErrorResponse;

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
18
19

#[get("/zones/<zone>/records")]
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
20
fn zone_records(client: State<SyncClient<TcpClientConnection>>, zone: String) -> Result<Json<Vec<models::dns::Record>>, ErrorResponse<()>> {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
21
    // TODO: Implement FromParam for Name
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
22
    let name = Name::from_utf8(&zone).unwrap();
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
23

24
    let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap();
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
25
26
27
28
29
30
31
32

    if response.response_code() != ResponseCode::NoError {
        return ErrorResponse::new(
            Status::NotFound,
            format!("zone {} could not be found", name.to_utf8())
        ).err()
    }

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
33
    let answers: &[Record] = response.answers();
34
    let mut records: Vec<_> = answers.to_vec().into_iter()
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
35
        .map(|record| models::dns::Record::from(record))
36
        .filter(|record| match record.rdata {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
37
            models::dns::RData::NULL { .. } | models::dns::RData::DNSSEC(_) => false,
38
39
40
41
42
            _ => true,
        }).collect();

    // AXFR response ends with SOA, we remove it so it is not doubled in the response.
    records.pop();
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
43

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
44
    Ok(Json(records))
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
45
46
47
48
49
50
51
52
53
54
55
56
}

fn main() {
    let app_config = config::load("config.toml".into());

    let conn = TcpClientConnection::new(app_config.dns_server.address).unwrap();
    let client = SyncClient::new(conn);

    rocket::ignite()
        .manage(client)
        .mount("/api/v1", routes![zone_records]).launch();
}