Skip to content

Commit

Permalink
add x-webrtc protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
melekes committed Mar 28, 2022
1 parent 031f645 commit c88f5f4
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 126 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ serde = "1.0.70"
static_assertions = "1.1"
unsigned-varint = "0.7"
url = { version = "2.1.0", optional = true, default-features = false }
hex = "0.4"

[dev-dependencies]
bincode = "1"
Expand Down
137 changes: 89 additions & 48 deletions src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use crate::onion_addr::Onion3Addr;
use crate::{Error, Result};
use arrayref::array_ref;
use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
use crate::{Result, Error};
use data_encoding::BASE32;
use hex;
use multihash::Multihash;
use std::{
borrow::Cow,
convert::From,
fmt,
io::{Cursor, Write},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
str::{self, FromStr}
str::{self, FromStr},
};
use unsigned_varint::{encode, decode};
use crate::onion_addr::Onion3Addr;
use unsigned_varint::{decode, encode};

// All the values are obtained by converting hexadecimal protocol codes to u32.
// Protocols as well as their corresponding codes are defined in
Expand Down Expand Up @@ -43,9 +44,10 @@ const UDT: u32 = 301;
const UNIX: u32 = 400;
const UTP: u32 = 302;
const WS: u32 = 477;
const WS_WITH_PATH: u32 = 4770; // Note: not standard
const WS_WITH_PATH: u32 = 4770; // Note: not standard
const WSS: u32 = 478;
const WSS_WITH_PATH: u32 = 4780; // Note: not standard
const WSS_WITH_PATH: u32 = 4780; // Note: not standard
const XWEBRTC: u32 = 277;

const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
.add(b'%')
Expand Down Expand Up @@ -79,6 +81,7 @@ pub enum Protocol<'a> {
Ip6(Ipv6Addr),
P2pWebRtcDirect,
P2pWebRtcStar,
XWebRTC(Cow<'a, [u8; 32]>),
P2pWebSocketStar,
/// Contains the "port" to contact. Similar to TCP or UDP, 0 means "assign me a port".
Memory(u64),
Expand Down Expand Up @@ -107,7 +110,7 @@ impl<'a> Protocol<'a> {
/// that iteration has finished whenever appropriate.
pub fn from_str_parts<I>(mut iter: I) -> Result<Self>
where
I: Iterator<Item=&'a str>
I: Iterator<Item = &'a str>,
{
match iter.next().ok_or(Error::InvalidProtocolString)? {
"ip4" => {
Expand Down Expand Up @@ -164,16 +167,16 @@ impl<'a> Protocol<'a> {
}
"http" => Ok(Protocol::Http),
"https" => Ok(Protocol::Https),
"onion" =>
iter.next()
.ok_or(Error::InvalidProtocolString)
.and_then(|s| read_onion(&s.to_uppercase()))
.map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)),
"onion3" =>
iter.next()
.ok_or(Error::InvalidProtocolString)
.and_then(|s| read_onion3(&s.to_uppercase()))
.map(|(a, p)| Protocol::Onion3((a, p).into())),
"onion" => iter
.next()
.ok_or(Error::InvalidProtocolString)
.and_then(|s| read_onion(&s.to_uppercase()))
.map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)),
"onion3" => iter
.next()
.ok_or(Error::InvalidProtocolString)
.and_then(|s| read_onion3(&s.to_uppercase()))
.map(|(a, p)| Protocol::Onion3((a, p).into())),
"quic" => Ok(Protocol::Quic),
"ws" => Ok(Protocol::Ws(Cow::Borrowed("/"))),
"wss" => Ok(Protocol::Wss(Cow::Borrowed("/"))),
Expand All @@ -195,7 +198,13 @@ impl<'a> Protocol<'a> {
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
Ok(Protocol::Memory(s.parse()?))
}
unknown => Err(Error::UnknownProtocolString(unknown.to_string()))
"x-webrtc" => {
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
let mut buf = [0; 32];
hex::decode_to_slice(s, &mut buf).map_err(|_| Error::InvalidProtocolString)?;
Ok(Protocol::XWebRTC(Cow::Owned(buf)))
}
unknown => Err(Error::UnknownProtocolString(unknown.to_string())),
}
}

Expand All @@ -204,7 +213,7 @@ impl<'a> Protocol<'a> {
pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8])> {
fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8])> {
if input.len() < n {
return Err(Error::DataLessThanLen)
return Err(Error::DataLessThanLen);
}
Ok(input.split_at(n))
}
Expand Down Expand Up @@ -234,13 +243,19 @@ impl<'a> Protocol<'a> {
DNSADDR => {
let (n, input) = decode::usize(input)?;
let (data, rest) = split_at(n, input)?;
Ok((Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)), rest))
Ok((
Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)),
rest,
))
}
HTTP => Ok((Protocol::Http, input)),
HTTPS => Ok((Protocol::Https, input)),
IP4 => {
let (data, rest) = split_at(4, input)?;
Ok((Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])), rest))
Ok((
Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])),
rest,
))
}
IP6 => {
let (data, rest) = split_at(16, input)?;
Expand All @@ -251,14 +266,9 @@ impl<'a> Protocol<'a> {
*x = rdr.read_u16::<BigEndian>()?;
}

let addr = Ipv6Addr::new(seg[0],
seg[1],
seg[2],
seg[3],
seg[4],
seg[5],
seg[6],
seg[7]);
let addr = Ipv6Addr::new(
seg[0], seg[1], seg[2], seg[3], seg[4], seg[5], seg[6], seg[7],
);

Ok((Protocol::Ip6(addr), rest))
}
Expand All @@ -273,13 +283,19 @@ impl<'a> Protocol<'a> {
}
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))
let port = BigEndian::read_u16(&data[10..]);
Ok((
Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port),
rest,
))
}
ONION3 => {
let (data, rest) = split_at(37, input)?;
let port = BigEndian::read_u16(&data[35 ..]);
Ok((Protocol::Onion3((array_ref!(data, 0, 35), port).into()), rest))
let port = BigEndian::read_u16(&data[35..]);
Ok((
Protocol::Onion3((array_ref!(data, 0, 35), port).into()),
rest,
))
}
P2P => {
let (n, input) = decode::usize(input)?;
Expand Down Expand Up @@ -326,7 +342,14 @@ impl<'a> Protocol<'a> {
let (data, rest) = split_at(n, input)?;
Ok((Protocol::Wss(Cow::Borrowed(str::from_utf8(data)?)), rest))
}
_ => Err(Error::UnknownProtocolId(id))
XWEBRTC => {
let (data, rest) = split_at(32, input)?;
Ok((
Protocol::XWebRTC(Cow::Borrowed(array_ref!(data, 0, 32))),
rest,
))
}
_ => Err(Error::UnknownProtocolId(id)),
}
}

Expand Down Expand Up @@ -419,14 +442,14 @@ impl<'a> Protocol<'a> {
let bytes = s.as_bytes();
w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
w.write_all(&bytes)?
},
}
Protocol::Wss(ref s) if s == "/" => w.write_all(encode::u32(WSS, &mut buf))?,
Protocol::Wss(s) => {
w.write_all(encode::u32(WSS_WITH_PATH, &mut buf))?;
let bytes = s.as_bytes();
w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
w.write_all(&bytes)?
},
}
Protocol::P2pWebSocketStar => w.write_all(encode::u32(P2P_WEBSOCKET_STAR, &mut buf))?,
Protocol::P2pWebRtcStar => w.write_all(encode::u32(P2P_WEBRTC_STAR, &mut buf))?,
Protocol::P2pWebRtcDirect => w.write_all(encode::u32(P2P_WEBRTC_DIRECT, &mut buf))?,
Expand All @@ -435,6 +458,10 @@ impl<'a> Protocol<'a> {
w.write_all(encode::u32(MEMORY, &mut buf))?;
w.write_u64::<BigEndian>(*port)?
}
Protocol::XWebRTC(fingerprint) => {
w.write_all(encode::u32(XWEBRTC, &mut buf))?;
w.write_all(fingerprint.as_ref())?
}
}
Ok(())
}
Expand Down Expand Up @@ -470,6 +497,7 @@ impl<'a> Protocol<'a> {
Utp => Utp,
Ws(cow) => Ws(Cow::Owned(cow.into_owned())),
Wss(cow) => Wss(Cow::Owned(cow.into_owned())),
XWebRTC(fingerprint) => XWebRTC(Cow::Owned(fingerprint.into_owned())),
}
}
}
Expand All @@ -495,7 +523,7 @@ impl<'a> fmt::Display for Protocol<'a> {
let s = BASE32.encode(addr.as_ref());
write!(f, "/onion/{}:{}", s.to_lowercase(), port)
}
Onion3(addr ) => {
Onion3(addr) => {
let s = BASE32.encode(addr.hash());
write!(f, "/onion3/{}:{}", s.to_lowercase(), addr.port())
}
Expand All @@ -511,14 +539,20 @@ impl<'a> fmt::Display for Protocol<'a> {
Utp => f.write_str("/utp"),
Ws(ref s) if s == "/" => f.write_str("/ws"),
Ws(s) => {
let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
let encoded =
percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
write!(f, "/x-parity-ws/{}", encoded)
},
}
Wss(ref s) if s == "/" => f.write_str("/wss"),
Wss(s) => {
let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
let encoded =
percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
write!(f, "/x-parity-wss/{}", encoded)
},
}
XWebRTC(fingerprint) => {
let s = hex::encode(fingerprint.as_ref());
write!(f, "/x-webrtc/{}", s.to_uppercase())
}
}
}
}
Expand Down Expand Up @@ -555,11 +589,12 @@ macro_rules! read_onion_impl {
// address part (without ".onion")
let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?;
if b32.len() != $encoded_len {
return Err(Error::InvalidMultiaddr)
return Err(Error::InvalidMultiaddr);
}

// port number
let port = parts.next()
let port = parts
.next()
.ok_or(Error::InvalidMultiaddr)
.and_then(|p| str::parse(p).map_err(From::from))?;

Expand All @@ -570,19 +605,25 @@ macro_rules! read_onion_impl {

// nothing else expected
if parts.next().is_some() {
return Err(Error::InvalidMultiaddr)
return Err(Error::InvalidMultiaddr);
}

if $len != BASE32.decode_len(b32.len()).map_err(|_| Error::InvalidMultiaddr)? {
return Err(Error::InvalidMultiaddr)
if $len
!= BASE32
.decode_len(b32.len())
.map_err(|_| Error::InvalidMultiaddr)?
{
return Err(Error::InvalidMultiaddr);
}

let mut buf = [0u8; $len];
BASE32.decode_mut(b32.as_bytes(), &mut buf).map_err(|_| Error::InvalidMultiaddr)?;
BASE32
.decode_mut(b32.as_bytes(), &mut buf)
.map_err(|_| Error::InvalidMultiaddr)?;

Ok((buf, port))
}
}
};
}

// Parse a version 2 onion address and return its binary representation.
Expand Down
Loading

0 comments on commit c88f5f4

Please sign in to comment.