diff --git a/Cargo.lock b/Cargo.lock index 98296ee..489228f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,7 +144,7 @@ dependencies = [ [[package]] name = "doge" -version = "0.2.1-beta" +version = "0.2.2-beta" dependencies = [ "ansi_term", "atty", diff --git a/Cargo.toml b/Cargo.toml index e7dd620..f31759a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ exclude = [ ] homepage = "https://dns.lookup.dog/" license = "MIT" -version = "0.2.1-beta" +version = "0.2.2-beta" [[bin]] @@ -60,7 +60,7 @@ log = "0.4" # Git workflows as well as my sanity will fail without this [dependencies.openssl-sys] -version = "0.9" +version = "0.9.99" features = ["vendored"] # windows default nameserver determination diff --git a/README.md b/README.md index 6bb5e56..ec8d0ca 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ It has colourful output, understands normal command-line argument syntax, suppor doge example.net MX ...looking up MX records instead doge example.net MX @1.1.1.1 ...using a specific nameserver instead doge example.net MX @1.1.1.1 -T ...using TCP rather than UDP + doge exapple.net MX @1.1.1.1 -p 53 ...using a nonstandart port doge -q example.net -t MX -n 1.1.1.1 -T As above, but using explicit arguments --- @@ -39,6 +40,7 @@ It has colourful output, understands normal command-line argument syntax, suppor -q, --query=HOST Host name or domain name to query -t, --type=TYPE Type of the DNS record being queried (A, MX, NS...) -n, --nameserver=ADDR Address of the nameserver to send packets to + -p, --port=PORT Port options for sending queries on nonstandart ports --class=CLASS Network class of the DNS record being queried (IN, CH, HS) ### Sending options diff --git a/dns-transport/src/auto.rs b/dns-transport/src/auto.rs index a70cf1c..596605f 100644 --- a/dns-transport/src/auto.rs +++ b/dns-transport/src/auto.rs @@ -11,20 +11,25 @@ use super::{Transport, Error, UdpTransport, TcpTransport}; /// This is the default behaviour for many DNS clients. pub struct AutoTransport { addr: String, + custom_port: u16 } impl AutoTransport { /// Creates a new automatic transport that connects to the given host. - pub fn new(addr: String) -> Self { - Self { addr } + pub fn new(addr: String, port: Option) -> Self { + let custom_port: u16 = match port { + Some(port) => port, + None => 53, + }; + Self { addr, custom_port } } } impl Transport for AutoTransport { fn send(&self, request: &Request) -> Result { - let udp_transport = UdpTransport::new(self.addr.clone()); + let udp_transport = UdpTransport::new(self.addr.clone(), Some(self.custom_port.clone())); let udp_response = udp_transport.send(&request)?; if ! udp_response.flags.truncated { @@ -33,7 +38,7 @@ impl Transport for AutoTransport { debug!("Truncated flag set, so switching to TCP"); - let tcp_transport = TcpTransport::new(self.addr.clone()); + let tcp_transport = TcpTransport::new(self.addr.clone(), Some(self.custom_port.clone())); let tcp_response = tcp_transport.send(&request)?; Ok(tcp_response) } diff --git a/dns-transport/src/https.rs b/dns-transport/src/https.rs index 6a3d324..6b99dfb 100644 --- a/dns-transport/src/https.rs +++ b/dns-transport/src/https.rs @@ -14,13 +14,18 @@ use super::tls_stream; /// encrypted with TLS, using TCP. pub struct HttpsTransport { url: String, + custom_port: u16 } impl HttpsTransport { /// Creates a new HTTPS transport that connects to the given URL. - pub fn new(url: String) -> Self { - Self { url } + pub fn new(url: String, port: Option) -> Self { + let custom_port: u16 = match port { + Some(port) => port, + None => 443, + }; + Self { url, custom_port } } } @@ -42,7 +47,7 @@ impl Transport for HttpsTransport { let (domain, path) = self.split_domain().expect("Invalid HTTPS nameserver"); info!("Opening TLS socket to {:?}", domain); - let mut stream = Self::stream(&domain, 443)?; + let mut stream = Self::stream(&domain, *&self.custom_port)?; debug!("Connected"); @@ -123,5 +128,5 @@ impl HttpsTransport { } /// The User-Agent header sent with HTTPS requests. -static USER_AGENT: &str = concat!("dog/", env!("CARGO_PKG_VERSION")); +static USER_AGENT: &str = concat!("doge/", env!("CARGO_PKG_VERSION")); diff --git a/dns-transport/src/tcp.rs b/dns-transport/src/tcp.rs index f9327a9..c58ce55 100644 --- a/dns-transport/src/tcp.rs +++ b/dns-transport/src/tcp.rs @@ -18,13 +18,18 @@ use super::{Transport, Error}; /// TCP, Implementation Requirements (March 2016) pub struct TcpTransport { addr: String, + custom_port: u16 } impl TcpTransport { /// Creates a new TCP transport that connects to the given host. - pub fn new(addr: String) -> Self { - Self { addr } + pub fn new(addr: String, port: Option) -> Self { + let custom_port: u16 = match port { + Some(port) => port, + None => 53, + }; + Self { addr, custom_port } } } @@ -33,12 +38,8 @@ impl Transport for TcpTransport { fn send(&self, request: &Request) -> Result { info!("Opening TCP stream"); let mut stream = - if self.addr.contains(':') { - TcpStream::connect(&*self.addr)? - } - else { - TcpStream::connect((&*self.addr, 53))? - }; + TcpStream::connect((&*self.addr, self.custom_port))?; + debug!("Opened"); // The message is prepended with the length when sent over TCP, diff --git a/dns-transport/src/tls.rs b/dns-transport/src/tls.rs index 959dbc9..44695c5 100644 --- a/dns-transport/src/tls.rs +++ b/dns-transport/src/tls.rs @@ -14,13 +14,18 @@ use super::tls_stream::TlsStream; /// encrypted TLS connection. pub struct TlsTransport { addr: String, + custom_port: u16 } impl TlsTransport { /// Creates a new TLS transport that connects to the given host. - pub fn new(addr: String) -> Self { - Self { addr } + pub fn new(addr: String, port: Option) -> Self { + let custom_port: u16 = match port { + Some(p) => p, + None => 853, + }; + Self { addr, custom_port } } } @@ -30,22 +35,14 @@ impl Transport for TlsTransport { #[cfg(feature = "with_tls")] fn send(&self, request: &Request) -> Result { + use native_tls::TlsStream; + info!("Opening TLS socket"); let domain = self.sni_domain(); info!("Connecting using domain {:?}", domain); - let mut stream = - if self.addr.contains(':') { - let mut parts = self.addr.split(":"); - let domain = parts.nth(0).unwrap(); - let port = parts.last().unwrap().parse::().expect("Invalid port number"); - - Self::stream(domain, port)? - } - else { - Self::stream(&*self.addr, 853)? - }; - + // comminicate that the port must EXPLICATLY BE SEPERATE + let mut stream: TlsStream = Self::stream(&self.addr, *&self.custom_port)?; debug!("Connected"); diff --git a/dns-transport/src/udp.rs b/dns-transport/src/udp.rs index 785b3d4..f7c251a 100644 --- a/dns-transport/src/udp.rs +++ b/dns-transport/src/udp.rs @@ -14,13 +14,19 @@ use super::{Transport, Error}; /// Implementation and Specification (November 1987) pub struct UdpTransport { addr: String, + custom_port: u16 } impl UdpTransport { /// Creates a new UDP transport that connects to the given host. - pub fn new(addr: String) -> Self { - Self { addr } + pub fn new(addr: String, port: Option) -> Self { + let custom_port: u16 = match port { + Some(p) => p, + None => 53, + }; + // info!("Running on nonstandart port"); + Self { addr, custom_port } } } @@ -30,13 +36,8 @@ impl Transport for UdpTransport { info!("Opening UDP socket"); // TODO: This will need to be changed for IPv6 support. let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?; + socket.connect( (&*self.addr, self.custom_port))?; - if self.addr.contains(':') { - socket.connect(&*self.addr)?; - } - else { - socket.connect((&*self.addr, 53))?; - } debug!("Opened"); let bytes_to_send = request.to_bytes().expect("failed to serialise request"); diff --git a/src/connect.rs b/src/connect.rs index 05a5a48..6e15c53 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -12,21 +12,26 @@ pub enum TransportType { /// UDP is used by default. If the request packet would be too large, send /// a TCP packet instead; if a UDP _response_ packet is truncated, try /// again with TCP. - Automatic, + /// Takes an 'Option' for diffrent ports None uses the protocol default port + Automatic(Option), /// Send packets over UDP only. /// If the request packet is too large or the response packet is /// truncated, fail with an error. - UDP, + /// Takes an 'Option' for diffrent ports None uses the protocol default port + UDP(Option), /// Send packets over TCP only. - TCP, + /// Takes an 'Option' for diffrent ports None uses the protocol default port + TCP(Option), /// Send encrypted DNS-over-TLS packets. - TLS, + /// Takes an 'Option' for diffrent ports None uses the protocol default port + TLS(Option), /// Send encrypted DNS-over-HTTPS packets. - HTTPS, + /// Takes an 'Option' for diffrent ports None uses the protocol default port + HTTPS(Option), } impl TransportType { @@ -36,11 +41,11 @@ impl TransportType { /// stringified address for the others. pub fn make_transport(self, param: String) -> Box { match self { - Self::Automatic => Box::new(AutoTransport::new(param)), - Self::UDP => Box::new(UdpTransport::new(param)), - Self::TCP => Box::new(TcpTransport::new(param)), - Self::TLS => Box::new(TlsTransport::new(param)), - Self::HTTPS => Box::new(HttpsTransport::new(param)), + Self::Automatic(p) => Box::new(AutoTransport::new(param, p)), + Self::UDP(p) => Box::new(UdpTransport::new(param, p)), + Self::TCP(p) => Box::new(TcpTransport::new(param, p)), + Self::TLS(p) => Box::new(TlsTransport::new(param, p)), + Self::HTTPS(p) => Box::new(HttpsTransport::new(param, p)), } } } diff --git a/src/main.rs b/src/main.rs index 4389457..0264f20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,14 +182,14 @@ fn disabled_feature_check(options: &Options) { use crate::connect::TransportType; #[cfg(all(not(feature = "with_tls"), not(feature = "with_rustls_tls")))] - if options.requests.inputs.transport_types.contains(&TransportType::TLS) { - eprintln!("dog: Cannot use '--tls': This version of dog has been compiled without TLS support"); + if options.requests.inputs.transport_types.contains(&TransportType::TLS(None)) { + eprintln!("doge: Cannot use '--tls': This version of dog has been compiled without TLS support"); exit(exits::OPTIONS_ERROR); } #[cfg(all(not(feature = "with_https"), not(feature = "with_rustls_https")))] - if options.requests.inputs.transport_types.contains(&TransportType::HTTPS) { - eprintln!("dog: Cannot use '--https': This version of dog has been compiled without HTTPS support"); + if options.requests.inputs.transport_types.contains(&TransportType::HTTPS(None)) { + eprintln!("doge: Cannot use '--https': This version of dog has been compiled without HTTPS support"); exit(exits::OPTIONS_ERROR); } } diff --git a/src/options.rs b/src/options.rs index 533cf14..833f3c7 100644 --- a/src/options.rs +++ b/src/options.rs @@ -47,6 +47,7 @@ impl Options { // Query options opts.optmulti("q", "query", "Host name or domain name to query", "HOST"); + opts.optmulti ("p", "port", "Specify using a non-standart port", "port"); opts.optmulti("t", "type", "Type of the DNS record being queried (A, MX, NS...)", "TYPE"); opts.optmulti("n", "nameserver", "Address of the nameserver to send packets to", "ADDR"); opts.optmulti("", "class", "Network class of the DNS record being queried (IN, CH, HS)", "CLASS"); @@ -56,11 +57,11 @@ impl Options { opts.optopt ("", "txid", "Set the transaction ID to a specific value", "NUMBER"); opts.optmulti("Z", "", "Set uncommon protocol tweaks", "TWEAKS"); - // Protocol options - opts.optflag ("U", "udp", "Use the DNS protocol over UDP"); - opts.optflag ("T", "tcp", "Use the DNS protocol over TCP"); - opts.optflag ("S", "tls", "Use the DNS-over-TLS protocol"); - opts.optflag ("H", "https", "Use the DNS-over-HTTPS protocol"); + // Protocol and options + opts.optflag ("U", "udp", "Use the DNS protocol over UDP"); + opts.optflag ("T", "tcp", "Use the DNS protocol over TCP"); + opts.optflag ("S", "tls", "Use the DNS-over-TLS protocol"); + opts.optflag ("H", "https", "Use the DNS-over-HTTPS protocol"); // Output options opts.optopt ("", "color", "When to use terminal colors", "WHEN"); @@ -138,20 +139,28 @@ impl Inputs { } fn load_transport_types(&mut self, matches: &getopts::Matches) { + + + let ports: Vec = matches.opt_strs("port"); + let port: Option = match ports.len() == 1{ + true => Some(ports[0].parse::().unwrap()), + false => None, // TODO add warning that multiple ports can't be given + }; + if matches.opt_present("https") { - self.transport_types.push(TransportType::HTTPS); + self.transport_types.push(TransportType::HTTPS(port)); } - + if matches.opt_present("tls") { - self.transport_types.push(TransportType::TLS); + self.transport_types.push(TransportType::TLS(port)); } - + if matches.opt_present("tcp") { - self.transport_types.push(TransportType::TCP); + self.transport_types.push(TransportType::TCP(port)); } - + if matches.opt_present("udp") { - self.transport_types.push(TransportType::UDP); + self.transport_types.push(TransportType::UDP(port)); } } @@ -227,7 +236,7 @@ impl Inputs { } fn check_for_missing_nameserver(&self) -> Result<(), OptionsError> { - if self.resolver_types.is_empty() && self.transport_types == [TransportType::HTTPS] { + if self.resolver_types.is_empty() && self.transport_types == [TransportType::HTTPS(None)] { Err(OptionsError::MissingHttpsUrl) } else { @@ -249,7 +258,7 @@ impl Inputs { } if self.transport_types.is_empty() { - self.transport_types.push(TransportType::Automatic); + self.transport_types.push(TransportType::Automatic(None)); } } @@ -518,7 +527,7 @@ mod test { record_types: vec![ RecordType::A ], classes: vec![ QClass::IN ], resolver_types: vec![ ResolverType::SystemDefault ], - transport_types: vec![ TransportType::Automatic ], + transport_types: vec![ TransportType::Automatic(None) ], } } } @@ -815,13 +824,15 @@ mod test { TxidGenerator::Sequence(1234)); } + // TODO add tests to ensure something happenes if multiple ports are given + #[test] fn all_transport_types() { use crate::connect::TransportType::*; let options = Options::getopts(&[ "dom.ain", "--https", "--tls", "--tcp", "--udp" ]).unwrap(); assert_eq!(options.requests.inputs.transport_types, - vec![ HTTPS, TLS, TCP, UDP ]); + vec![ HTTPS(None), TLS(None), TCP(None), UDP(None) ]); } // invalid options tests diff --git a/src/usage.txt b/src/usage.txt index c7177ef..7eb9164 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -6,11 +6,12 @@ \1mdoge\0m \36mexample.net\0m \32mMX\0m ...looking up MX records instead \1mdoge\0m \36mexample.net\0m \32mMX\0m \36m@1.1.1.1\0m ...using a specific nameserver instead \1mdoge\0m \36mexample.net\0m \32mMX\0m \36m@1.1.1.1\0m \1;33m-T\0m ...using TCP rather than UDP - \1mdoge\0m \1;33m-q\0m \33mexample.net\0m \1;33m-t\0m \33mMX\0m \1;33m-n\0m \33m1.1.1.1\0m \1;33m-T\0m As above, but using explicit arguments + \1mdoge\0m \36mexample.net\0m \32mMX\0m \36m@1.1.1.1\0m\1;33m -p 67\0m ...using a non standart port \4mQuery options:\0m - \32m\0m Human-readable host names, nameservers, types, or classes + \1;36m[arguments]\0m Human-readable host names, nameservers, types, or classes \1;33m-q\0m, \1;33m--query\0m=\33mHOST\0m Host name or domain name to query + \1;33m-p\0m, \1;33m--port\0m=\33mPORT\0m Specifing a non-standart port to use \1;33m-t\0m, \1;33m--type\0m=\33mTYPE\0m Type of the DNS record being queried (A, MX, NS...) \1;33m-n\0m, \1;33m--nameserver\0m=\33mADDR\0m Address of the nameserver to send packets to \1;33m--class\0m=\33mCLASS\0m Network class of the DNS record being queried (IN, CH, HS)