Browse Source

dns: allow specifying name in human-friendly format.

Dario Nieuwenhuis 2 years ago
parent
commit
ea0d4d7f46
2 changed files with 51 additions and 7 deletions
  1. 2 5
      examples/dns.rs
  2. 49 2
      src/socket/dns.rs

+ 2 - 5
examples/dns.rs

@@ -25,11 +25,13 @@ fn main() {
     let (mut opts, mut free) = utils::create_options();
     utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
+    free.push("ADDRESS");
 
     let mut matches = utils::parse_options(&opts, free);
     let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
+    let name = &matches.free[0];
 
     let neighbor_cache = NeighborCache::new(BTreeMap::new());
 
@@ -66,11 +68,6 @@ fn main() {
 
     let dns_handle = iface.add_socket(dns_socket);
 
-    //let name = b"\x08facebook\x03com\x00";
-    //let name = b"\x03www\x08facebook\x03com\x00";
-    //let name = b"\x06reddit\x03com\x00";
-    let name = b"\x09rust-lang\x03org\x00";
-
     let (socket, cx) = iface.get_socket_and_context::<DnsSocket>(dns_handle);
     let query = socket.start_query(cx, name).unwrap();
 

+ 49 - 2
src/socket/dns.rs

@@ -141,12 +141,59 @@ impl<'a> DnsSocket<'a> {
         }
     }
 
-    pub fn start_query(&mut self, cx: &mut Context, name: &[u8]) -> Result<QueryHandle> {
+    /// Start a query.
+    ///
+    /// `name` is specified in human-friendly format, such as `"rust-lang.org"`.
+    /// It accepts names both with and without trailing dot, and they're treated
+    /// the same (there's no support for DNS search path).
+    pub fn start_query(&mut self, cx: &mut Context, name: &str) -> Result<QueryHandle> {
+        let mut name = name.as_bytes();
+
+        if name.is_empty() {
+            net_trace!("invalid name: zero length");
+            return Err(Error::Illegal);
+        }
+
+        // Remove trailing dot, if any
+        if name[name.len() - 1] == b'.' {
+            name = &name[..name.len() - 1];
+        }
+
+        let mut raw_name: Vec<u8, MAX_NAME_LEN> = Vec::new();
+
+        for s in name.split(|&c| c == b'.') {
+            if s.len() > 255 {
+                net_trace!("invalid name: too long label");
+                return Err(Error::Illegal);
+            }
+            if s.is_empty() {
+                net_trace!("invalid name: zero length label");
+                return Err(Error::Illegal);
+            }
+
+            // Push label
+            raw_name.push(s.len() as u8).map_err(|_| Error::Exhausted)?;
+            raw_name
+                .extend_from_slice(s)
+                .map_err(|_| Error::Exhausted)?;
+        }
+
+        // Push terminator.
+        raw_name.push(0x00).map_err(|_| Error::Exhausted)?;
+
+        self.start_query_raw(cx, &raw_name)
+    }
+
+    /// Start a query with a raw (wire-format) DNS name.
+    /// `b"\x09rust-lang\x03org\x00"`
+    ///
+    /// You probably want to use [`start_query`] instead.
+    pub fn start_query_raw(&mut self, cx: &mut Context, raw_name: &[u8]) -> Result<QueryHandle> {
         let handle = self.find_free_query()?;
 
         self.queries[handle.0] = Some(DnsQuery {
             state: State::Pending(PendingQuery {
-                name: Vec::from_slice(name).map_err(|_| Error::Truncated)?,
+                name: Vec::from_slice(raw_name).map_err(|_| Error::Exhausted)?,
                 type_: Type::A,
                 txid: cx.rand().rand_u16(),
                 port: cx.rand().rand_source_port(),