main.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //! dog, the command-line DNS client.
  2. #![warn(deprecated_in_future)]
  3. #![warn(future_incompatible)]
  4. #![warn(missing_copy_implementations)]
  5. #![warn(missing_docs)]
  6. #![warn(nonstandard_style)]
  7. #![warn(rust_2018_compatibility)]
  8. #![warn(rust_2018_idioms)]
  9. #![warn(single_use_lifetimes)]
  10. #![warn(trivial_casts, trivial_numeric_casts)]
  11. #![warn(unused)]
  12. #![warn(clippy::all, clippy::pedantic)]
  13. #![allow(clippy::enum_glob_use)]
  14. #![allow(clippy::module_name_repetitions)]
  15. #![allow(clippy::option_if_let_else)]
  16. #![allow(clippy::too_many_lines)]
  17. #![allow(clippy::upper_case_acronyms)]
  18. #![allow(clippy::wildcard_imports)]
  19. #![deny(unsafe_code)]
  20. use log::*;
  21. mod colours;
  22. mod connect;
  23. mod hints;
  24. mod logger;
  25. mod output;
  26. mod requests;
  27. mod resolve;
  28. mod table;
  29. mod txid;
  30. mod options;
  31. use self::options::*;
  32. /// Configures logging, parses the command-line options, and handles any
  33. /// errors before passing control over to the Dog type.
  34. fn main() {
  35. use std::env;
  36. use std::process::exit;
  37. logger::configure(env::var_os("DOG_DEBUG"));
  38. #[cfg(windows)]
  39. if let Err(e) = ansi_term::enable_ansi_support() {
  40. warn!("Failed to enable ANSI support: {}", e);
  41. }
  42. match Options::getopts(env::args_os().skip(1)) {
  43. OptionsResult::Ok(options) => {
  44. info!("Running with options -> {:#?}", options);
  45. disabled_feature_check(&options);
  46. exit(run(options));
  47. }
  48. OptionsResult::Help(help_reason, use_colours) => {
  49. if use_colours.should_use_colours() {
  50. print!("{}", include_str!(concat!(env!("OUT_DIR"), "/usage.pretty.txt")));
  51. }
  52. else {
  53. print!("{}", include_str!(concat!(env!("OUT_DIR"), "/usage.bland.txt")));
  54. }
  55. if help_reason == HelpReason::NoDomains {
  56. exit(exits::OPTIONS_ERROR);
  57. }
  58. else {
  59. exit(exits::SUCCESS);
  60. }
  61. }
  62. OptionsResult::Version(use_colours) => {
  63. if use_colours.should_use_colours() {
  64. print!("{}", include_str!(concat!(env!("OUT_DIR"), "/version.pretty.txt")));
  65. }
  66. else {
  67. print!("{}", include_str!(concat!(env!("OUT_DIR"), "/version.bland.txt")));
  68. }
  69. exit(exits::SUCCESS);
  70. }
  71. OptionsResult::InvalidOptionsFormat(oe) => {
  72. eprintln!("dog: Invalid options: {}", oe);
  73. exit(exits::OPTIONS_ERROR);
  74. }
  75. OptionsResult::InvalidOptions(why) => {
  76. eprintln!("dog: Invalid options: {}", why);
  77. exit(exits::OPTIONS_ERROR);
  78. }
  79. }
  80. }
  81. /// Runs dog with some options, returning the status to exit with.
  82. fn run(Options { requests, format, measure_time }: Options) -> i32 {
  83. use std::time::Instant;
  84. let should_show_opt = requests.edns.should_show();
  85. let mut responses = Vec::new();
  86. let timer = if measure_time { Some(Instant::now()) } else { None };
  87. let mut errored = false;
  88. let local_host_hints = match hints::LocalHosts::load() {
  89. Ok(lh) => lh,
  90. Err(e) => {
  91. warn!("Error loading local host hints: {}", e);
  92. hints::LocalHosts::default()
  93. }
  94. };
  95. for hostname_in_query in &requests.inputs.domains {
  96. if local_host_hints.contains(hostname_in_query) {
  97. eprintln!("warning: domain '{}' also exists in hosts file", hostname_in_query);
  98. }
  99. }
  100. let request_tuples = match requests.generate() {
  101. Ok(rt) => rt,
  102. Err(e) => {
  103. eprintln!("Unable to obtain resolver: {}", e);
  104. return exits::SYSTEM_ERROR;
  105. }
  106. };
  107. for (transport, request_list) in request_tuples {
  108. let request_list_len = request_list.len();
  109. for (i, request) in request_list.into_iter().enumerate() {
  110. let result = transport.send(&request);
  111. match result {
  112. Ok(mut response) => {
  113. if response.flags.error_code.is_some() && i != request_list_len - 1 {
  114. continue;
  115. }
  116. if ! should_show_opt {
  117. response.answers.retain(dns::Answer::is_standard);
  118. response.authorities.retain(dns::Answer::is_standard);
  119. response.additionals.retain(dns::Answer::is_standard);
  120. }
  121. responses.push(response);
  122. break;
  123. }
  124. Err(e) => {
  125. format.print_error(e);
  126. errored = true;
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. let duration = timer.map(|t| t.elapsed());
  133. if format.print(responses, duration) {
  134. if errored {
  135. exits::NETWORK_ERROR
  136. }
  137. else {
  138. exits::SUCCESS
  139. }
  140. }
  141. else {
  142. exits::NO_SHORT_RESULTS
  143. }
  144. }
  145. /// Checks whether the options contain parameters that will cause dog to fail
  146. /// because the feature is disabled by exiting if so.
  147. #[allow(unused)]
  148. fn disabled_feature_check(options: &Options) {
  149. use std::process::exit;
  150. use crate::connect::TransportType;
  151. #[cfg(all(not(feature = "with_tls"), not(feature = "with_rustls_tls")))]
  152. if options.requests.inputs.transport_types.contains(&TransportType::TLS) {
  153. eprintln!("dog: Cannot use '--tls': This version of dog has been compiled without TLS support");
  154. exit(exits::OPTIONS_ERROR);
  155. }
  156. #[cfg(all(not(feature = "with_https"), not(feature = "with_rustls_https")))]
  157. if options.requests.inputs.transport_types.contains(&TransportType::HTTPS) {
  158. eprintln!("dog: Cannot use '--https': This version of dog has been compiled without HTTPS support");
  159. exit(exits::OPTIONS_ERROR);
  160. }
  161. }
  162. /// The possible status numbers dog can exit with.
  163. mod exits {
  164. /// Exit code for when everything turns out OK.
  165. pub const SUCCESS: i32 = 0;
  166. /// Exit code for when there was at least one network error during execution.
  167. pub const NETWORK_ERROR: i32 = 1;
  168. /// Exit code for when there is no result from the server when running in
  169. /// short mode. This can be any received server error, not just `NXDOMAIN`.
  170. pub const NO_SHORT_RESULTS: i32 = 2;
  171. /// Exit code for when the command-line options are invalid.
  172. pub const OPTIONS_ERROR: i32 = 3;
  173. /// Exit code for when the system network configuration could not be determined.
  174. pub const SYSTEM_ERROR: i32 = 4;
  175. }