multiaddr: add support for onion protocol. (#542)

* multiaddr: add support for onion protocol.

* Comment `read_onion`.

* Split onion into address and port parts.
This commit is contained in:
Toralf Wittner
2018-10-08 11:13:00 +02:00
committed by Pierre Krieger
parent d81f4e264f
commit e05422c05f
4 changed files with 66 additions and 8 deletions

View File

@ -9,8 +9,10 @@ readme = "README.md"
version = "0.3.0"
[dependencies]
arrayref = "0.3"
bs58 = "0.2.0"
byteorder = "0.4"
data-encoding = "2.1"
multihash = { path = "../multihash" }
serde = "1.0.70"
unsigned-varint = "0.1"

View File

@ -3,8 +3,11 @@
///! Implementation of [multiaddr](https://github.com/jbenet/multiaddr)
///! in Rust.
#[macro_use]
extern crate arrayref;
extern crate bs58;
extern crate byteorder;
extern crate data_encoding;
extern crate serde;
extern crate unsigned_varint;
pub extern crate multihash;

View File

@ -1,5 +1,6 @@
use bs58;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
use data_encoding::BASE32;
use multihash::Multihash;
use std::{
borrow::Cow,
@ -50,7 +51,7 @@ pub enum Protocol<'a> {
P2pWebRtcStar,
P2pWebSocketStar,
Memory,
Onion(Cow<'a, [u8]>),
Onion(Cow<'a, [u8; 10]>, u16),
P2p(Multihash),
P2pCircuit,
Quic,
@ -121,7 +122,11 @@ impl<'a> Protocol<'a> {
}
"http" => Ok(Protocol::Http),
"https" => Ok(Protocol::Https),
"onion" => unimplemented!(), // FIXME
"onion" =>
iter.next()
.ok_or(Error::InvalidProtocolString)
.and_then(|s| read_onion(&s.to_uppercase()))
.map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)),
"quic" => Ok(Protocol::Quic),
"ws" => Ok(Protocol::Ws),
"wss" => Ok(Protocol::Wss),
@ -191,7 +196,11 @@ impl<'a> Protocol<'a> {
P2P_WEBRTC_STAR => Ok((Protocol::P2pWebRtcStar, input)),
P2P_WEBSOCKET_STAR => Ok((Protocol::P2pWebSocketStar, input)),
MEMORY => Ok((Protocol::Memory, input)),
ONION => unimplemented!(), // FIXME
ONION => {
let (data, rest) = split_at(12, input)?;
let port = BigEndian::read_u16(&data[10 ..]);
Ok((Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port), rest))
}
P2P => {
let (n, input) = decode::usize(input)?;
let (data, rest) = split_at(n, input)?;
@ -285,7 +294,11 @@ impl<'a> Protocol<'a> {
w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
w.write_all(&bytes)?
}
Protocol::Onion(_) => unimplemented!(), // FIXME
Protocol::Onion(addr, port) => {
w.write_all(encode::u32(ONION, &mut buf))?;
w.write_all(addr.as_ref())?;
w.write_u16::<BigEndian>(*port)?
}
Protocol::Quic => w.write_all(encode::u32(QUIC, &mut buf))?,
Protocol::Utp => w.write_all(encode::u32(UTP, &mut buf))?,
Protocol::Udt => w.write_all(encode::u32(UDT, &mut buf))?,
@ -317,7 +330,7 @@ impl<'a> Protocol<'a> {
P2pWebRtcStar => P2pWebRtcStar,
P2pWebSocketStar => P2pWebSocketStar,
Memory => Memory,
Onion(cow) => Onion(Cow::Owned(cow.into_owned())),
Onion(addr, port) => Onion(Cow::Owned(addr.into_owned()), port),
P2p(a) => P2p(a),
P2pCircuit => P2pCircuit,
Quic => Quic,
@ -348,7 +361,10 @@ impl<'a> fmt::Display for Protocol<'a> {
P2pWebRtcStar => f.write_str("/p2p-webrtc-star"),
P2pWebSocketStar => f.write_str("/p2p-websocket-star"),
Memory => f.write_str("/memory"),
Onion(_) => unimplemented!(), // FIXME!
Onion(addr, port) => {
let s = BASE32.encode(addr.as_ref());
write!(f, "/onion/{}:{}", s.to_lowercase(), port)
}
P2p(c) => write!(f, "/p2p/{}", bs58::encode(c.as_bytes()).into_string()),
P2pCircuit => f.write_str("/p2p-circuit"),
Quic => f.write_str("/quic"),
@ -378,3 +394,35 @@ impl<'a> From<Ipv6Addr> for Protocol<'a> {
}
}
// Parse a version 2 onion address and return its binary representation.
//
// Format: <base-32 address> ":" <port number>
fn read_onion(s: &str) -> Result<([u8; 10], u16)> {
let mut parts = s.split(':');
// address part (without ".onion")
let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?;
if b32.len() != 16 {
return Err(Error::InvalidMultiaddr)
}
// port number
let port = parts.next()
.ok_or(Error::InvalidMultiaddr)
.and_then(|p| str::parse(p).map_err(From::from))?;
// nothing else expected
if parts.next().is_some() {
return Err(Error::InvalidMultiaddr)
}
if 10 != BASE32.decode_len(b32.len()).map_err(|_| Error::InvalidMultiaddr)? {
return Err(Error::InvalidMultiaddr)
}
let mut buf = [0u8; 10];
BASE32.decode_mut(b32.as_bytes(), &mut buf).map_err(|_| Error::InvalidMultiaddr)?;
Ok((buf, port))
}

View File

@ -59,7 +59,7 @@ struct Proto(Protocol<'static>);
impl Arbitrary for Proto {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
use Protocol::*;
match g.gen_range(0, 22) { // TODO: Add Protocol::Quic
match g.gen_range(0, 23) { // TODO: Add Protocol::Quic
0 => Proto(Dccp(g.gen())),
1 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
2 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
@ -83,6 +83,11 @@ impl Arbitrary for Proto {
19 => Proto(Utp),
20 => Proto(Ws),
21 => Proto(Wss),
22 => {
let mut a = [0; 10];
g.fill(&mut a);
Proto(Onion(Cow::Owned(a), g.gen()))
}
_ => panic!("outside range")
}
}