Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map peers by ip only (ignoring port unless on loopback ip) #2540

Merged
merged 7 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions api/src/handlers/peers_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use super::utils::w;
use crate::p2p;
use crate::p2p::types::{PeerInfoDisplay, ReasonForBan};
use crate::p2p::types::{PeerAddr, PeerInfoDisplay, ReasonForBan};
use crate::router::{Handler, ResponseFuture};
use crate::web::*;
use hyper::{Body, Request, StatusCode};
Expand Down Expand Up @@ -57,16 +57,25 @@ pub struct PeerHandler {
impl Handler for PeerHandler {
fn get(&self, req: Request<Body>) -> ResponseFuture {
let command = right_path_element!(req);
if let Ok(addr) = command.parse() {
match w(&self.peers).get_peer(addr) {
Ok(peer) => json_response(&peer),
Err(_) => response(StatusCode::NOT_FOUND, "peer not found"),
}

// We support both "ip" and "ip:port" here for peer_addr.
// "ip:port" is only really useful for local usernet testing on loopback address.
// Normally we map peers to ip and only allow a single peer per ip address.
let peer_addr;
if let Ok(ip_addr) = command.parse() {
peer_addr = PeerAddr::from_ip(ip_addr);
} else if let Ok(addr) = command.parse() {
peer_addr = PeerAddr(addr);
} else {
response(
return response(
StatusCode::BAD_REQUEST,
format!("peer address unrecognized: {}", req.uri().path()),
)
);
}

match w(&self.peers).get_peer(peer_addr) {
Ok(peer) => json_response(&peer),
Err(_) => response(StatusCode::NOT_FOUND, "peer not found"),
}
}
fn post(&self, req: Request<Body>) -> ResponseFuture {
Expand All @@ -77,20 +86,23 @@ impl Handler for PeerHandler {
};
let addr = match path_elems.next() {
None => return response(StatusCode::BAD_REQUEST, "invalid url"),
Some(a) => match a.parse() {
Err(e) => {
Some(a) => {
if let Ok(ip_addr) = a.parse() {
PeerAddr::from_ip(ip_addr)
} else if let Ok(addr) = a.parse() {
PeerAddr(addr)
} else {
return response(
StatusCode::BAD_REQUEST,
format!("invalid peer address: {}", e),
format!("invalid peer address: {}", req.uri().path()),
);
}
Ok(addr) => addr,
},
}
};

match command {
"ban" => w(&self.peers).ban_peer(&addr, ReasonForBan::ManualBan),
"unban" => w(&self.peers).unban_peer(&addr),
"ban" => w(&self.peers).ban_peer(addr, ReasonForBan::ManualBan),
"unban" => w(&self.peers).unban_peer(addr),
_ => return response(StatusCode::BAD_REQUEST, "invalid command"),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ extern crate grin_core;
extern crate grin_p2p;

use grin_core::ser;
use grin_p2p::msg::SockAddr;
use grin_p2p::types::PeerAddr;

fuzz_target!(|data: &[u8]| {
let mut d = data.clone();
let _t: Result<SockAddr, ser::Error> = ser::deserialize(&mut d);
let _t: Result<PeerAddr, ser::Error> = ser::deserialize(&mut d);
});
54 changes: 18 additions & 36 deletions p2p/src/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ use rand::{thread_rng, Rng};

use crate::core::core::hash::Hash;
use crate::core::pow::Difficulty;
use crate::msg::{
read_message, write_message, Hand, Shake, SockAddr, Type, PROTOCOL_VERSION, USER_AGENT,
};
use crate::msg::{read_message, write_message, Hand, Shake, Type, PROTOCOL_VERSION, USER_AGENT};
use crate::peer::Peer;
use crate::types::{Capabilities, Direction, Error, P2PConfig, PeerInfo, PeerLiveInfo};
use crate::types::{Capabilities, Direction, Error, P2PConfig, PeerAddr, PeerInfo, PeerLiveInfo};

/// Local generated nonce for peer connecting.
/// Used for self-connecting detection (on receiver side),
Expand All @@ -44,7 +42,7 @@ pub struct Handshake {
/// a node id.
nonces: Arc<RwLock<VecDeque<u64>>>,
/// Ring buffer of self addr(s) collected from PeerWithSelf detection (by nonce).
pub addrs: Arc<RwLock<VecDeque<SocketAddr>>>,
pub addrs: Arc<RwLock<VecDeque<PeerAddr>>>,
/// 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).
Expand All @@ -67,13 +65,13 @@ impl Handshake {
&self,
capab: Capabilities,
total_difficulty: Difficulty,
self_addr: SocketAddr,
self_addr: PeerAddr,
conn: &mut TcpStream,
) -> Result<PeerInfo, Error> {
// prepare the first part of the handshake
let nonce = self.next_nonce();
let peer_addr = match conn.peer_addr() {
Ok(pa) => pa,
Ok(pa) => PeerAddr(pa),
Err(e) => return Err(Error::Connection(e)),
};

Expand All @@ -83,8 +81,8 @@ impl Handshake {
nonce: nonce,
genesis: self.genesis,
total_difficulty: total_difficulty,
sender_addr: SockAddr(self_addr),
receiver_addr: SockAddr(peer_addr),
sender_addr: self_addr,
receiver_addr: peer_addr,
user_agent: USER_AGENT.to_string(),
};

Expand Down Expand Up @@ -118,7 +116,7 @@ impl Handshake {

// If denied then we want to close the connection
// (without providing our peer with any details why).
if Peer::is_denied(&self.config, &peer_info.addr) {
if Peer::is_denied(&self.config, peer_info.addr) {
return Err(Error::ConnectionClose);
}

Expand Down Expand Up @@ -155,7 +153,7 @@ impl Handshake {
} else {
// check the nonce to see if we are trying to connect to ourselves
let nonces = self.nonces.read();
let addr = extract_ip(&hand.sender_addr.0, &conn);
let addr = resolve_peer_addr(hand.sender_addr, &conn);
if nonces.contains(&hand.nonce) {
// save ip addresses of ourselves
let mut addrs = self.addrs.write();
Expand All @@ -171,7 +169,7 @@ impl Handshake {
let peer_info = PeerInfo {
capabilities: hand.capabilities,
user_agent: hand.user_agent,
addr: extract_ip(&hand.sender_addr.0, &conn),
addr: resolve_peer_addr(hand.sender_addr, &conn),
version: hand.version,
live_info: Arc::new(RwLock::new(PeerLiveInfo {
total_difficulty: hand.total_difficulty,
Expand All @@ -186,7 +184,7 @@ impl Handshake {
// 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(&self.config, &peer_info.addr) {
if Peer::is_denied(&self.config, peer_info.addr) {
return Err(Error::ConnectionClose);
}

Expand Down Expand Up @@ -219,28 +217,12 @@ impl Handshake {
}
}

// Attempts to make a best guess at the correct remote IP by checking if the
// advertised address is the loopback and our TCP connection. Note that the
// port reported by the connection is always incorrect for receiving
// connections as it's dynamically allocated by the server.
fn extract_ip(advertised: &SocketAddr, conn: &TcpStream) -> SocketAddr {
match advertised {
&SocketAddr::V4(v4sock) => {
let ip = v4sock.ip();
if ip.is_loopback() || ip.is_unspecified() {
if let Ok(addr) = conn.peer_addr() {
return SocketAddr::new(addr.ip(), advertised.port());
}
}
}
&SocketAddr::V6(v6sock) => {
let ip = v6sock.ip();
if ip.is_loopback() || ip.is_unspecified() {
if let Ok(addr) = conn.peer_addr() {
return SocketAddr::new(addr.ip(), advertised.port());
}
}
}
/// Resolve the correct peer_addr based on the connection and the advertised port.
fn resolve_peer_addr(advertised: PeerAddr, conn: &TcpStream) -> PeerAddr {
let port = advertised.0.port();
if let Ok(addr) = conn.peer_addr() {
PeerAddr(SocketAddr::new(addr.ip(), port))
} else {
advertised
}
advertised.clone()
}
4 changes: 2 additions & 2 deletions p2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ pub use crate::peers::Peers;
pub use crate::serv::{DummyAdapter, Server};
pub use crate::store::{PeerData, State};
pub use crate::types::{
Capabilities, ChainAdapter, Direction, Error, P2PConfig, PeerInfo, ReasonForBan, Seeding,
TxHashSetRead, MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
Capabilities, ChainAdapter, Direction, Error, P2PConfig, PeerAddr, PeerInfo, ReasonForBan,
Seeding, TxHashSetRead, MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
};
68 changes: 7 additions & 61 deletions p2p/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

use num::FromPrimitive;
use std::io::{Read, Write};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::time;

use crate::core::core::hash::Hash;
Expand All @@ -25,7 +24,7 @@ use crate::core::pow::Difficulty;
use crate::core::ser::{self, FixedLength, Readable, Reader, StreamingReader, Writeable, Writer};
use crate::core::{consensus, global};
use crate::types::{
Capabilities, Error, ReasonForBan, MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
Capabilities, Error, PeerAddr, ReasonForBan, MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
};
use crate::util::read_write::read_exact;

Expand Down Expand Up @@ -254,9 +253,9 @@ pub struct Hand {
/// may be needed
pub total_difficulty: Difficulty,
/// network address of the sender
pub sender_addr: SockAddr,
pub sender_addr: PeerAddr,
/// network address of the receiver
pub receiver_addr: SockAddr,
pub receiver_addr: PeerAddr,
/// name of version of the software
pub user_agent: String,
}
Expand All @@ -283,8 +282,8 @@ impl Readable for Hand {
let (version, capab, nonce) = ser_multiread!(reader, read_u32, read_u32, read_u64);
let capabilities = Capabilities::from_bits_truncate(capab);
let total_diff = Difficulty::read(reader)?;
let sender_addr = SockAddr::read(reader)?;
let receiver_addr = SockAddr::read(reader)?;
let sender_addr = PeerAddr::read(reader)?;
let receiver_addr = PeerAddr::read(reader)?;
let ua = reader.read_bytes_len_prefix()?;
let user_agent = String::from_utf8(ua).map_err(|_| ser::Error::CorruptedData)?;
let genesis = Hash::read(reader)?;
Expand Down Expand Up @@ -373,7 +372,7 @@ impl Readable for GetPeerAddrs {
/// GetPeerAddrs.
#[derive(Debug)]
pub struct PeerAddrs {
pub peers: Vec<SockAddr>,
pub peers: Vec<PeerAddr>,
}

impl Writeable for PeerAddrs {
Expand All @@ -394,10 +393,9 @@ impl Readable for PeerAddrs {
} else if peer_count == 0 {
return Ok(PeerAddrs { peers: vec![] });
}
// let peers = try_map_vec!([0..peer_count], |_| SockAddr::read(reader));
let mut peers = Vec::with_capacity(peer_count as usize);
for _ in 0..peer_count {
peers.push(SockAddr::read(reader)?);
peers.push(PeerAddr::read(reader)?);
}
Ok(PeerAddrs { peers: peers })
}
Expand Down Expand Up @@ -431,58 +429,6 @@ impl Readable for PeerError {
}
}

/// Only necessary so we can implement Readable and Writeable. Rust disallows
/// implementing traits when both types are outside of this crate (which is the
/// case for SocketAddr and Readable/Writeable).
#[derive(Debug)]
pub struct SockAddr(pub SocketAddr);

impl Writeable for SockAddr {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
match self.0 {
SocketAddr::V4(sav4) => {
ser_multiwrite!(
writer,
[write_u8, 0],
[write_fixed_bytes, &sav4.ip().octets().to_vec()],
[write_u16, sav4.port()]
);
}
SocketAddr::V6(sav6) => {
writer.write_u8(1)?;
for seg in &sav6.ip().segments() {
writer.write_u16(*seg)?;
}
writer.write_u16(sav6.port())?;
}
}
Ok(())
}
}

impl Readable for SockAddr {
fn read(reader: &mut dyn Reader) -> Result<SockAddr, ser::Error> {
let v4_or_v6 = reader.read_u8()?;
if v4_or_v6 == 0 {
let ip = reader.read_fixed_bytes(4)?;
let port = reader.read_u16()?;
Ok(SockAddr(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]),
port,
))))
} else {
let ip = try_iter_map_vec!(0..8, |_| reader.read_u16());
let port = reader.read_u16()?;
Ok(SockAddr(SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
port,
0,
0,
))))
}
}
}

/// Serializable wrapper for the block locator.
#[derive(Debug)]
pub struct Locator {
Expand Down
Loading