Skip to content

Commit 5ee673b

Browse files
committed
plaintext/2.0.0
1 parent e686c2f commit 5ee673b

File tree

5 files changed

+534
-17
lines changed

5 files changed

+534
-17
lines changed

protocols/plaintext/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ keywords = ["peer-to-peer", "libp2p", "networking"]
1010
categories = ["network-programming", "asynchronous"]
1111

1212
[dependencies]
13+
bytes = "0.4"
1314
futures = "0.1"
1415
libp2p-core = { version = "0.12.0", path = "../../core" }
16+
log = "0.4.6"
1517
void = "1"
16-
18+
tokio-io = "0.1.12"
19+
protobuf = "2.3"
20+
rw-stream-sink = { version = "0.1.1", path = "../../misc/rw-stream-sink" }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
3+
# This script regenerates the `src/structs_proto.rs` file from `structs.proto`.
4+
5+
sudo docker run --rm -v `pwd`:/usr/code:z -w /usr/code rust /bin/bash -c " \
6+
apt-get update; \
7+
apt-get install -y protobuf-compiler; \
8+
cargo install --version 2.3.0 protobuf-codegen; \
9+
protoc --rust_out . structs.proto"
10+
11+
#sudo chown $USER:$USER *.rs
12+
13+
mv -f structs.rs ./src/structs_proto.rs

protocols/plaintext/src/lib.rs

Lines changed: 289 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,313 @@
1818
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1919
// DEALINGS IN THE SOFTWARE.
2020

21+
use futures::{Future, StartSend, Poll};
22+
use futures::sink::Sink;
23+
use futures::stream::MapErr as StreamMapErr;
24+
use futures::stream::Stream;
2125
use futures::future::{self, FutureResult};
22-
use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, upgrade::Negotiated};
26+
use log::{debug, trace};
27+
use libp2p_core::{identity, InboundUpgrade, OutboundUpgrade, UpgradeInfo, upgrade::Negotiated, PeerId, PublicKey};
28+
use rw_stream_sink::RwStreamSink;
2329
use std::iter;
30+
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
31+
use std::error;
32+
use std::fmt;
2433
use void::Void;
34+
use bytes::BytesMut;
35+
use tokio_io::{AsyncRead, AsyncWrite};
36+
use tokio_io::codec::length_delimited;
37+
use tokio_io::codec::length_delimited::Framed;
38+
use crate::structs_proto::Propose;
39+
use protobuf::Message;
40+
use protobuf::error::ProtobufError;
2541

26-
#[derive(Debug, Copy, Clone)]
27-
pub struct PlainTextConfig;
42+
mod structs_proto;
43+
44+
#[derive(Clone)]
45+
pub struct PlainTextConfig {
46+
// peerId: PeerId,
47+
pub key: identity::Keypair,
48+
}
2849

2950
impl UpgradeInfo for PlainTextConfig {
3051
type Info = &'static [u8];
3152
type InfoIter = iter::Once<Self::Info>;
3253

3354
fn protocol_info(&self) -> Self::InfoIter {
34-
iter::once(b"/plaintext/1.0.0")
55+
iter::once(b"/plaintext/2.0.0")
56+
}
57+
}
58+
59+
impl<C> InboundUpgrade<C> for PlainTextConfig
60+
where
61+
C: AsyncRead + AsyncWrite + Send + 'static
62+
{
63+
type Output = PlainTextOutput<Negotiated<C>>;
64+
type Error = PlainTextError;
65+
type Future = Box<dyn Future<Item = Self::Output, Error = Self::Error> + Send>;
66+
67+
fn upgrade_inbound(self, socket: Negotiated<C>, _: Self::Info) -> Self::Future {
68+
Box::new(self.handshake(socket))
69+
// future::ok(i)
70+
71+
}
72+
}
73+
74+
impl<C> OutboundUpgrade<C> for PlainTextConfig
75+
where
76+
C: AsyncRead + AsyncWrite + Send + 'static
77+
{
78+
type Output = PlainTextOutput<Negotiated<C>>;
79+
type Error = PlainTextError;
80+
type Future = Box<dyn Future<Item = Self::Output, Error = Self::Error> + Send>;
81+
82+
fn upgrade_outbound(self, socket: Negotiated<C>, _: Self::Info) -> Self::Future {
83+
Box::new(self.handshake(socket))
84+
// future::ok(i)
85+
}
86+
}
87+
88+
impl PlainTextConfig {
89+
fn handshake<T>(self, socket: T) -> impl Future<Item = PlainTextOutput<T>, Error = PlainTextError>
90+
where
91+
T: AsyncRead + AsyncWrite + Send + 'static
92+
{
93+
debug!("Starting plaintext upgrade");
94+
PlainTextMiddleware::handshake(socket, self)
95+
.map(|(stream_sink, public_key)| {
96+
let mapped = stream_sink.map_err(map_err as fn(_) -> _);
97+
PlainTextOutput {
98+
stream: RwStreamSink::new(mapped),
99+
remote_key: public_key,
100+
}
101+
})
102+
}
103+
}
104+
105+
#[derive(Debug)]
106+
pub enum PlainTextError {
107+
/// I/O error.
108+
IoError(IoError),
109+
110+
/// Protocol buffer error.
111+
ProtobufError(ProtobufError),
112+
113+
/// Failed to parse one of the handshake protobuf messages.
114+
HandshakeParsingFailure,
115+
}
116+
117+
impl error::Error for PlainTextError {
118+
fn cause(&self) -> Option<&dyn error::Error> {
119+
match *self {
120+
PlainTextError::IoError(ref err) => Some(err),
121+
PlainTextError::ProtobufError(ref err) => Some(err),
122+
_ => None,
123+
}
124+
}
125+
}
126+
127+
impl fmt::Display for PlainTextError {
128+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
129+
match self {
130+
PlainTextError::IoError(e) => write!(f, "I/O error: {}", e),
131+
PlainTextError::ProtobufError(e) => write!(f, "Protobuf error: {}", e),
132+
PlainTextError::HandshakeParsingFailure => f.write_str("Failed to parse one of the handshake protobuf messages"),
133+
}
134+
}
135+
}
136+
137+
impl From<IoError> for PlainTextError {
138+
#[inline]
139+
fn from(err: IoError) -> PlainTextError {
140+
PlainTextError::IoError(err)
141+
}
142+
}
143+
144+
impl From<ProtobufError> for PlainTextError {
145+
#[inline]
146+
fn from(err: ProtobufError) -> PlainTextError {
147+
PlainTextError::ProtobufError(err)
148+
}
149+
}
150+
151+
struct HandShakeContext<T> {
152+
config: PlainTextConfig,
153+
state: T
154+
}
155+
156+
// HandshakeContext<()> --with_local-> HandshakeContext<Local>
157+
struct Local {
158+
// Our encoded local public key
159+
public_key_encoded: Vec<u8>,
160+
// Our local proposition's raw bytes:
161+
proposition_bytes: Vec<u8>,
162+
}
163+
164+
struct Remote {
165+
local: Local,
166+
// The remote's proposition's raw bytes:
167+
proposition_bytes: BytesMut,
168+
// The remote's public key:
169+
public_key: PublicKey,
170+
}
171+
172+
impl HandShakeContext<()> {
173+
fn new(config: PlainTextConfig) -> Self {
174+
Self {
175+
config,
176+
state: (),
177+
}
178+
}
179+
180+
fn with_local(self) -> Result<HandShakeContext<Local>, PlainTextError> {
181+
let public_key_encoded = self.config.key.public().into_protobuf_encoding();
182+
let mut proposition = Propose::new();
183+
proposition.set_pubkey(public_key_encoded.clone());
184+
185+
let proposition_bytes = proposition.write_to_bytes()?;
186+
187+
Ok(HandShakeContext {
188+
config: self.config,
189+
state: Local {
190+
public_key_encoded,
191+
proposition_bytes,
192+
}
193+
})
35194
}
36195
}
37196

38-
impl<C> InboundUpgrade<C> for PlainTextConfig {
39-
type Output = Negotiated<C>;
40-
type Error = Void;
41-
type Future = FutureResult<Negotiated<C>, Self::Error>;
197+
impl HandShakeContext<Local> {
198+
fn with_remote(self, proposition_bytes: BytesMut) -> Result<HandShakeContext<Remote>, PlainTextError> {
199+
let mut prop = match protobuf::parse_from_bytes::<Propose>(&proposition_bytes) {
200+
Ok(prop) => prop,
201+
Err(_) => {
202+
debug!("failed to parse remote's proposition protobuf message");
203+
return Err(PlainTextError::HandshakeParsingFailure);
204+
},
205+
};
42206

43-
fn upgrade_inbound(self, i: Negotiated<C>, _: Self::Info) -> Self::Future {
44-
future::ok(i)
207+
let public_key_encoded = prop.take_pubkey();
208+
let public_key = match PublicKey::from_protobuf_encoding(&public_key_encoded) {
209+
Ok(p) => p,
210+
Err(_) => {
211+
debug!("failed to parse remote's proposition's pubkey protobuf");
212+
return Err(PlainTextError::HandshakeParsingFailure);
213+
},
214+
};
215+
216+
Ok(HandShakeContext {
217+
config: self.config,
218+
state: Remote {
219+
local: self.state,
220+
proposition_bytes,
221+
public_key,
222+
}
223+
})
45224
}
46225
}
47226

48-
impl<C> OutboundUpgrade<C> for PlainTextConfig {
49-
type Output = Negotiated<C>;
50-
type Error = Void;
51-
type Future = FutureResult<Negotiated<C>, Self::Error>;
227+
#[inline]
228+
fn map_err(err: IoError) -> IoError {
229+
debug!("error during plaintext handshake {:?}", err);
230+
IoError::new(IoErrorKind::InvalidData, err)
231+
}
232+
233+
pub struct PlainTextMiddleware<S> {
234+
pub inner: Framed<S, BytesMut>,
235+
}
52236

53-
fn upgrade_outbound(self, i: Negotiated<C>, _: Self::Info) -> Self::Future {
54-
future::ok(i)
237+
impl<T> PlainTextMiddleware<T>
238+
where
239+
T: AsyncRead + AsyncWrite + Send,
240+
{
241+
fn handshake(socket: T, config: PlainTextConfig) -> impl Future<Item = (PlainTextMiddleware<T>, PublicKey), Error = PlainTextError>
242+
where
243+
T: AsyncRead + AsyncWrite + Send + 'static
244+
{
245+
let socket = length_delimited::Builder::new()
246+
.big_endian()
247+
.length_field_length(4)
248+
.new_framed(socket);
249+
250+
future::ok::<_, PlainTextError>(HandShakeContext::new(config))
251+
.and_then(|context| {
252+
trace!("starting handshake");
253+
Ok(context.with_local()?)
254+
})
255+
.and_then(|context| {
256+
trace!("sending proposition to remote");
257+
socket.send(BytesMut::from(context.state.proposition_bytes.clone()))
258+
.from_err()
259+
.map(|s| (s, context))
260+
})
261+
.and_then(move |(socket, context)| {
262+
trace!("receiving the remote's proposition");
263+
socket.into_future()
264+
.map_err(|(e, _)| e.into())
265+
.and_then(move |(prop_raw, socket)| {
266+
let context = match prop_raw {
267+
Some(p) => context.with_remote(p)?,
268+
None => {
269+
debug!("unexpected eof while waiting for remote's proposition");
270+
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
271+
return Err(err.into());
272+
}
273+
};
274+
275+
trace!("received proposition from remote; pubkey = {:?}", context.state.public_key);
276+
Ok((socket, context))
277+
})
278+
})
279+
.and_then(|(socket, context)| {
280+
let middleware = PlainTextMiddleware { inner: socket };
281+
Ok((middleware, context.state.public_key))
282+
})
55283
}
56284
}
57285

286+
impl<S> Sink for PlainTextMiddleware<S>
287+
where
288+
S: AsyncRead + AsyncWrite,
289+
{
290+
type SinkItem = BytesMut;
291+
type SinkError = IoError;
292+
293+
#[inline]
294+
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
295+
self.inner.start_send(item)
296+
}
297+
298+
#[inline]
299+
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
300+
self.inner.poll_complete()
301+
}
302+
303+
#[inline]
304+
fn close(&mut self) -> Poll<(), Self::SinkError> {
305+
self.inner.close()
306+
}
307+
}
308+
309+
impl<S> Stream for PlainTextMiddleware<S>
310+
where
311+
S: AsyncRead + AsyncWrite,
312+
{
313+
type Item = BytesMut;
314+
type Error = IoError;
315+
316+
#[inline]
317+
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
318+
self.inner.poll()
319+
}
320+
}
321+
322+
/// Output of the plaintext protocol.
323+
pub struct PlainTextOutput<S>
324+
where
325+
S: AsyncRead + AsyncWrite,
326+
{
327+
pub stream: RwStreamSink<StreamMapErr<PlainTextMiddleware<S>, fn(IoError) -> IoError>>,
328+
/// The public key of the remote.
329+
pub remote_key: PublicKey,
330+
}

0 commit comments

Comments
 (0)