First working test with related fixes demonstrating a full peer handshake.

This commit is contained in:
Ignotus Peverell 2016-10-27 14:28:02 -07:00
parent a9dc8a05ac
commit fdaf2ba6af
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
8 changed files with 158 additions and 106 deletions

View file

@ -12,3 +12,6 @@ mioco = "^0.8"
time = "^0.1"
grin_core = { path = "../core" }
[dev-dependencies]
env_logger = "^0.3"

View file

@ -34,8 +34,8 @@ pub struct Handshake {
nonces: RwLock<VecDeque<u64>>,
}
unsafe impl Sync for Handshake{}
unsafe impl Send for Handshake{}
unsafe impl Sync for Handshake {}
unsafe impl Send for Handshake {}
impl Handshake {
/// Creates a new handshake handler
@ -44,7 +44,7 @@ impl Handshake {
}
/// Handles connecting to a new remote peer, starting the version handshake.
pub fn connect<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol+'a>, Error> {
pub fn connect<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol + 'a>, Error> {
// get a new nonce that can be used on handshake to detect self-connection
let nonce = self.next_nonce();
@ -68,30 +68,39 @@ impl Handshake {
// deserialize the handshake response and do version negotiation
let shake = try!(deserialize::<Shake>(peer));
if shake.version != 1 {
self.close(peer, ErrCodes::UnsupportedVersion as u32,
self.close(peer,
ErrCodes::UnsupportedVersion as u32,
format!("Unsupported version: {}, ours: {})",
shake.version,
PROTOCOL_VERSION));
return Err(Error::UnexpectedData{expected: vec![PROTOCOL_VERSION as u8], received: vec![shake.version as u8]});
return Err(Error::UnexpectedData {
expected: vec![PROTOCOL_VERSION as u8],
received: vec![shake.version as u8],
});
}
peer.capabilities = shake.capabilities;
peer.user_agent = shake.user_agent;
info!("Connected to peer {}", peer);
// when more than one protocol version is supported, choosing should go here
Ok(Box::new(ProtocolV1::new(peer)))
}
/// Handles receiving a connection from a new remote peer that started the
/// version handshake.
pub fn handshake<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol+'a>, Error> {
pub fn handshake<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol + 'a>, Error> {
// deserialize first part of handshake sent to us and do version negotiation
let hand = try!(deserialize::<Hand>(peer));
if hand.version != 1 {
self.close(peer, ErrCodes::UnsupportedVersion as u32,
self.close(peer,
ErrCodes::UnsupportedVersion as u32,
format!("Unsupported version: {}, ours: {})",
hand.version,
PROTOCOL_VERSION));
return Err(Error::UnexpectedData{expected: vec![PROTOCOL_VERSION as u8], received: vec![hand.version as u8]});
return Err(Error::UnexpectedData {
expected: vec![PROTOCOL_VERSION as u8],
received: vec![hand.version as u8],
});
}
{
// check the nonce to see if we could be trying to connect to ourselves
@ -120,6 +129,7 @@ impl Handshake {
None => {}
}
info!("Received connection from peer {}", peer);
// when more than one protocol version is supported, choosing should go here
Ok(Box::new(ProtocolV1::new(peer)))
}

View file

@ -37,3 +37,6 @@ mod handshake;
mod protocol;
mod server;
mod peer;
pub use server::Server;
pub use server::DEFAULT_LISTEN_ADDR;

View file

@ -209,7 +209,9 @@ impl Readable<PeerError> 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).
/// 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).
pub struct SockAddr(pub SocketAddr);
impl Writeable for SockAddr {
@ -239,11 +241,25 @@ impl Readable<SockAddr> for SockAddr {
if v4_or_v6 == 0 {
let ip = try!(reader.read_fixed_bytes(4));
let port = try!(reader.read_u16());
Ok(SockAddr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]), port))))
Ok(SockAddr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(ip[0],
ip[1],
ip[2],
ip[3]),
port))))
} else {
let ip = try_oap_vec!([0..8], |_| reader.read_u16());
let port = try!(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))))
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))))
}
}
}

View file

@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::SocketAddr;
use std::fmt;
use std::io::{self, Read, Write};
use std::net::SocketAddr;
use mioco::tcp::{TcpStream, Shutdown};
@ -30,6 +31,13 @@ pub struct PeerConn {
pub user_agent: String,
}
impl fmt::Display for PeerConn {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.peer_addr(), self.user_agent)
}
}
/// Make the Peer a Reader for convenient access to the underlying connection.
/// Allows the peer to track how much is received.
impl Read for PeerConn {

View file

@ -34,7 +34,7 @@ impl<'a> Protocol for ProtocolV1<'a> {
impl<'a> ProtocolV1<'a> {
pub fn new(p: &mut PeerConn) -> ProtocolV1 {
ProtocolV1{peer: p}
ProtocolV1 { peer: p }
}
fn close(&mut self, err_code: u32, explanation: &'static str) {

View file

@ -21,13 +21,14 @@ use std::str::FromStr;
use std::sync::Arc;
use mioco;
use mioco::tcp::TcpListener;
use mioco::tcp::{TcpListener, TcpStream};
use core::ser::Error;
use handshake::Handshake;
use peer::PeerConn;
use types::*;
const DEFAULT_LISTEN_ADDR: &'static str = "127.0.0.1:555";
pub const DEFAULT_LISTEN_ADDR: &'static str = "127.0.0.1:3414";
// replace with some config lookup or something
fn listen_addr() -> SocketAddr {
@ -43,12 +44,12 @@ pub struct Server {
impl Server {
/// Creates a new p2p server. Opens a TCP port to allow incoming
/// connections and starts the bootstrapping process to find peers.
pub fn new() -> Server {
mioco::start(|| -> io::Result<()> {
// TODO SSL
let addr = "127.0.0.1:3414".parse().unwrap();
pub fn start() -> Result<Server, Error> {
// TODO TLS
mioco::spawn(move || -> io::Result<()> {
let addr = DEFAULT_LISTEN_ADDR.parse().unwrap();
let listener = try!(TcpListener::bind(&addr));
info!("P2P server started on {}", addr);
warn!("P2P server started on {}", addr);
let hs = Arc::new(Handshake::new());
@ -64,9 +65,20 @@ impl Server {
Ok(())
});
}
})
.unwrap()
.unwrap();
Server{}
Ok(())
});
Ok(Server {})
}
/// Simulates an unrelated client connecting to our server. Mostly used for
/// tests.
pub fn connect_as_client(addr: SocketAddr) -> Option<Error> {
let tcp_client = TcpStream::connect(&addr).unwrap();
let mut peer = PeerConn::new(tcp_client);
let hs = Handshake::new();
if let Err(e) = hs.connect(&mut peer) {
return Some(e);
}
None
}
}