diff --git a/api/src/handlers/peers_api.rs b/api/src/handlers/peers_api.rs
index 692245dbc..6453432fb 100644
--- a/api/src/handlers/peers_api.rs
+++ b/api/src/handlers/peers_api.rs
@@ -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};
@@ -57,16 +57,25 @@ pub struct PeerHandler {
impl Handler for PeerHandler {
fn get(&self, req: Request
) -> 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) -> ResponseFuture {
@@ -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"),
};
diff --git a/p2p/fuzz/fuzz_targets/read_sock_addr.rs b/p2p/fuzz/fuzz_targets/read_peer_addr.rs
similarity index 65%
rename from p2p/fuzz/fuzz_targets/read_sock_addr.rs
rename to p2p/fuzz/fuzz_targets/read_peer_addr.rs
index cbe9a26ea..723634a1a 100644
--- a/p2p/fuzz/fuzz_targets/read_sock_addr.rs
+++ b/p2p/fuzz/fuzz_targets/read_peer_addr.rs
@@ -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 = ser::deserialize(&mut d);
+ let _t: Result = ser::deserialize(&mut d);
});
diff --git a/p2p/src/handshake.rs b/p2p/src/handshake.rs
index cd0ea197a..dbb1b14da 100644
--- a/p2p/src/handshake.rs
+++ b/p2p/src/handshake.rs
@@ -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),
@@ -44,7 +42,7 @@ pub struct Handshake {
/// a node id.
nonces: Arc>>,
/// Ring buffer of self addr(s) collected from PeerWithSelf detection (by nonce).
- pub addrs: Arc>>,
+ pub addrs: Arc>>,
/// 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).
@@ -67,13 +65,13 @@ impl Handshake {
&self,
capab: Capabilities,
total_difficulty: Difficulty,
- self_addr: SocketAddr,
+ self_addr: PeerAddr,
conn: &mut TcpStream,
) -> Result {
// 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)),
};
@@ -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(),
};
@@ -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);
}
@@ -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();
@@ -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,
@@ -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);
}
@@ -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()
}
diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs
index 4f967b1fb..3ba530201 100644
--- a/p2p/src/lib.rs
+++ b/p2p/src/lib.rs
@@ -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,
};
diff --git a/p2p/src/msg.rs b/p2p/src/msg.rs
index 3f9a6d733..8e7b5d151 100644
--- a/p2p/src/msg.rs
+++ b/p2p/src/msg.rs
@@ -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;
@@ -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;
@@ -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,
}
@@ -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)?;
@@ -373,7 +372,7 @@ impl Readable for GetPeerAddrs {
/// GetPeerAddrs.
#[derive(Debug)]
pub struct PeerAddrs {
- pub peers: Vec,
+ pub peers: Vec,
}
impl Writeable for PeerAddrs {
@@ -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 })
}
@@ -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(&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 {
- 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 {
diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs
index eecae9b25..1b50bf59a 100644
--- a/p2p/src/peer.rs
+++ b/p2p/src/peer.rs
@@ -14,7 +14,7 @@
use crate::util::{Mutex, RwLock};
use std::fs::File;
-use std::net::{Shutdown, SocketAddr, TcpStream};
+use std::net::{Shutdown, TcpStream};
use std::sync::Arc;
use crate::conn;
@@ -25,7 +25,8 @@ use crate::handshake::Handshake;
use crate::msg::{self, BanReason, GetPeerAddrs, Locator, Ping, TxHashSetRequest};
use crate::protocol::Protocol;
use crate::types::{
- Capabilities, ChainAdapter, Error, NetAdapter, P2PConfig, PeerInfo, ReasonForBan, TxHashSetRead,
+ Capabilities, ChainAdapter, Error, NetAdapter, P2PConfig, PeerAddr, PeerInfo, ReasonForBan,
+ TxHashSetRead,
};
use chrono::prelude::{DateTime, Utc};
@@ -93,7 +94,7 @@ impl Peer {
conn: &mut TcpStream,
capab: Capabilities,
total_difficulty: Difficulty,
- self_addr: SocketAddr,
+ self_addr: PeerAddr,
hs: &Handshake,
na: Arc,
) -> Result {
@@ -124,10 +125,9 @@ impl Peer {
self.connection = Some(Mutex::new(conn::listen(conn, handler)));
}
- pub fn is_denied(config: &P2PConfig, peer_addr: &SocketAddr) -> bool {
- let peer = format!("{}:{}", peer_addr.ip(), peer_addr.port());
+ pub fn is_denied(config: &P2PConfig, peer_addr: PeerAddr) -> bool {
if let Some(ref denied) = config.peers_deny {
- if denied.contains(&peer) {
+ if denied.contains(&peer_addr) {
debug!(
"checking peer allowed/denied: {:?} explicitly denied",
peer_addr
@@ -136,7 +136,7 @@ impl Peer {
}
}
if let Some(ref allowed) = config.peers_allow {
- if allowed.contains(&peer) {
+ if allowed.contains(&peer_addr) {
debug!(
"checking peer allowed/denied: {:?} explicitly allowed",
peer_addr
@@ -566,7 +566,7 @@ impl ChainAdapter for TrackingAdapter {
self.adapter.get_transaction(kernel_hash)
}
- fn tx_kernel_received(&self, kernel_hash: Hash, addr: SocketAddr) {
+ fn tx_kernel_received(&self, kernel_hash: Hash, addr: PeerAddr) {
self.push_recv(kernel_hash);
self.adapter.tx_kernel_received(kernel_hash, addr)
}
@@ -582,23 +582,23 @@ impl ChainAdapter for TrackingAdapter {
self.adapter.transaction_received(tx, stem)
}
- fn block_received(&self, b: core::Block, addr: SocketAddr, _was_requested: bool) -> bool {
+ fn block_received(&self, b: core::Block, addr: PeerAddr, _was_requested: bool) -> bool {
let bh = b.hash();
self.push_recv(bh);
self.adapter.block_received(b, addr, self.has_req(bh))
}
- fn compact_block_received(&self, cb: core::CompactBlock, addr: SocketAddr) -> bool {
+ fn compact_block_received(&self, cb: core::CompactBlock, addr: PeerAddr) -> bool {
self.push_recv(cb.hash());
self.adapter.compact_block_received(cb, addr)
}
- fn header_received(&self, bh: core::BlockHeader, addr: SocketAddr) -> bool {
+ fn header_received(&self, bh: core::BlockHeader, addr: PeerAddr) -> bool {
self.push_recv(bh.hash());
self.adapter.header_received(bh, addr)
}
- fn headers_received(&self, bh: &[core::BlockHeader], addr: SocketAddr) -> bool {
+ fn headers_received(&self, bh: &[core::BlockHeader], addr: PeerAddr) -> bool {
self.adapter.headers_received(bh, addr)
}
@@ -618,7 +618,7 @@ impl ChainAdapter for TrackingAdapter {
self.adapter.txhashset_receive_ready()
}
- fn txhashset_write(&self, h: Hash, txhashset_data: File, peer_addr: SocketAddr) -> bool {
+ fn txhashset_write(&self, h: Hash, txhashset_data: File, peer_addr: PeerAddr) -> bool {
self.adapter.txhashset_write(h, txhashset_data, peer_addr)
}
@@ -634,19 +634,19 @@ impl ChainAdapter for TrackingAdapter {
}
impl NetAdapter for TrackingAdapter {
- fn find_peer_addrs(&self, capab: Capabilities) -> Vec {
+ fn find_peer_addrs(&self, capab: Capabilities) -> Vec {
self.adapter.find_peer_addrs(capab)
}
- fn peer_addrs_received(&self, addrs: Vec) {
+ fn peer_addrs_received(&self, addrs: Vec) {
self.adapter.peer_addrs_received(addrs)
}
- fn peer_difficulty(&self, addr: SocketAddr, diff: Difficulty, height: u64) {
+ fn peer_difficulty(&self, addr: PeerAddr, diff: Difficulty, height: u64) {
self.adapter.peer_difficulty(addr, diff, height)
}
- fn is_banned(&self, addr: SocketAddr) -> bool {
+ fn is_banned(&self, addr: PeerAddr) -> bool {
self.adapter.is_banned(addr)
}
}
diff --git a/p2p/src/peers.rs b/p2p/src/peers.rs
index f60abbc4c..19713b7d8 100644
--- a/p2p/src/peers.rs
+++ b/p2p/src/peers.rs
@@ -15,7 +15,6 @@
use crate::util::RwLock;
use std::collections::HashMap;
use std::fs::File;
-use std::net::SocketAddr;
use std::sync::Arc;
use rand::{thread_rng, Rng};
@@ -30,14 +29,14 @@ use chrono::Duration;
use crate::peer::Peer;
use crate::store::{PeerData, PeerStore, State};
use crate::types::{
- Capabilities, ChainAdapter, Direction, Error, NetAdapter, P2PConfig, ReasonForBan,
+ Capabilities, ChainAdapter, Direction, Error, NetAdapter, P2PConfig, PeerAddr, ReasonForBan,
TxHashSetRead, MAX_PEER_ADDRS,
};
pub struct Peers {
pub adapter: Arc,
store: PeerStore,
- peers: RwLock>>,
+ peers: RwLock>>,
dandelion_relay: RwLock