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
use std::io;
use std::sync::{Arc, Mutex};

use rotor::GenericScope;

use {Query, Resolver, CacheEntry, Request, TimeEntry};

quick_error! {
    /// Error when creating a query
    ///
    /// Errors here are purely theoretical, should not happen in practice.
    /// It's okay to assert/unwrap on the errors, unless you pass
    /// user-generated data here.
    #[derive(Debug)]
    pub enum QueryError {
        /// Your query is too long so it doesn't fit in 512 bytes.
        /// Should not happen in practice
        TruncatedPacket {
            description("query results in packet truncation")
        }
        Net(err: io::Error) {
            description(err.description())
            display("{}", err)
            cause(err)
            from()
        }
    }
}

impl Resolver {
    pub fn query<S>(&self, query: Query, scope: &mut GenericScope)
        -> Result<Arc<Mutex<Option<Arc<CacheEntry>>>>, QueryError>
        where S: GenericScope
    {
        let ref mut res = *self.0.lock().unwrap();
        if let Some(cache) =  res.cache.get(&query).map(|x| x.clone()) {
            if scope.now() > cache.expire {
                res.cache.remove(&query);
            } else {
                // TODO(tailhook) should we trade off possible bugs for
                //                performance?
                scope.notifier().wakeup().unwrap();
                return Ok(Arc::new(Mutex::new(Some(cache.clone()))));
            }
        }
        // TODO(tailhook) implement round-robin/random server selection
        let server = 0;
        let id = try!(res.send_request(&query, server));

        let result = Arc::new(Mutex::new(None));
        let deadline = scope.now() + res.config.timeout;
        res.running.insert(id, Request {
            id: id,
            query: query,
            nameserver_index: server,
            attempts: 1,
            server: res.config.nameservers[server],
            deadline: deadline,
            notifiers: vec![(result.clone(), scope.notifier())],
        });
        res.timeouts.push(TimeEntry(deadline, id));
        res.notifier.wakeup().unwrap();  // to schedule a timeout
        Ok(result)
    }
}