diff --git a/grin.toml b/grin.toml index 9595c0b91..3a3382345 100644 --- a/grin.toml +++ b/grin.toml @@ -31,7 +31,7 @@ db_root = ".grin" #If seeding_type = List, the list of peers to connect to. # -#seeds = ["192.168.0.1:8080","192.168.0.2:8080"] +#seeds = ["192.168.0.1:13414","192.168.0.2:13414"] #The chain type, which defines the genesis block and the set of cuckoo #parameters used for mining. Can be: @@ -57,6 +57,12 @@ capabilities = [7] host = "0.0.0.0" port = 13414 +#hardcoded peer lists for allow/deny +#will *only* connect to peers in allow list +#peers_allow = ["192.168.0.1:13414", "192.168.0.2:13414"] +#will *never* connect to peers in deny list +#peers_deny = ["192.168.0.3:13414", "192.168.0.4:13414"] + ######################################### ### LOGGING CONFIGURATION ### ######################################### diff --git a/grin/src/server.rs b/grin/src/server.rs index 3898258b7..3003a476c 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -123,10 +123,12 @@ impl Server { // work from the main run loop in p2p_server let cpu_pool = CpuPool::new(1); + let p2p_config = config.p2p_config.clone(); + let p2p_server = Arc::new(p2p::Server::new( config.db_root.clone(), config.capabilities, - config.p2p_config.unwrap(), + p2p_config, net_adapter.clone(), genesis.hash(), cpu_pool.clone(), @@ -188,7 +190,7 @@ impl Server { warn!(LOGGER, "Grin server started."); Ok(Server { - config: config, + config: config.clone(), evt_handle: evt_handle.clone(), p2p: p2p_server, chain: shared_chain, @@ -222,7 +224,7 @@ impl Server { let currently_syncing = self.currently_syncing.clone(); let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone()); - miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.unwrap().port)); + miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.port)); let _ = thread::Builder::new() .name("miner".to_string()) .spawn(move || { diff --git a/grin/src/types.rs b/grin/src/types.rs index c5a44ac4c..9b8d8d9c6 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -120,15 +120,17 @@ pub struct ServerConfig { #[serde(default)] pub seeding_type: Seeding, + /// TODO - move this into p2p_config? /// The list of seed nodes, if using Seeding as a seed type pub seeds: Option>, + /// TODO - move this into p2p_config? /// Capabilities expose by this node, also conditions which other peers this /// node will have an affinity toward when connection. pub capabilities: p2p::Capabilities, /// Configuration for the peer-to-peer server - pub p2p_config: Option, + pub p2p_config: p2p::P2PConfig, /// Configuration for the mining daemon pub mining_config: Option, @@ -150,7 +152,7 @@ impl Default for ServerConfig { capabilities: p2p::FULL_NODE, seeding_type: Seeding::default(), seeds: None, - p2p_config: Some(p2p::P2PConfig::default()), + p2p_config: p2p::P2PConfig::default(), mining_config: Some(pow::types::MinerConfig::default()), chain_type: ChainTypes::default(), pool_config: pool::PoolConfig::default(), @@ -159,7 +161,7 @@ impl Default for ServerConfig { } } -/// Thread-safe container to return all sever related stats that other +/// Thread-safe container to return all server related stats that other /// consumers might be interested in, such as test results /// /// diff --git a/grin/tests/framework/mod.rs b/grin/tests/framework/mod.rs index ffbbf357b..59e50676f 100644 --- a/grin/tests/framework/mod.rs +++ b/grin/tests/framework/mod.rs @@ -205,10 +205,10 @@ impl LocalServerContainer { grin::ServerConfig { api_http_addr: api_addr, db_root: format!("{}/.grin", self.working_dir), - p2p_config: Some(p2p::P2PConfig { + p2p_config: p2p::P2PConfig { port: self.config.p2p_server_port, ..p2p::P2PConfig::default() - }), + }, seeds: Some(seeds), seeding_type: seeding_type, chain_type: core::global::ChainTypes::AutomatedTesting, diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs index 87c93def6..f3f1216b1 100644 --- a/grin/tests/simulnet.rs +++ b/grin/tests/simulnet.rs @@ -216,10 +216,10 @@ fn a_simulate_block_propagation() { grin::ServerConfig { api_http_addr: format!("127.0.0.1:{}", 19000 + n), db_root: format!("target/{}/grin-prop-{}", test_name_dir, n), - p2p_config: Some(p2p::P2PConfig { + p2p_config: p2p::P2PConfig { port: 18000 + n, ..p2p::P2PConfig::default() - }), + }, seeding_type: grin::Seeding::List, seeds: Some(vec!["127.0.0.1:18000".to_string()]), chain_type: core::global::ChainTypes::AutomatedTesting, @@ -279,10 +279,10 @@ fn simulate_full_sync() { let config = grin::ServerConfig { api_http_addr: format!("127.0.0.1:{}", 19000 + n), db_root: format!("target/{}/grin-sync-{}", test_name_dir, n), - p2p_config: Some(p2p::P2PConfig { + p2p_config: p2p::P2PConfig { port: 11000 + n, ..p2p::P2PConfig::default() - }), + }, seeding_type: grin::Seeding::List, seeds: Some(vec!["127.0.0.1:11000".to_string()]), chain_type: core::global::ChainTypes::AutomatedTesting, diff --git a/p2p/src/handshake.rs b/p2p/src/handshake.rs index 733b04924..2159af93d 100644 --- a/p2p/src/handshake.rs +++ b/p2p/src/handshake.rs @@ -24,6 +24,7 @@ use tokio_core::net::TcpStream; use core::core::target::Difficulty; use core::core::hash::Hash; use msg::*; +use peer::Peer; use types::*; use protocol::ProtocolV1; use util::LOGGER; @@ -39,6 +40,7 @@ pub struct Handshake { /// The genesis block header of the chain seen by this node. /// We only want to connect to other nodes seeing the same chain (forks are ok). genesis: Hash, + config: P2PConfig, } unsafe impl Sync for Handshake {} @@ -46,10 +48,11 @@ unsafe impl Send for Handshake {} impl Handshake { /// Creates a new handshake handler - pub fn new(genesis: Hash) -> Handshake { + pub fn new(genesis: Hash, config: P2PConfig) -> Handshake { Handshake { nonces: Arc::new(RwLock::new(VecDeque::with_capacity(NONCES_CAP))), - genesis: genesis, + genesis, + config, } } @@ -80,7 +83,8 @@ impl Handshake { user_agent: USER_AGENT.to_string(), }; - let genesis = self.genesis; + let genesis = self.genesis.clone(); + let config = self.config.clone(); // write and read the handshake response Box::new( @@ -106,6 +110,12 @@ impl Handshake { total_difficulty: shake.total_difficulty, }; + // If denied then we want to close the connection + // (without providing our peer with any details why). + if Peer::is_denied(config, peer_info.addr) { + return Err(Error::ConnectionClose); + } + debug!( LOGGER, "Connected! Cumulative {} offered from {:?} {:?} {:?}", @@ -131,6 +141,8 @@ impl Handshake { ) -> Box> { let nonces = self.nonces.clone(); let genesis = self.genesis.clone(); + let config = self.config.clone(); + Box::new( read_msg::(conn) .and_then(move |(conn, hand)| { @@ -160,6 +172,15 @@ impl Handshake { version: hand.version, total_difficulty: hand.total_difficulty, }; + + // At this point we know the published ip and port of the peer + // so check if we are configured to explicitly allow or deny it. + // If denied then we want to close the connection + // (without providing our peer with any details why). + if Peer::is_denied(config, peer_info.addr) { + return Err(Error::ConnectionClose); + } + // send our reply with our info let shake = Shake { version: PROTOCOL_VERSION, diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs index 44d9ea6a6..6cb932742 100644 --- a/p2p/src/peer.rs +++ b/p2p/src/peer.rs @@ -118,6 +118,28 @@ impl Peer { })) } + pub fn is_denied(config: P2PConfig, peer_addr: SocketAddr) -> bool { + let peer = format!("{}:{}", peer_addr.ip(), peer_addr.port()); + if let Some(ref denied) = config.peers_deny { + if denied.contains(&peer) { + debug!(LOGGER, "checking peer allowed/denied: {:?} explicitly denied", peer_addr); + return true; + } + } + if let Some(ref allowed) = config.peers_allow { + if allowed.contains(&peer) { + debug!(LOGGER, "checking peer allowed/denied: {:?} explicitly allowed", peer_addr); + return false; + } else { + debug!(LOGGER, "checking peer allowed/denied: {:?} not explicitly allowed, denying", peer_addr); + return true; + } + } + + // default to allowing peer connection if we do not explicitly allow or deny the peer + false + } + /// Whether this peer is still connected. pub fn is_connected(&self) -> bool { let state = self.state.read().unwrap(); diff --git a/p2p/src/peers.rs b/p2p/src/peers.rs index 09e4e7859..366ac438e 100644 --- a/p2p/src/peers.rs +++ b/p2p/src/peers.rs @@ -33,17 +33,19 @@ pub struct Peers { pub adapter: Arc, store: Arc, peers: Arc>>>>, + config: P2PConfig, } unsafe impl Send for Peers {} unsafe impl Sync for Peers {} impl Peers { - pub fn new(store: PeerStore, adapter: Arc) -> Peers { + pub fn new(store: PeerStore, adapter: Arc, config: P2PConfig) -> Peers { Peers { - adapter: adapter, + adapter, store: Arc::new(store), peers: Arc::new(RwLock::new(HashMap::new())), + config, } } diff --git a/p2p/src/server.rs b/p2p/src/server.rs index b5d3c93f1..2157452c3 100644 --- a/p2p/src/server.rs +++ b/p2p/src/server.rs @@ -94,10 +94,10 @@ impl Server { pool: CpuPool, ) -> Result { Ok(Server { - config: config, + config: config.clone(), capabilities: capab, - handshake: Arc::new(Handshake::new(genesis)), - peers: Peers::new(PeerStore::new(db_root)?, adapter), + handshake: Arc::new(Handshake::new(genesis, config.clone())), + peers: Peers::new(PeerStore::new(db_root)?, adapter, config.clone()), pool: pool, stop: RefCell::new(None), }) @@ -127,7 +127,7 @@ impl Server { let pool = pool.clone(); future::ok(conn).and_then(move |conn| { - // Refuse banned peers connection + // Refuse connection from banned peers if let Ok(peer_addr) = conn.peer_addr() { if peers.is_banned(peer_addr) { debug!(LOGGER, "Peer {} banned, refusing connection.", peer_addr); @@ -139,7 +139,6 @@ impl Server { } Ok(conn) }).and_then(move |conn| { - let total_diff = peers2.total_difficulty(); // accept the peer and add it to the server map @@ -217,6 +216,11 @@ impl Server { h: reactor::Handle, ) -> Box>>, Error = Error>> { + if Peer::is_denied(self.config.clone(), addr) { + debug!(LOGGER, "Peer {} denied, not connecting.", addr); + return Box::new(future::err(Error::ConnectionClose)); + } + if let Some(p) = self.peers.get_connected_peer(&addr) { // if we're already connected to the addr, just return the peer debug!(LOGGER, "connect_peer: already connected {}", addr); diff --git a/p2p/src/types.rs b/p2p/src/types.rs index 71cda1a7c..4a7fa3b62 100644 --- a/p2p/src/types.rs +++ b/p2p/src/types.rs @@ -79,10 +79,14 @@ impl From for Error { } /// Configuration for the peer-to-peer server. -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct P2PConfig { pub host: IpAddr, pub port: u16, + + pub peers_allow: Option>, + + pub peers_deny: Option>, } /// Default address for peer-to-peer connections. @@ -92,6 +96,8 @@ impl Default for P2PConfig { P2PConfig { host: ipaddr, port: 13414, + peers_allow: None, + peers_deny: None, } } } diff --git a/p2p/tests/peer_handshake.rs b/p2p/tests/peer_handshake.rs index 9ff8b584a..aca347ca2 100644 --- a/p2p/tests/peer_handshake.rs +++ b/p2p/tests/peer_handshake.rs @@ -48,14 +48,16 @@ fn peer_handshake() { let handle = evtlp.handle(); let p2p_conf = p2p::P2PConfig { host: "0.0.0.0".parse().unwrap(), - port: open_port() + port: open_port(), + peers_allow: None, + peers_deny: None, }; let net_adapter = Arc::new(p2p::DummyAdapter {}); let pool = CpuPool::new(1); let server = p2p::Server::new( ".grin".to_owned(), p2p::UNKNOWN, - p2p_conf, + p2p_conf.clone(), net_adapter.clone(), Hash::from_vec(vec![]), pool.clone(), @@ -71,7 +73,8 @@ fn peer_handshake() { timeout .from_err() .and_then(move |_| { - let addr = SocketAddr::new(p2p_conf.host, p2p_conf.port); + let conf = p2p_conf.clone(); + let addr = SocketAddr::new(conf.host, conf.port); let socket = TcpStream::connect(&addr, &phandle).map_err(|e| p2p::Error::Connection(e)); socket @@ -81,7 +84,12 @@ fn peer_handshake() { p2p::UNKNOWN, Difficulty::one(), my_addr, - Arc::new(p2p::handshake::Handshake::new(Hash::from_vec(vec![]))), + Arc::new( + p2p::handshake::Handshake::new( + Hash::from_vec(vec![]), + p2p_conf.clone(), + ), + ), net_adapter.clone(), ) }) diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 3eb465856..d00944a4b 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -320,7 +320,7 @@ fn server_command(server_args: &ArgMatches, global_config: GlobalConfig) { let mut server_config = global_config.members.unwrap().server; if let Some(port) = server_args.value_of("port") { - server_config.p2p_config.as_mut().unwrap().port = port.parse().unwrap(); + server_config.p2p_config.port = port.parse().unwrap(); } if let Some(api_port) = server_args.value_of("api_port") {