diff --git a/dns_utility/tests/inspect_and_status_test_windows.rs b/dns_utility/tests/inspect_and_status_test_windows.rs index 7c9ae9e39..da5c35dda 100644 --- a/dns_utility/tests/inspect_and_status_test_windows.rs +++ b/dns_utility/tests/inspect_and_status_test_windows.rs @@ -11,7 +11,11 @@ use utils::TestCommand; // Any integration tests that should be run without root should have names ending in '_user_integration' fn winreg_inspect_and_status_user_integration() { let modifier = WinDnsModifier::default(); - let interfaces = modifier.find_interfaces_to_inspect().unwrap(); + let interfaces = match modifier.find_interfaces_to_inspect() { + Ok(interfaces) => interfaces, + Err(e) if e.contains("active network interfaces configured with") => return, + Err(e) => panic!("Didn't expect to stop for this error: {:?}", e), + }; let dns_server_list_csv = modifier.find_dns_server_list(interfaces).unwrap(); let dns_server_list = dns_server_list_csv.split(","); let expected_inspect_output = dns_server_list diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 1bd9cbaab..eb815a840 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -320,7 +320,7 @@ fn assert_balances( "TokenBalance" ); } else { - assert!(false, "Failed to retrieve balances {}", wallet); + panic!("Failed to retrieve balances {}", wallet); } } diff --git a/node/Cargo.lock b/node/Cargo.lock index ab1c8b4ce..9ad02ee3b 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -1427,7 +1427,7 @@ dependencies = [ "httpdate", "itoa 0.4.6", "pin-project", - "socket2 0.4.2", + "socket2 0.4.4", "tokio 1.15.0", "tower-service", "tracing", @@ -1666,9 +1666,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libsecp256k1" @@ -2161,6 +2161,7 @@ dependencies = [ "serial_test_derive", "sha1", "simple-server", + "socket2 0.4.4", "sodiumoxide", "sysinfo", "system-configuration", @@ -3385,9 +3386,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi 0.3.9", diff --git a/node/Cargo.toml b/node/Cargo.toml index a69954047..cd55d2a75 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -50,6 +50,7 @@ serde_derive = "1.0.130" serde_json = "1.0.69" serde_cbor = "0.11.2" sha1 = "0.6.0" +socket2 = { version = "0.4.4", features = ["all"] } sodiumoxide = "0.2.2" sysinfo = "0.21.1" tiny-bip39 = "0.8.2" diff --git a/node/src/sub_lib/mod.rs b/node/src/sub_lib/mod.rs index f69ad092d..602cb32d5 100644 --- a/node/src/sub_lib/mod.rs +++ b/node/src/sub_lib/mod.rs @@ -42,6 +42,7 @@ pub mod tcp_wrappers; pub mod tls_framer; pub mod tokio_wrappers; pub mod ttl_hashmap; +pub mod udp_multicast; pub mod udp_socket_wrapper; pub mod ui_gateway; pub mod wallet; diff --git a/node/src/sub_lib/udp_multicast.rs b/node/src/sub_lib/udp_multicast.rs new file mode 100644 index 000000000..bbd179797 --- /dev/null +++ b/node/src/sub_lib/udp_multicast.rs @@ -0,0 +1,174 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +#[allow(unused_imports)] +use crossbeam_channel::unbounded; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; +#[allow(unused_imports)] +use std::thread; +#[allow(unused_imports)] +use std::time::Duration; + +#[allow(dead_code)] +//multicast IP address must that is shared between any number of subscribers +const MULTICAST_GROUP_ADDRESS: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 2); +//port that multicast group subscribers will bind to and communicate with +const MCAST_PORT: u16 = 8888; +//unspecified interface here resolves into any available interface, if multiple interfaces are present it will try to select "default" interface first +const MCAST_INTERFACE: Ipv4Addr = Ipv4Addr::UNSPECIFIED; + +//abstracted out to have a common creation path +#[allow(dead_code)] +fn create_socket() -> UdpSocket { + //creates new UDP socket on ipv4 address + let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)) + .expect("could not create socket!"); + //linux/macos have reuse_port exposed so we can flag it for non-windows systems + #[cfg(not(target_os = "windows"))] + socket.set_reuse_port(true).unwrap(); + //windows has reuse_port hidden and implicitly flagged with reuse_address + socket.set_reuse_address(true).unwrap(); + //subscribes to multicast group on the unspecified interface + socket + .join_multicast_v4(&MULTICAST_GROUP_ADDRESS, &MCAST_INTERFACE) + .unwrap(); + //binds to the multicast interface and port + socket + .bind(&SockAddr::from(SocketAddr::new( + IpAddr::from(MCAST_INTERFACE), + MCAST_PORT, + ))) + .unwrap(); + //converts socket2 sicket into a std::net socket, required for correct recv_from method + let socket: UdpSocket = socket.into(); + socket +} + +#[allow(dead_code)] +fn run_receiver() { + //creates socket + let socket = create_socket(); + //sets buffer type/size, change as needed (64 bytes is fine for a small message, but UDP info from router/app will be much larger) + let mut buffer = [0; 64]; + //easy way to run 10 receives + (0..10).for_each(|x| { + let message = format!("Test message {} for MASQ UDP multicast", x); + //receives message from socket + match socket.recv_from(&mut buffer) { + Ok((len, _remote_addr)) => { + let data = &buffer[..len]; + let response = std::str::from_utf8(data).unwrap(); + + eprintln!("{}: Received on receiver1: {:?}", x, response); + assert_eq!(response, message) + } + Err(err) => { + println!("client: had a problem: {}", err); + panic!(); + } + } + }) +} + +#[allow(dead_code)] +fn run_sender() { + //socket address to use for send_to later on, must be the same multicast group and port we set for the receiver + let addr = &SockAddr::from(SocketAddr::new(MULTICAST_GROUP_ADDRESS.into(), MCAST_PORT)); + //creates socket + let socket = create_socket(); + //easy way to send 10 messages + (0..10).for_each(|x| { + println!("sending multicast message to group"); + let message = format!("Test message {} for MASQ UDP multicast", x); + //sends message as bytes to the socket address we set earlier + socket + .send_to(message.as_bytes(), &addr.as_socket().unwrap()) + .expect("could not send_to!"); + }) +} + +//crossbeam_channel is extremely fast and able to confirm that sender/receiver both work correctly, but cannot have multiple receivers +#[test] +fn singlecast_udp_test() { + let (sender, receiver) = unbounded(); + thread::spawn(move || { + receiver.recv().unwrap(); + run_sender() + }); + sender.send(()).unwrap(); + run_receiver() +} + +#[test] +fn multicast_udp_test() { + //creates 3 receiver sockets, probably a more elegant way to do this. + let receiver1 = create_socket(); + let receiver2 = create_socket(); + let receiver3 = create_socket(); + //creates socket to send + let socket = create_socket(); + let mut buffer1 = [0; 64]; + let mut buffer2 = [0; 64]; + let mut buffer3 = [0; 64]; + //socket address to use for send_to later on, must be the same multicast group and port we set for the receiver + let addr = &SockAddr::from(SocketAddr::new(MULTICAST_GROUP_ADDRESS.into(), MCAST_PORT)); + receiver1 + .set_read_timeout(Some(Duration::from_millis(100))) + .expect("could not set read timeout"); + receiver2 + .set_read_timeout(Some(Duration::from_millis(100))) + .expect("could not set read timeout"); + receiver3 + .set_read_timeout(Some(Duration::from_millis(100))) + .expect("could not set read timeout"); + //easy way to send/receive 10 messages + (0..10).for_each(|x| { + println!("sending multicast message to group"); + let message = format!("Test message {} for MASQ UDP multicast", x); + //sends message as bytes to socket address + socket + .send_to(message.as_bytes(), &addr.as_socket().unwrap()) + .expect("could not send_to!"); + //receives message from socket for receiver1 + match receiver1.recv_from(&mut buffer1) { + Ok((len, _remote_addr)) => { + let data = &buffer1[..len]; + let response = std::str::from_utf8(data).unwrap(); + + eprintln!("{}: Received on receiver1: {:?}", x, response); + assert_eq!(response, message) + } + Err(err) => { + println!("client: had a problem: {}", err); + panic!() + } + } + //receives message from socket for receiver2 + match receiver2.recv_from(&mut buffer2) { + Ok((len, _remote_addr)) => { + let data = &buffer2[..len]; + let response = std::str::from_utf8(data).unwrap(); + + eprintln!("{}: Received on receiver2: {:?}", x, response); + assert_eq!(response, message) + } + Err(err) => { + println!("client: had a problem: {}", err); + panic!(); + } + } + //receives message from socket for receiver3 + match receiver3.recv_from(&mut buffer3) { + Ok((len, _remote_addr)) => { + let data = &buffer3[..len]; + let response = std::str::from_utf8(data).unwrap(); + + eprintln!("{}: Received on receiver3: {:?}", x, response); + assert_eq!(response, message) + } + Err(err) => { + println!("client: had a problem: {}", err); + panic!(); + } + } + }) +} diff --git a/node/src/sub_lib/wallet.rs b/node/src/sub_lib/wallet.rs index 4cbea1df1..969d25396 100644 --- a/node/src/sub_lib/wallet.rs +++ b/node/src/sub_lib/wallet.rs @@ -707,7 +707,7 @@ mod tests { assert_ne!(actual.kind, expected.kind); match actual.kind { WalletKind::Address(address) => assert_eq!(address, expected.address()), - _ => assert!(false, "Failed to match expected WalletKind::Address"), + _ => panic!("Failed to match expected WalletKind::Address"), } }