Skip to content

Commit

Permalink
added Lua scripting
Browse files Browse the repository at this point in the history
  • Loading branch information
dandyvica committed Jan 26, 2024
1 parent 42e6356 commit 7eb7239
Show file tree
Hide file tree
Showing 39 changed files with 639 additions and 227 deletions.
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
"./Cargo.toml",
"./dns/Cargo.toml",
],
"rust-analyzer.showUnlinkedFileNotification": false
"rust-analyzer.showUnlinkedFileNotification": false,
"Lua.diagnostics.globals": [
"dns",
"info"
]
}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ members = [
"show"]
resolver = "2"


101 changes: 91 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,99 @@
[![Actions](https://github.com/dandyvica/siphash_c_d/actions/workflows/rust.yml/badge.svg)](https://github.com/dandyvica/siphash_c_d/actions/workflows/rust.yml)

Note: this is currently under development in my free time. Version is not even yet 0.1.0
but hope to release executables in a couple of weeks.

# dqy
A DNS query tool
A DNS query tool inspired by _dig_, _drill_ and _dog_.

## Features
This tool is written in pure Rust with the following features:

* depends only on _rustls_ (no _openssl_)
* support upd, tcp, DoT and DoH protocols (DoQ to come)
* available on: Linux (x64, ARM64, musl), Window, MacOs
* possible outputs:
* plain vanilla ascii
* Json
* ability to call a Lua script to fine tune the output

## Supported resource records
The following list of RRs is supported:

* A
* AAAA
* AFSDB
* APL
* CAA
* CDNSKEY
* CDS
* CERT
* CNAME
* CSYNC
* DHCID
* DLV
* DNAME
* DNSKEY
* DS
* EUI48
* EUI64
* HINFO
* HIP (*)
* HTTPS (*)
* IPSECKEY
* KX
* LOC
* MX
* NAPTR
* NS
* NSEC
* NSEC3
* NSEC3PARAM
* OPENPGPKEY
* OPT
* PTR
* RP
* RRSIG
* SMIMEA
* SOA
* SRV
* SSHFP
* SVCB (*)
* TLSA
* TXT
* UNKNOWN
* URI
* ZONEMD

Those with (*) are not yet fully tested.

## JSON support
The _--json_ and _--json-pretty_ options allows to display output data in JSON format with key:

* messages: list of messages
* info: meta-info like elpased time, endpoint address etc

## Lua scripting support
Using _-l <Lua source file>_, all DNS data are sent as global variables to the Lua interpreter which makes it possible to format the output in a very flexible manner.

## Roadmap
Following is a tentative roadmap:

* v0.2: ipv6 support
* v0.3: trace option
* v0.4: DNS over Quic
* v0.5: OPT options full support
* ...

## Usage
Just type:

```
$ dqy --help
```

## List of RFCs
## Examples

* [NSEC3 & NSEC3PARAM](https://datatracker.ietf.org/doc/html/rfc5155)
* [Handling of Unknown DNS Resource Record (RR) Types](https://datatracker.ietf.org/doc/html/rfc3597)
* [OPT Padding](https://datatracker.ietf.org/doc/html/rfc7830)
* [NSEC](https://datatracker.ietf.org/doc/html/rfc3845)

## Tests
* dig +dnssec NSEC3 gggg.icann.org.
* dqy @1.1.1.1 NSEC gggg.icann.org. --dnssec --tls => Padding option
dig +dnssec @ns6.cloudflare.com NSEC ocsp.cloudflare.com => A HINFO MX TXT AAAA LOC SRV NAPTR CERT SSHFP RRSIG NSEC TLSA SMIMEA HIP OPENPGPKEY TYPE64 TYPE65 URI CAA


108 changes: 65 additions & 43 deletions args/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Manage command line arguments here.
use std::fs;
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;

Expand Down Expand Up @@ -29,7 +31,9 @@ macro_rules! set_unset_flag {
};
}

/// This structure holds the command line arguments.
//───────────────────────────────────────────────────────────────────────────────────
// This structure holds the command line arguments.
//───────────────────────────────────────────────────────────────────────────────────
#[derive(Debug, Default)]
pub struct CliOptions {
// DNS protocol options
Expand Down Expand Up @@ -59,7 +63,7 @@ impl CliOptions {

let (without_dash, with_dash) = match dash_pos {
Some(pos) => (&args[0..pos], &args[pos..]),
None => (&args[..], &[] as &[String]),
None => (args, &[] as &[String]),
};

trace!("options without dash:{:?}", without_dash);
Expand Down Expand Up @@ -347,33 +351,43 @@ impl CliOptions {
)
//───────────────────────────────────────────────────────────────────────────────────
// Display options
//───────────────────────────────────────────────────────────────────────────────────
//───────────────────────────────────────────────────────────────────────────────────
.arg(
Arg::new("no-add")
.long("no-add")
.long_help("Don't show the additional RR section. Showed by default.")
Arg::new("json")
.short('j')
.long("json")
.long_help("Results are rendered as a JSON formatted string.")
.action(ArgAction::SetTrue)
.help_heading("Display options")
)
.arg(
Arg::new("no-auth")
.long("no-auth")
.long_help("Don't show the authorative RR section. Showed by default.")
Arg::new("json-pretty")
.long("json-pretty")
.long_help("Results are rendered as a JSON pretty-formatted string.")
.action(ArgAction::SetTrue)
.help_heading("Display options")
)
.arg(
Arg::new("json")
.short('j')
.long("json")
.long_help("Results are rendered as a JSON formatted string.")
Arg::new("lua")
.short('l')
.long("lua")
.long_help("Name of a lua script that will be called to display results.")
.action(ArgAction::Set)
.value_name("lua")
.value_parser(clap::value_parser!(PathBuf))
.help_heading("Display options")
)
.arg(
Arg::new("no-add")
.long("no-add")
.long_help("Don't show the additional RR section. Showed by default.")
.action(ArgAction::SetTrue)
.help_heading("Display options")
)
.arg(
Arg::new("json-pretty")
.long("json-pretty")
.long_help("Results are rendered as a JSON pretty-formatted string.")
Arg::new("no-auth")
.long("no-auth")
.long_help("Don't show the authorative RR section. Showed by default.")
.action(ArgAction::SetTrue)
.help_heading("Display options")
)
Expand Down Expand Up @@ -484,10 +498,17 @@ impl CliOptions {
}
// fetch OS resolvers
else {
//????
let resolvers = ResolverList::new().unwrap();
options.transport.end_point =
EndPoint::from((&resolvers.to_ip_list()[..], options.transport.port));
let resolvers = ResolverList::new()?;
let ip_list = resolvers.to_ip_list();
options.transport.end_point = EndPoint::from((&ip_list[..], options.transport.port));

// TLS needs are server name or ip address
// in that case, get the first ip address from resolvers
if options.transport.transport_mode == Protocol::DoT {
let server = ip_list[0].to_string();
options.transport.end_point =
EndPoint::from((server.as_str(), options.transport.port));
}
}

//───────────────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -525,7 +546,7 @@ impl CliOptions {

// this will convert to ["2001", "0470", "0030", "0084", "e276", "63ff", "fe72", "3900"]
let split = ip
.split(":") // split accordsing to ":"
.split(':') // split accordsing to ":"
.map(|x| format!("{:0>4}", x)) // convert to string with heading 0
.collect::<Vec<String>>()
.join(""); // and finally join to get a whole string
Expand All @@ -541,9 +562,6 @@ impl CliOptions {
//───────────────────────────────────────────────────────────────────────────────────
// Flags
//───────────────────────────────────────────────────────────────────────────────────
// by default, we want recursive queries, other flags are unset
//options.flags.recursion_desired = true;

// all flags options are set to false except RD
// set
if let Some(v) = matches.get_many::<String>("set") {
Expand Down Expand Up @@ -576,25 +594,17 @@ impl CliOptions {
options.edns.no_opt = matches.get_flag("no-opt");
options.edns.dnssec = matches.get_flag("dnssec");
options.edns.nsid = matches.get_flag("nsid");
options.edns.padding = matches.get_one::<u16>("padding").and_then(|v| Some(*v));

options.edns.dau = if let Some(v) = matches.get_many::<u8>("dau") {
Some(v.copied().collect::<Vec<u8>>())
} else {
None
};

options.edns.dhu = if let Some(v) = matches.get_many::<u8>("dhu") {
Some(v.copied().collect::<Vec<u8>>())
} else {
None
};

options.edns.n3u = if let Some(v) = matches.get_many::<u8>("n3u") {
Some(v.copied().collect::<Vec<u8>>())
} else {
None
};
options.edns.padding = matches.get_one::<u16>("padding").copied();

options.edns.dau = matches
.get_many::<u8>("dau")
.map(|v| v.copied().collect::<Vec<u8>>());
options.edns.dhu = matches
.get_many::<u8>("dhu")
.map(|v| v.copied().collect::<Vec<u8>>());
options.edns.n3u = matches
.get_many::<u8>("n3u")
.map(|v| v.copied().collect::<Vec<u8>>());

//───────────────────────────────────────────────────────────────────────────────────
// manage other misc. options
Expand Down Expand Up @@ -623,6 +633,13 @@ impl CliOptions {

options.display.trace = matches.get_flag("trace");

// open Lua script to load code
if let Some(path) = matches.get_one::<PathBuf>("lua") {
// open Lua script and load code
let code = fs::read_to_string(path)?;
options.display.lua_code = Some(code);
}

Ok(options)
}
}
Expand Down Expand Up @@ -723,6 +740,7 @@ mod tests {
assert_eq!(&opts.protocol.domain, "www.google.com");
assert_eq!(opts.transport.ip_version, IPVersion::V4);
assert_eq!(opts.transport.transport_mode, Protocol::Udp);
assert_eq!(&opts.transport.end_point.server().unwrap(), &"1.1.1.1");
}

#[test]
Expand All @@ -737,6 +755,10 @@ mod tests {
assert_eq!(&opts.protocol.domain, "www.google.com");
assert_eq!(opts.transport.ip_version, IPVersion::V6);
assert_eq!(opts.transport.transport_mode, Protocol::Udp);
assert_eq!(
&opts.transport.end_point.server().unwrap(),
&"2606:4700:4700::1111"
);
}

#[test]
Expand Down
12 changes: 6 additions & 6 deletions dns/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ impl Buffer {
}

// when printing out some RRs, it's easier to use this
pub fn to_string(&self) -> String {
String::from_utf8_lossy(&self.0).to_string()
}
// pub fn to_string(&self) -> String {
// String::from_utf8_lossy(&self.0).to_string()
// }

// some RRs need to convert the raw data into b16 or b64 hexa strings
pub fn as_b64(&self) -> String {
general_purpose::STANDARD.encode(&self.as_ref())
general_purpose::STANDARD.encode(self.as_ref())
}

// some RRs need to convert the raw data into b16 or b64 hexa strings
Expand Down Expand Up @@ -56,7 +56,7 @@ impl fmt::Debug for Buffer {

impl fmt::Display for Buffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = String::from_utf8_lossy(&self.0.as_ref()).to_string();
let s = String::from_utf8_lossy(self.0.as_ref()).to_string();
write!(f, "{}", s)
}
}
Expand Down Expand Up @@ -106,7 +106,7 @@ impl Serialize for Buffer {
}
}

pub(crate) fn serialize_buffer<'a, S>(owner: &Buffer, serializer: S) -> Result<S::Ok, S::Error>
pub(crate) fn serialize_buffer<S>(owner: &Buffer, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Expand Down
4 changes: 2 additions & 2 deletions dns/src/rfc/caa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ impl fmt::Display for CAA {
f,
"{} {} \"{}\"",
self.flags,
self.tag_key.to_string(),
self.tag_value.to_string()
self.tag_key,
self.tag_value
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion dns/src/rfc/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl DomainName {
self.labels.iter()
}

pub fn from_position<'a>(&mut self, pos: usize, buffer: &'a [u8]) -> error::Result<usize> {
pub fn from_position(&mut self, pos: usize, buffer: &[u8]) -> error::Result<usize> {
let mut index = pos;
let at_index = *buffer
.get(index)
Expand Down
2 changes: 1 addition & 1 deletion dns/src/rfc/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub struct Flags {
//6-15 Reserved for future use.
}

#[derive(Debug, PartialEq, Copy, Clone, Serialize)]
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct BitFlags {
pub authorative_answer: bool, // Authoritative Answer - this bit is valid in responses,
//and specifies that the responding name server is an
Expand Down
1 change: 0 additions & 1 deletion dns/src/rfc/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use rand::Rng;
use serde::Serialize;

use super::{flags::Flags, opcode::OpCode, packet_type::PacketType};
use show::*;

// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
Expand Down
Loading

0 comments on commit 7eb7239

Please sign in to comment.