diff --git a/USER-INTERFACE-INTERFACE.md b/USER-INTERFACE-INTERFACE.md index c7059929a..47c78f92f 100644 --- a/USER-INTERFACE-INTERFACE.md +++ b/USER-INTERFACE-INTERFACE.md @@ -1143,6 +1143,7 @@ be cleared. * `db-password` - Password to unlock the sensitive values in the database. * `dns-servers` - Comma-separated list of DNS servers to use. * `earning-wallet` - Wallet into which earnings should be deposited. +* `entry-dns` - To start MASQ's local DNS server if HTTP proxying is unavailable. * `gas-price` - The fee per unit of computational effort in blockchain transactions, measured in gwei. * `ip` - The public IP address of the Node. * `log-level` - The lowest level of logs that should be recorded. `off`, `error`, `warn`, `info`, `debug`, `trace` diff --git a/masq_lib/src/shared_schema.rs b/masq_lib/src/shared_schema.rs index 276108d2e..0d3500873 100644 --- a/masq_lib/src/shared_schema.rs +++ b/masq_lib/src/shared_schema.rs @@ -50,6 +50,10 @@ pub const EARNING_WALLET_HELP: &str = (case-insensitive). If you already have a derivation-path earning wallet, don't supply this. \ If you have supplied an earning wallet address before, either don't supply it again or be \ careful to supply exactly the same one you supplied before."; +pub const ENTRY_DNS_HELP: &str = "Specify whether to use entry DNS. If you can't use HTTP proxying \ + to reroute your client's traffic through MASQ's Node, you can specify --entry-dns to start up \ + MASQ's local DNS server; but you will also need to subvert your network stack's DNS traffic \ + using dns_utility. This practice is not recommended but may be necessary in certain situations."; pub const IP_ADDRESS_HELP: &str = "The public IP address of your MASQ Node: that is, the IPv4 \ address at which other Nodes can contact yours. If you're running your Node behind \ a router, this will be the IP address of the router. If this IP address starts with 192.168 or 10.0, \ @@ -408,6 +412,14 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> { EARNING_WALLET_HELP, common_validators::validate_ethereum_address, )) + .arg( + Arg::with_name("entry-dns") + .long("entry-dns") + .value_name("ENTRY-DNS") + .required(false) + .takes_value(false) + .help(ENTRY_DNS_HELP), + ) .arg( Arg::with_name("fake-public-key") .long("fake-public-key") @@ -741,6 +753,13 @@ mod tests { If you have supplied an earning wallet address before, either don't supply it again or be \ careful to supply exactly the same one you supplied before." ); + assert_eq!( + ENTRY_DNS_HELP, + "Specify whether to use entry DNS. If you can't use HTTP proxying \ + to reroute your client's traffic through MASQ's Node, you can specify --entry-dns to start up \ + MASQ's local DNS server; but you will also need to subvert your network stack's DNS traffic \ + using dns_utility. This practice is not recommended but may be necessary in certain situations." + ); assert_eq!( IP_ADDRESS_HELP, "The public IP address of your MASQ Node: that is, the IPv4 \ diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 4238bd8d5..74b46ffb5 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -37,7 +37,7 @@ pub trait UnprivilegedParseArgsConfiguration { unprivileged_config .blockchain_bridge_config .blockchain_service_url_opt = - if is_user_specified(multi_config, "blockchain-service-url") { + if has_user_specified(multi_config, "blockchain-service-url") { value_m!(multi_config, "blockchain-service-url", String) } else { match persistent_config.blockchain_service_url() { @@ -48,7 +48,7 @@ pub trait UnprivilegedParseArgsConfiguration { }; unprivileged_config.clandestine_port_opt = value_m!(multi_config, "clandestine-port", u16); unprivileged_config.blockchain_bridge_config.gas_price = - if is_user_specified(multi_config, "gas-price") { + if has_user_specified(multi_config, "gas-price") { value_m!(multi_config, "gas-price", u64).expectv("gas price") } else { match persistent_config.gas_price() { @@ -614,7 +614,7 @@ fn set_db_password_at_first_mention( } } -fn is_user_specified(multi_config: &MultiConfig, parameter: &str) -> bool { +pub fn has_user_specified(multi_config: &MultiConfig, parameter: &str) -> bool { multi_config.occurrences_of(parameter) > 0 } diff --git a/node/src/server_initializer.rs b/node/src/server_initializer.rs index ea288fdce..8de2ad6c4 100644 --- a/node/src/server_initializer.rs +++ b/node/src/server_initializer.rs @@ -4,6 +4,7 @@ use super::privilege_drop::{PrivilegeDropper, PrivilegeDropperReal}; use crate::bootstrapper::RealUser; use crate::entry_dns::dns_socket_server::DnsSocketServer; use crate::node_configurator::node_configurator_standard::server_initializer_collected_params; +use crate::node_configurator::unprivileged_parse_args_configuration::has_user_specified; use crate::node_configurator::{DirsWrapper, DirsWrapperReal}; use crate::run_modes_factories::{RunModeResult, ServerInitializer}; use crate::sub_lib::socket_server::ConfiguredByPrivilege; @@ -18,6 +19,7 @@ use log::{log, Level}; use masq_lib::command::StdStreams; use masq_lib::logger; use masq_lib::logger::{real_format_function, POINTER_TO_FORMAT_FUNCTION}; +use masq_lib::multi_config::MultiConfig; use masq_lib::shared_schema::ConfiguratorError; use std::any::Any; use std::io; @@ -33,47 +35,71 @@ pub struct ServerInitializerReal { bootstrapper: Box>, privilege_dropper: Box, dirs_wrapper: Box, + is_entry_dns_enabled: bool, } impl ServerInitializer for ServerInitializerReal { fn go(&mut self, streams: &mut StdStreams<'_>, args: &[String]) -> RunModeResult { let multi_config = server_initializer_collected_params(self.dirs_wrapper.as_ref(), args)?; + self.is_entry_dns_enabled = has_user_specified(&multi_config, "entry-dns"); + + let privileged_result = self.initialize_privileged(&multi_config); + self.drop_privileges(&multi_config); + let unprivileged_result = self.initialize_unprivileged(&multi_config, streams); + + privileged_result.combine_results(unprivileged_result) + } + + as_any_ref_in_trait_impl!(); +} + +impl ServerInitializerReal { + fn drop_privileges(&self, multi_config: &MultiConfig) { let real_user = value_m!(multi_config, "real-user", RealUser) .expect("ServerInitializer: Real user not present in Multi Config"); let data_directory = value_m!(multi_config, "data-directory", String) .expect("ServerInitializer: Data directory not present in Multi Config"); - // TODO: GH-525: This card should bring back the commented out code for dns_socket_server - let result: RunModeResult = Ok(()) - // .combine_results( - // self.dns_socket_server - // .as_mut() - // .initialize_as_privileged(&multi_config), - // ) - .combine_results( - self.bootstrapper - .as_mut() - .initialize_as_privileged(&multi_config), - ); - self.privilege_dropper .chown(Path::new(data_directory.as_str()), &real_user); - self.privilege_dropper.drop_privileges(&real_user); + } - result - // .combine_results( - // self.dns_socket_server - // .as_mut() - // .initialize_as_unprivileged(&multi_config, streams), - // ) - .combine_results( - self.bootstrapper - .as_mut() - .initialize_as_unprivileged(&multi_config, streams), - ) + fn initialize_privileged(&mut self, multi_config: &MultiConfig) -> RunModeResult { + let result = match self.is_entry_dns_enabled { + true => self + .dns_socket_server + .as_mut() + .initialize_as_privileged(multi_config), + false => Ok(()), + }; + + result.combine_results( + self.bootstrapper + .as_mut() + .initialize_as_privileged(multi_config), + ) + } + + fn initialize_unprivileged( + &mut self, + multi_config: &MultiConfig, + streams: &mut StdStreams<'_>, + ) -> RunModeResult { + let result = match self.is_entry_dns_enabled { + true => self + .dns_socket_server + .as_mut() + .initialize_as_unprivileged(multi_config, streams), + false => Ok(()), + }; + + result.combine_results( + self.bootstrapper + .as_mut() + .initialize_as_unprivileged(multi_config, streams), + ) } - as_any_ref_in_trait_impl!(); } impl Future for ServerInitializerReal { @@ -81,12 +107,16 @@ impl Future for ServerInitializerReal { type Error = (); fn poll(&mut self) -> Result::Item>, ::Error> { - // try_ready!(self - // .dns_socket_server - // .as_mut() - // .join(self.bootstrapper.as_mut()) - // .poll()); - try_ready!(self.bootstrapper.as_mut().poll()); + match self.is_entry_dns_enabled { + true => { + try_ready!(self + .dns_socket_server + .as_mut() + .join(self.bootstrapper.as_mut()) + .poll()); + } + false => try_ready!(self.bootstrapper.as_mut().poll()), + } Ok(Async::Ready(())) } } @@ -98,6 +128,7 @@ impl Default for ServerInitializerReal { bootstrapper: Box::new(Bootstrapper::new(Box::new(LoggerInitializerWrapperReal {}))), privilege_dropper: Box::new(PrivilegeDropperReal::new()), dirs_wrapper: Box::new(DirsWrapperReal::default()), + is_entry_dns_enabled: false, } } } @@ -681,9 +712,65 @@ pub mod tests { tlh.exists_log_containing("ERROR: PanicHandler: file.txt:24:42 - I'm just a string slice"); } + #[test] + fn go_updates_entry_dns_flag() { + let _ = LogfileNameGuard::new(&PathBuf::from("go_updates_entry_dns_flag")); + let dns_socket_server = CrashTestDummy::new(CrashPoint::None, ()); + let bootstrapper = CrashTestDummy::new(CrashPoint::None, BootstrapperConfig::new()); + let privilege_dropper = PrivilegeDropperMock::new(); + let dirs_wrapper = make_pre_populated_mocked_directory_wrapper(); + let mut subject = ServerInitializerReal { + dns_socket_server: Box::new(dns_socket_server), + bootstrapper: Box::new(bootstrapper), + privilege_dropper: Box::new(privilege_dropper), + dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, + }; + + let _ = subject.go( + &mut StdStreams { + stdin: &mut ByteArrayReader::new(&[0; 0]), + stdout: &mut ByteArrayWriter::new(), + stderr: &mut ByteArrayWriter::new(), + }, + &slice_of_strs_to_vec_of_strings(&["MASQNode", "--entry-dns"]), + ); + + assert!(subject.is_entry_dns_enabled); + } + + #[test] + fn go_maintains_entry_dns_flag_disabled_if_absent_in_args() { + let _ = LogfileNameGuard::new(&PathBuf::from( + "go_maintains_entry_dns_flag_disabled_if_absent_in_args", + )); + let dns_socket_server = CrashTestDummy::new(CrashPoint::None, ()); + let bootstrapper = CrashTestDummy::new(CrashPoint::None, BootstrapperConfig::new()); + let privilege_dropper = PrivilegeDropperMock::new(); + let dirs_wrapper = make_pre_populated_mocked_directory_wrapper(); + let mut subject = ServerInitializerReal { + dns_socket_server: Box::new(dns_socket_server), + bootstrapper: Box::new(bootstrapper), + privilege_dropper: Box::new(privilege_dropper), + dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, + }; + + let _ = subject.go( + &mut StdStreams { + stdin: &mut ByteArrayReader::new(&[0; 0]), + stdout: &mut ByteArrayWriter::new(), + stderr: &mut ByteArrayWriter::new(), + }, + &slice_of_strs_to_vec_of_strings(&["MASQNode"]), // "--entry-dns" is absent + ); + + assert!(!subject.is_entry_dns_enabled); + } + #[test] fn exits_after_all_socket_servers_exit() { - let _ = LogfileNameGuard::new(&PathBuf::from("uninitialized")); + let _ = LogfileNameGuard::new(&PathBuf::from("exits_after_all_socket_servers_exit")); let dns_socket_server = CrashTestDummy::new(CrashPoint::Error, ()); let bootstrapper = CrashTestDummy::new(CrashPoint::Error, BootstrapperConfig::new()); let dirs_wrapper = make_pre_populated_mocked_directory_wrapper(); @@ -693,6 +780,7 @@ pub mod tests { bootstrapper: Box::new(bootstrapper), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, }; let stdin = &mut ByteArrayReader::new(&[0; 0]); let stdout = &mut ByteArrayWriter::new(); @@ -722,6 +810,7 @@ pub mod tests { bootstrapper: Box::new(bootstrapper), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, }; let result = subject.poll(); @@ -729,8 +818,7 @@ pub mod tests { } #[test] - // TODO: GH-525: It should panic - // #[should_panic(expected = "EntryDnsServerMock was instructed to panic")] + #[should_panic(expected = "EntryDnsServerMock was instructed to panic")] fn server_initializer_dns_socket_server_panics() { let bootstrapper = CrashTestDummy::new(CrashPoint::None, BootstrapperConfig::new()); let privilege_dropper = PrivilegeDropperMock::new(); @@ -744,6 +832,7 @@ pub mod tests { bootstrapper: Box::new(bootstrapper), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: true, }; let _ = subject.poll(); @@ -763,6 +852,7 @@ pub mod tests { )), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, }; let _ = subject.poll(); @@ -770,7 +860,7 @@ pub mod tests { #[test] fn go_should_drop_privileges() { - let _ = LogfileNameGuard::new(&PathBuf::from("uninitialized")); + let _ = LogfileNameGuard::new(&PathBuf::from("go_should_drop_privileges")); let bootstrapper_init_privileged_params_arc = Arc::new(Mutex::new(vec![])); let bootstrapper_init_unprivileged_params_arc = Arc::new(Mutex::new(vec![])); let dns_socket_server_privileged_params_arc = Arc::new(Mutex::new(vec![])); @@ -812,6 +902,7 @@ pub mod tests { bootstrapper: Box::new(bootstrapper), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(dirs_wrapper), + is_entry_dns_enabled: false, }; let result = subject.go( @@ -822,6 +913,7 @@ pub mod tests { "123:456:/home/alice", "--dns-servers", "5.5.6.6", + "--entry-dns", ]), ); @@ -844,8 +936,8 @@ pub mod tests { [ bootstrapper_init_privileged_params_arc, bootstrapper_init_unprivileged_params_arc, - // dns_socket_server_privileged_params_arc, // TODO: GH-525: Fix me - // dns_socket_server_unprivileged_params_arc, + dns_socket_server_privileged_params_arc, + dns_socket_server_unprivileged_params_arc, ] .iter() .for_each(|arc_params| { @@ -859,7 +951,7 @@ pub mod tests { #[test] fn go_should_combine_errors() { - let _ = LogfileNameGuard::new(&PathBuf::from("uninitialized")); + let _ = LogfileNameGuard::new(&PathBuf::from("go_should_combine_errors")); let dns_socket_server = ConfiguredByPrivilegeMock::default() .initialize_as_privileged_result(Err(ConfiguratorError::required( "dns-iap", @@ -884,23 +976,27 @@ pub mod tests { bootstrapper: Box::new(bootstrapper), privilege_dropper: Box::new(privilege_dropper), dirs_wrapper: Box::new(make_pre_populated_mocked_directory_wrapper()), + is_entry_dns_enabled: false, }; - let args = - slice_of_strs_to_vec_of_strings(&["MASQNode", "--real-user", "123:123:/home/alice"]); + let args = slice_of_strs_to_vec_of_strings(&[ + "MASQNode", + "--real-user", + "123:123:/home/alice", + "--entry-dns", + ]); let stderr = ByteArrayWriter::new(); let mut holder = FakeStreamHolder::new(); holder.stderr = stderr; let result = subject.go(&mut holder.streams(), &args); - // TODO: GH-525: Fix me assert_eq!( result, Err(ConfiguratorError::new(vec![ - // ParamError::new("dns-iap", "dns-iap-reason"), + ParamError::new("dns-iap", "dns-iap-reason"), ParamError::new("boot-iap", "boot-iap-reason"), - // ParamError::new("dns-iau", "dns-iau-reason"), - ParamError::new("boot-iau", "boot-iau-reason") + ParamError::new("dns-iau", "dns-iau-reason"), + ParamError::new("boot-iau", "boot-iau-reason"), ])) ); } diff --git a/node/tests/contract_test.rs b/node/tests/contract_test.rs index 42d6fce37..2e1a0090b 100644 --- a/node/tests/contract_test.rs +++ b/node/tests/contract_test.rs @@ -143,6 +143,7 @@ fn masq_erc20_contract_exists_on_ethereum_mainnet_integration() { #[test] fn masq_erc20_contract_exists_on_base_mainnet_integration() { let blockchain_urls = vec![ + "https://base.rpc.subquery.network/public", "https://base-rpc.publicnode.com", "https://base.drpc.org", "https://base-pokt.nodies.app", diff --git a/node/tests/dns_round_trip_test.rs b/node/tests/dns_round_trip_test.rs index 7d32e0892..f857e0945 100644 --- a/node/tests/dns_round_trip_test.rs +++ b/node/tests/dns_round_trip_test.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod utils; +use crate::utils::CommandConfig; use node_lib::entry_dns::packet_facade::PacketFacade; use serial_test_derive::serial; use std::net::UdpSocket; @@ -9,13 +10,11 @@ use trust_dns::op::{OpCode, ResponseCode}; use trust_dns::rr::{DNSClass, RecordType}; #[test] -// TODO This ignore should be lifted by GH-525 -#[ignore] #[serial(port53)] fn handles_two_consecutive_ipv4_dns_requests_integration() { let _node = utils::MASQNode::start_standard( "handles_two_consecutive_ipv4_dns_requests_integration", - None, + Some(CommandConfig::new().opt("--entry-dns")), true, true, false, @@ -27,13 +26,11 @@ fn handles_two_consecutive_ipv4_dns_requests_integration() { } #[test] -// TODO This ignore should be lifted by GH-525 -#[ignore] #[serial(port53)] fn handles_consecutive_heterogeneous_dns_requests_integration() { let _node = utils::MASQNode::start_standard( "handles_consecutive_heterogeneous_dns_requests_integration", - None, + Some(CommandConfig::new().opt("--entry-dns")), true, true, false,