proto: major refactor to channel cells
The protocol is now based around a message-oriented scheme intended to use futures_codec, which in turn uses BytesMut. The transition to BytesMut is incomplete, and we're not nearly so zero-copy as intended. We should think about that.
This commit is contained in:
parent
8db525dab4
commit
e749e2bc27
|
@ -25,6 +25,8 @@ zeroize = "1.1.0"
|
|||
subtle = "2.2.3"
|
||||
stream-cipher = "0.7.1"
|
||||
sha2 = "0.9.1"
|
||||
futures_codec = "*"
|
||||
bytes = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3.1"
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
//! Messages sent over Tor channels
|
||||
//!
|
||||
//! A 'channel' is a direct connection between a tor client and a
|
||||
//! relay, or between two relays. Current channels all use TLS.
|
||||
//!
|
||||
//! This module implements the "cell" type, which is the encoding for
|
||||
//! data sent over a channel. It also encodes and decodes various
|
||||
//! channel messages, which are the types of data conveyed over a
|
||||
//! channel.
|
||||
#![allow(missing_docs)]
|
||||
|
||||
pub mod codec;
|
||||
pub mod msg;
|
||||
use bytes;
|
||||
use caret::caret_int;
|
||||
|
||||
/// The amount of data sent in a fixed-length cell.
|
||||
///
|
||||
/// Historically, this was set at 509 bytes so that cells would be
|
||||
/// 512 bytes long once commands and circuit IDs were added. But now
|
||||
/// circuit IDs are longer, so cells are 514 bytes.
|
||||
pub const CELL_DATA_LEN: usize = 509;
|
||||
|
||||
/// Channel-local identifier for a circuit.
|
||||
///
|
||||
/// A circuit ID can be 2 or 4 bytes long; on modern versions of the Tor
|
||||
/// protocol, it's 4 bytes long.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct CircID(u32);
|
||||
|
||||
impl From<u32> for CircID {
|
||||
fn from(item: u32) -> Self {
|
||||
Self(item)
|
||||
}
|
||||
}
|
||||
impl Into<u32> for CircID {
|
||||
fn into(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
caret_int! {
|
||||
/// A ChanCmd is the type of a channel cell. The value of the ChanCmd
|
||||
/// indicates the meaning of the cell, and (possibly) its length.
|
||||
pub struct ChanCmd(u8) {
|
||||
/// A fixed-length cell that will be dropped.
|
||||
PADDING = 0,
|
||||
/// Create a new circuit (obsolete format)
|
||||
CREATE = 1,
|
||||
/// Finish circuit-creation handshake (obsolete format)
|
||||
CREATED = 2,
|
||||
/// Relay cell, transmitted over a circuit.
|
||||
RELAY = 3,
|
||||
/// Destroy a circuit
|
||||
DESTROY = 4,
|
||||
/// Create a new circuit (no public-key)
|
||||
CREATE_FAST = 5,
|
||||
/// Finish a circuit-creation handshake (no public-key)
|
||||
CREATED_FAST = 6,
|
||||
// note gap in numbering: 7 is grouped with the variable-length cells
|
||||
/// Finish a channel handshake with time and address information
|
||||
NETINFO = 8,
|
||||
/// Relay cellm transmitted over a circuit. Limited.
|
||||
RELAY_EARLY = 9,
|
||||
/// Create a new circuit (current format)
|
||||
CREATE2 = 10,
|
||||
/// Finish a circuit-creation handshake (current format)
|
||||
CREATED2 = 11,
|
||||
/// Adjust channel-padding settings
|
||||
PADDING_NEGOTIATE = 12,
|
||||
|
||||
/// Variable-length cell, despite its number: negotiate versions
|
||||
VERSIONS = 7,
|
||||
/// Variable-length channel-padding cell
|
||||
VPADDING = 128,
|
||||
/// Provide additional certificates beyond those given in the TLS
|
||||
/// handshake
|
||||
CERTS = 129,
|
||||
/// Challenge material used in relay-to-relay handshake.
|
||||
AUTH_CHALLENGE = 130,
|
||||
/// Response material used in relay-to-relay handshake.
|
||||
AUTHENTICATE = 131,
|
||||
/// Indicates client permission to use relay. Not currently used.
|
||||
AUTHORIZE = 132,
|
||||
}
|
||||
}
|
||||
|
||||
impl ChanCmd {
|
||||
/// Return true if this cell uses the variable-length format.
|
||||
pub fn is_var_cell(self) -> bool {
|
||||
// Version 1 of the channel protocol had no variable-length
|
||||
// cells, but that's obsolete. In version 2, only the VERSIONS
|
||||
// cell was variable-length.
|
||||
self == ChanCmd::VERSIONS || self.0 >= 128u8
|
||||
}
|
||||
pub fn is_recognized(self) -> bool {
|
||||
match self {
|
||||
ChanCmd::PADDING
|
||||
| ChanCmd::NETINFO
|
||||
| ChanCmd::PADDING_NEGOTIATE
|
||||
| ChanCmd::VERSIONS
|
||||
| ChanCmd::VPADDING
|
||||
| ChanCmd::CERTS
|
||||
| ChanCmd::AUTH_CHALLENGE
|
||||
| ChanCmd::AUTHENTICATE
|
||||
| ChanCmd::CREATE
|
||||
| ChanCmd::CREATED
|
||||
| ChanCmd::RELAY
|
||||
| ChanCmd::DESTROY
|
||||
| ChanCmd::CREATE_FAST
|
||||
| ChanCmd::CREATED_FAST
|
||||
| ChanCmd::RELAY_EARLY
|
||||
| ChanCmd::CREATE2
|
||||
| ChanCmd::CREATED2 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn allows_circid(self) -> bool {
|
||||
match self {
|
||||
ChanCmd::PADDING
|
||||
| ChanCmd::NETINFO
|
||||
| ChanCmd::PADDING_NEGOTIATE
|
||||
| ChanCmd::VERSIONS
|
||||
| ChanCmd::VPADDING
|
||||
| ChanCmd::CERTS
|
||||
| ChanCmd::AUTH_CHALLENGE
|
||||
| ChanCmd::AUTHENTICATE => false,
|
||||
ChanCmd::CREATE
|
||||
| ChanCmd::CREATED
|
||||
| ChanCmd::RELAY
|
||||
| ChanCmd::DESTROY
|
||||
| ChanCmd::CREATE_FAST
|
||||
| ChanCmd::CREATED_FAST
|
||||
| ChanCmd::RELAY_EARLY
|
||||
| ChanCmd::CREATE2
|
||||
| ChanCmd::CREATED2 => true,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single cell extracted from, or encodeable onto, a channel.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ChanCell {
|
||||
circ: CircID,
|
||||
cmd: ChanCmd,
|
||||
body: bytes::Bytes,
|
||||
}
|
||||
|
||||
impl ChanCell {
|
||||
/// Return the cell's circuit ID.
|
||||
pub fn get_circid(&self) -> CircID {
|
||||
self.circ
|
||||
}
|
||||
/// Return the cell's channel ID
|
||||
pub fn get_cmd(&self) -> ChanCmd {
|
||||
self.cmd
|
||||
}
|
||||
/// Return the body of this cell.
|
||||
pub fn get_body(&self) -> &bytes::Bytes {
|
||||
&self.body
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
use crate::chancell::{
|
||||
msg::{ChanMsg, ChannelMessage},
|
||||
ChanCmd, CircID,
|
||||
};
|
||||
use crate::crypto::cell::CELL_BODY_LEN;
|
||||
use arrayref::{array_mut_ref, array_ref};
|
||||
use bytes;
|
||||
use futures_codec;
|
||||
use tor_bytes::{self, Reader, Writer};
|
||||
|
||||
// XXXX make a crate-level error type
|
||||
pub enum Error {
|
||||
Io(std::io::Error),
|
||||
Bytes(tor_bytes::Error),
|
||||
Misc(),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(e: std::io::Error) -> Error {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
impl From<tor_bytes::Error> for Error {
|
||||
fn from(e: tor_bytes::Error) -> Error {
|
||||
Error::Bytes(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: only link versions 3 and higher are supported. Versions cell
|
||||
// is not supported via coder/decoder ,since it always uses a two-byte
|
||||
// circuit-ID.
|
||||
pub struct ChannelCodec {
|
||||
link_version: u16,
|
||||
}
|
||||
|
||||
impl futures_codec::Encoder for ChannelCodec {
|
||||
type Item = (CircID, ChannelMessage);
|
||||
type Error = Error;
|
||||
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut bytes::BytesMut) -> Result<(), Self::Error> {
|
||||
let (circid, msg) = item;
|
||||
let cmd = msg.get_cmd();
|
||||
dst.write_u32(circid.into());
|
||||
dst.write_u8(cmd.into());
|
||||
|
||||
// now write the cell body and handle the length.
|
||||
if cmd.is_var_cell() {
|
||||
let pos = dst.len(); // always 5?
|
||||
dst.write_u16(0);
|
||||
msg.write_body_onto(dst);
|
||||
let len = dst.len() - pos - 2;
|
||||
if len > std::u16::MAX as usize {
|
||||
return Err(Error::Misc());
|
||||
}
|
||||
// go back and set the length.
|
||||
*(array_mut_ref![&mut dst[pos..pos + 2], 0, 2]) = (len as u16).to_be_bytes();
|
||||
} else {
|
||||
let pos = dst.len(); // Always 5?
|
||||
msg.write_body_onto(dst);
|
||||
let len = dst.len() - pos;
|
||||
if len > CELL_BODY_LEN {
|
||||
return Err(Error::Misc());
|
||||
}
|
||||
// pad to end of fixed-length cell
|
||||
dst.write_zeros(CELL_BODY_LEN - len);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl futures_codec::Decoder for ChannelCodec {
|
||||
type Item = (CircID, ChannelMessage);
|
||||
type Error = Error;
|
||||
|
||||
fn decode(&mut self, src: &mut bytes::BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if src.len() < 7 {
|
||||
// Smallest possible command: varcell with len 0
|
||||
return Ok(None);
|
||||
}
|
||||
let cmd: ChanCmd = src[4].into();
|
||||
let varcell = cmd.is_var_cell();
|
||||
let cell_len: usize = if varcell {
|
||||
let msg_len = u16::from_be_bytes(*array_ref![&src[5..7], 0, 2]);
|
||||
msg_len as usize + 7
|
||||
} else {
|
||||
514
|
||||
};
|
||||
if src.len() < cell_len {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let cell = src.split_to(cell_len).freeze();
|
||||
let mut r = Reader::from_bytes(&cell);
|
||||
let circid = r.take_u32()?.into();
|
||||
r.advance(if varcell { 1 } else { 3 })?;
|
||||
let msg = r.extract()?;
|
||||
Ok(Some((circid, msg)))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,566 @@
|
|||
/// A channel message is a decoded channel cell.
|
||||
use crate::crypto::cell::{RawCellBody, CELL_BODY_LEN};
|
||||
use tor_bytes::{Error, Readable, Reader, Result, Writer};
|
||||
|
||||
use super::ChanCmd;
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
pub trait ChanMsg: Readable {
|
||||
fn as_message(self) -> ChannelMessage;
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W);
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ChannelMessage {
|
||||
Padding(Padding),
|
||||
VPadding(VPadding),
|
||||
Create(Create),
|
||||
CreateFast(CreateFast),
|
||||
Create2(Create2),
|
||||
Created(Created),
|
||||
CreatedFast(CreatedFast),
|
||||
Created2(Created2),
|
||||
Relay(Relay),
|
||||
RelayEarly(Relay),
|
||||
Destroy(Destroy),
|
||||
Netinfo(Netinfo),
|
||||
Versions(Versions),
|
||||
PaddingNegotiate(PaddingNegotiate),
|
||||
Certs(Certs),
|
||||
AuthChallenge(AuthChallenge),
|
||||
Authenticate(Authenticate),
|
||||
Authorize(Authorize),
|
||||
Unrecognized(Unrecognized),
|
||||
}
|
||||
|
||||
impl ChannelMessage {
|
||||
pub fn get_cmd(&self) -> ChanCmd {
|
||||
use ChannelMessage::*;
|
||||
match self {
|
||||
Padding(_) => ChanCmd::PADDING,
|
||||
VPadding(_) => ChanCmd::VPADDING,
|
||||
Create(_) => ChanCmd::CREATE,
|
||||
CreateFast(_) => ChanCmd::CREATE_FAST,
|
||||
Create2(_) => ChanCmd::CREATE2,
|
||||
Created(_) => ChanCmd::CREATED,
|
||||
CreatedFast(_) => ChanCmd::CREATED_FAST,
|
||||
Created2(_) => ChanCmd::CREATED2,
|
||||
Relay(_) => ChanCmd::RELAY,
|
||||
RelayEarly(_) => ChanCmd::RELAY_EARLY,
|
||||
Destroy(_) => ChanCmd::DESTROY,
|
||||
Netinfo(_) => ChanCmd::NETINFO,
|
||||
Versions(_) => ChanCmd::VERSIONS,
|
||||
PaddingNegotiate(_) => ChanCmd::PADDING_NEGOTIATE,
|
||||
Certs(_) => ChanCmd::CERTS,
|
||||
AuthChallenge(_) => ChanCmd::AUTH_CHALLENGE,
|
||||
Authenticate(_) => ChanCmd::AUTHENTICATE,
|
||||
Authorize(_) => ChanCmd::AUTHORIZE,
|
||||
Unrecognized(c) => c.get_cmd(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChanMsg for ChannelMessage {
|
||||
fn as_message(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
use ChannelMessage::*;
|
||||
match self {
|
||||
Padding(b) => b.write_body_onto(w),
|
||||
VPadding(b) => b.write_body_onto(w),
|
||||
Create(b) => b.write_body_onto(w),
|
||||
CreateFast(b) => b.write_body_onto(w),
|
||||
Create2(b) => b.write_body_onto(w),
|
||||
Created(b) => b.write_body_onto(w),
|
||||
CreatedFast(b) => b.write_body_onto(w),
|
||||
Created2(b) => b.write_body_onto(w),
|
||||
Relay(b) => b.write_body_onto(w),
|
||||
RelayEarly(b) => b.write_body_onto(w),
|
||||
Destroy(b) => b.write_body_onto(w),
|
||||
Netinfo(b) => b.write_body_onto(w),
|
||||
Versions(b) => b.write_body_onto(w),
|
||||
PaddingNegotiate(b) => b.write_body_onto(w),
|
||||
Certs(b) => b.write_body_onto(w),
|
||||
AuthChallenge(b) => b.write_body_onto(w),
|
||||
Authenticate(b) => b.write_body_onto(w),
|
||||
Authorize(b) => b.write_body_onto(w),
|
||||
Unrecognized(b) => b.write_body_onto(w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for ChannelMessage {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let cmd = r.take_u8()?.into();
|
||||
use ChannelMessage::*;
|
||||
Ok(match cmd {
|
||||
ChanCmd::PADDING => Padding(r.extract()?),
|
||||
ChanCmd::VPADDING => VPadding(r.extract()?),
|
||||
ChanCmd::CREATE => Create(r.extract()?),
|
||||
ChanCmd::CREATE_FAST => CreateFast(r.extract()?),
|
||||
ChanCmd::CREATE2 => Create2(r.extract()?),
|
||||
ChanCmd::CREATED => Created(r.extract()?),
|
||||
ChanCmd::CREATED_FAST => CreatedFast(r.extract()?),
|
||||
ChanCmd::CREATED2 => Created2(r.extract()?),
|
||||
ChanCmd::RELAY => Relay(r.extract()?),
|
||||
ChanCmd::RELAY_EARLY => RelayEarly(r.extract()?),
|
||||
ChanCmd::DESTROY => Destroy(r.extract()?),
|
||||
ChanCmd::NETINFO => Netinfo(r.extract()?),
|
||||
ChanCmd::VERSIONS => Versions(r.extract()?),
|
||||
ChanCmd::PADDING_NEGOTIATE => PaddingNegotiate(r.extract()?),
|
||||
ChanCmd::CERTS => Certs(r.extract()?),
|
||||
ChanCmd::AUTH_CHALLENGE => AuthChallenge(r.extract()?),
|
||||
ChanCmd::AUTHENTICATE => Authenticate(r.extract()?),
|
||||
ChanCmd::AUTHORIZE => Authorize(r.extract()?),
|
||||
_ => Unrecognized(unrecognized_with_cmd(cmd, r)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Padding {}
|
||||
impl ChanMsg for Padding {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Padding(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, _w: &mut W) {}
|
||||
}
|
||||
impl Readable for Padding {
|
||||
fn take_from(_r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(Padding {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VPadding {
|
||||
len: u16,
|
||||
}
|
||||
impl ChanMsg for VPadding {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::VPadding(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_zeros(self.len as usize);
|
||||
}
|
||||
}
|
||||
impl Readable for VPadding {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
if r.remaining() > std::u16::MAX as usize {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
Ok(VPadding {
|
||||
len: r.remaining() as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fixed_len {
|
||||
{
|
||||
$name:ident , $cmd:ident, $len:ident
|
||||
} => {
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct $name {
|
||||
handshake: Vec<u8>
|
||||
}
|
||||
impl ChanMsg for $name {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::$name(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_all(&self.handshake[..])
|
||||
}
|
||||
}
|
||||
impl Readable for $name {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok($name {
|
||||
handshake: r.take($len)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XXXX MOVE THESE
|
||||
pub const TAP_C_HANDSHAKE_LEN: usize = 128 * 2 + 42;
|
||||
pub const TAP_S_HANDSHAKE_LEN: usize = 128 + 20;
|
||||
|
||||
const FAST_C_HANDSHAKE_LEN: usize = 20;
|
||||
const FAST_S_HANDSHAKE_LEN: usize = 20 * 2;
|
||||
|
||||
fixed_len! { Create, CREATE, TAP_C_HANDSHAKE_LEN }
|
||||
fixed_len! { Created, CREATED, TAP_S_HANDSHAKE_LEN }
|
||||
fixed_len! { CreateFast, CREATE_FAST, FAST_C_HANDSHAKE_LEN }
|
||||
fixed_len! { CreatedFast, CREATED_FAST, FAST_S_HANDSHAKE_LEN }
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Create2 {
|
||||
handshake_type: u16,
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
impl ChanMsg for Create2 {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Create2(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_u16(self.handshake_type);
|
||||
assert!(self.handshake.len() <= std::u16::MAX as usize);
|
||||
w.write_u16(self.handshake.len() as u16);
|
||||
w.write_all(&self.handshake[..]);
|
||||
}
|
||||
}
|
||||
impl Readable for Create2 {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let handshake_type = r.take_u16()?;
|
||||
let hlen = r.take_u16()?;
|
||||
let handshake = r.take(hlen as usize)?.into();
|
||||
Ok(Create2 {
|
||||
handshake_type,
|
||||
handshake,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Created2 {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
impl ChanMsg for Created2 {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Created2(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
assert!(self.handshake.len() <= std::u16::MAX as usize);
|
||||
w.write_u16(self.handshake.len() as u16);
|
||||
w.write_all(&self.handshake[..]);
|
||||
}
|
||||
}
|
||||
impl Readable for Created2 {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let hlen = r.take_u16()?;
|
||||
let handshake = r.take(hlen as usize)?.into();
|
||||
Ok(Created2 { handshake })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Relay {
|
||||
body: Box<RawCellBody>,
|
||||
}
|
||||
impl std::fmt::Debug for Relay {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Relay").finish()
|
||||
}
|
||||
}
|
||||
impl ChanMsg for Relay {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Relay(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_all(&self.body[..])
|
||||
}
|
||||
}
|
||||
impl Readable for Relay {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let mut body = Box::new([0u8; CELL_BODY_LEN]);
|
||||
(&mut body[..]).copy_from_slice(r.take(CELL_BODY_LEN)?);
|
||||
Ok(Relay { body })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Destroy {}
|
||||
impl ChanMsg for Destroy {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Destroy(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, _w: &mut W) {}
|
||||
}
|
||||
impl Readable for Destroy {
|
||||
fn take_from(_r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(Destroy {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Netinfo {
|
||||
timestamp: u32,
|
||||
their_addr: IpAddr,
|
||||
my_addr: Vec<IpAddr>,
|
||||
}
|
||||
fn enc_one_netinfo_addr<W: Writer + ?Sized>(w: &mut W, addr: &IpAddr) {
|
||||
match addr {
|
||||
IpAddr::V4(ipv4) => {
|
||||
w.write_u8(0x04); // type.
|
||||
w.write_u8(4); // length.
|
||||
w.write_all(&ipv4.octets()[..]);
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
w.write_u8(0x06); // type.
|
||||
w.write_u8(16); // length.
|
||||
w.write_all(&ipv6.octets()[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn take_one_netinfo_addr(r: &mut Reader<'_>) -> Result<Option<IpAddr>> {
|
||||
let atype = r.take_u8()?;
|
||||
let alen = r.take_u8()?;
|
||||
let abody = r.take(alen as usize)?;
|
||||
match (atype, alen) {
|
||||
(0x04, 4) => {
|
||||
let bytes = [abody[0], abody[1], abody[2], abody[3]];
|
||||
Ok(Some(IpAddr::V4(bytes.into())))
|
||||
}
|
||||
(0x06, 16) => {
|
||||
// XXXX is there a better way?
|
||||
let mut bytes = [0u8; 16];
|
||||
(&mut bytes[..]).copy_from_slice(abody);
|
||||
Ok(Some(IpAddr::V6(bytes.into())))
|
||||
}
|
||||
(0x04, _) => Err(Error::BadMessage("XX")),
|
||||
(0x06, _) => Err(Error::BadMessage("XX")),
|
||||
(_, _) => Ok(None),
|
||||
}
|
||||
}
|
||||
impl ChanMsg for Netinfo {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Netinfo(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_u32(self.timestamp);
|
||||
enc_one_netinfo_addr(w, &self.their_addr);
|
||||
w.write_u8(self.my_addr.len() as u8); // XXXX overflow?
|
||||
for addr in self.my_addr.iter() {
|
||||
enc_one_netinfo_addr(w, &addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Readable for Netinfo {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let timestamp = r.take_u32()?;
|
||||
let their_addr = take_one_netinfo_addr(r)?.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
|
||||
let mut my_addr = Vec::new();
|
||||
let my_n_addrs = r.take_u8()?;
|
||||
for _ in 0..my_n_addrs {
|
||||
if let Some(a) = take_one_netinfo_addr(r)? {
|
||||
my_addr.push(a);
|
||||
}
|
||||
}
|
||||
Ok(Netinfo {
|
||||
timestamp,
|
||||
their_addr,
|
||||
my_addr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Versions {
|
||||
versions: Vec<u16>,
|
||||
}
|
||||
impl ChanMsg for Versions {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Versions(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
for v in self.versions.iter() {
|
||||
w.write_u16(*v);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Readable for Versions {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let mut versions = Vec::new();
|
||||
while r.remaining() > 0 {
|
||||
versions.push(r.take_u16()?);
|
||||
}
|
||||
Ok(Versions { versions })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PaddingNegotiate {
|
||||
command: u8,
|
||||
ito_low_ms: u16,
|
||||
ito_high_ms: u16,
|
||||
}
|
||||
impl ChanMsg for PaddingNegotiate {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::PaddingNegotiate(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_u8(0); // version
|
||||
w.write_u8(self.command);
|
||||
w.write_u16(self.ito_low_ms);
|
||||
w.write_u16(self.ito_high_ms);
|
||||
}
|
||||
}
|
||||
impl Readable for PaddingNegotiate {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let v = r.take_u8()?;
|
||||
if v != 0 {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
let command = r.take_u8()?;
|
||||
let ito_low_ms = r.take_u16()?;
|
||||
let ito_high_ms = r.take_u16()?;
|
||||
Ok(PaddingNegotiate {
|
||||
command,
|
||||
ito_low_ms,
|
||||
ito_high_ms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TorCert {
|
||||
certtype: u8,
|
||||
cert: Vec<u8>,
|
||||
}
|
||||
fn enc_one_tor_cert<W: Writer + ?Sized>(w: &mut W, c: &TorCert) {
|
||||
w.write_u8(c.certtype);
|
||||
w.write_u16(c.cert.len() as u16); // XXXX overflow?
|
||||
w.write_all(&c.cert[..]);
|
||||
}
|
||||
fn take_one_tor_cert(r: &mut Reader<'_>) -> Result<TorCert> {
|
||||
let certtype = r.take_u8()?;
|
||||
let certlen = r.take_u16()?;
|
||||
let cert = r.take(certlen as usize)?;
|
||||
Ok(TorCert {
|
||||
certtype,
|
||||
cert: cert.into(),
|
||||
})
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Certs {
|
||||
certs: Vec<TorCert>,
|
||||
}
|
||||
impl ChanMsg for Certs {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Certs(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_u8(self.certs.len() as u8); //XXXXX overflow?
|
||||
for c in self.certs.iter() {
|
||||
enc_one_tor_cert(w, &c)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Readable for Certs {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let n = r.take_u8()?;
|
||||
let mut certs = Vec::new();
|
||||
for _ in 0..n {
|
||||
certs.push(take_one_tor_cert(r)?);
|
||||
}
|
||||
Ok(Certs { certs })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuthChallenge {
|
||||
challenge: Vec<u8>,
|
||||
methods: Vec<u16>,
|
||||
}
|
||||
const CHALLENGE_LEN: usize = 32;
|
||||
impl ChanMsg for AuthChallenge {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::AuthChallenge(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_all(&self.challenge[..]);
|
||||
w.write_u16(self.methods.len() as u16); // XXXXX overflow
|
||||
for m in self.methods.iter() {
|
||||
w.write_u16(*m);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Readable for AuthChallenge {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let challenge = r.take(CHALLENGE_LEN)?.into();
|
||||
let n_methods = r.take_u16()?;
|
||||
let mut methods = Vec::new();
|
||||
for _ in 0..n_methods {
|
||||
methods.push(r.take_u16()?);
|
||||
}
|
||||
Ok(AuthChallenge { challenge, methods })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Authenticate {
|
||||
authtype: u16,
|
||||
auth: Vec<u8>,
|
||||
}
|
||||
impl ChanMsg for Authenticate {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Authenticate(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_u16(self.authtype);
|
||||
w.write_u16(self.auth.len() as u16); // XXXX overflow
|
||||
w.write_all(&self.auth[..]);
|
||||
}
|
||||
}
|
||||
impl Readable for Authenticate {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let authtype = r.take_u16()?;
|
||||
let authlen = r.take_u16()?;
|
||||
let auth = r.take(authlen as usize)?.into();
|
||||
Ok(Authenticate { authtype, auth })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Authorize {
|
||||
content: Vec<u8>,
|
||||
}
|
||||
impl ChanMsg for Authorize {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Authorize(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_all(&self.content[..])
|
||||
}
|
||||
}
|
||||
impl Readable for Authorize {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(Authorize {
|
||||
content: r.take(r.remaining())?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Unrecognized {
|
||||
cmd: ChanCmd,
|
||||
content: Vec<u8>,
|
||||
}
|
||||
fn unrecognized_with_cmd(cmd: ChanCmd, r: &mut Reader<'_>) -> Result<Unrecognized> {
|
||||
let mut u = Unrecognized::take_from(r)?;
|
||||
u.cmd = cmd;
|
||||
Ok(u)
|
||||
}
|
||||
impl Unrecognized {
|
||||
fn get_cmd(&self) -> ChanCmd {
|
||||
self.cmd
|
||||
}
|
||||
}
|
||||
impl ChanMsg for Unrecognized {
|
||||
fn as_message(self) -> ChannelMessage {
|
||||
ChannelMessage::Unrecognized(self)
|
||||
}
|
||||
fn write_body_onto<W: Writer + ?Sized>(&self, w: &mut W) {
|
||||
w.write_all(&self.content[..])
|
||||
}
|
||||
}
|
||||
impl Readable for Unrecognized {
|
||||
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(Unrecognized {
|
||||
cmd: 0.into(),
|
||||
content: r.take(r.remaining())?.into(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#![allow(dead_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub mod chancell;
|
||||
mod crypto;
|
||||
pub mod proto;
|
||||
mod util;
|
||||
|
|
|
@ -11,38 +11,9 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use caret::caret_int;
|
||||
use tor_bytes::{Error, Reader, Result, Writer};
|
||||
|
||||
pub mod cellmsg;
|
||||
pub mod relaymsg;
|
||||
|
||||
pub const CELL_DATA_LEN: usize = 509;
|
||||
|
||||
caret_int! {
|
||||
pub struct ChanCmd(u8) {
|
||||
PADDING = 0,
|
||||
CREATE = 1,
|
||||
CREATED = 2,
|
||||
RELAY = 3,
|
||||
DESTROY = 4,
|
||||
CREATE_FAST = 5,
|
||||
CREATED_FAST = 6,
|
||||
// note gap.
|
||||
NETINFO = 8,
|
||||
RELAY_EARLY = 9,
|
||||
CREATE2 = 10,
|
||||
CREATED2 = 11,
|
||||
PADDING_NEGOTIATE = 12,
|
||||
|
||||
VERSIONS = 7,
|
||||
VPADDING = 128,
|
||||
CERTS = 129,
|
||||
AUTH_CHALLENGE = 130,
|
||||
AUTHENTICATE = 131,
|
||||
AUTHORIZE = 132,
|
||||
}
|
||||
}
|
||||
|
||||
caret_int! {
|
||||
pub struct StreamCmd(u8) {
|
||||
BEGIN = 1,
|
||||
|
@ -74,28 +45,6 @@ caret_int! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct CircID(u32);
|
||||
|
||||
impl From<u32> for CircID {
|
||||
fn from(item: u32) -> Self {
|
||||
Self(item)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChanCell {
|
||||
circ: CircID,
|
||||
cmd: ChanCmd,
|
||||
body: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct CellRef<'a> {
|
||||
pub circ: CircID,
|
||||
pub cmd: ChanCmd,
|
||||
pub body: &'a [u8],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct StreamID(u16);
|
||||
|
||||
|
@ -104,121 +53,3 @@ pub struct RelayCellRef<'a> {
|
|||
pub cmd: StreamCmd,
|
||||
pub body: &'a [u8],
|
||||
}
|
||||
|
||||
pub struct ChannelProto {
|
||||
link_version: u16,
|
||||
relay_early_count: Option<u8>,
|
||||
}
|
||||
|
||||
impl ChanCmd {
|
||||
pub fn is_var_cell(self) -> bool {
|
||||
self == ChanCmd::VERSIONS || self.0 >= 128u8
|
||||
}
|
||||
}
|
||||
|
||||
impl ChannelProto {
|
||||
fn circ_id_len(&self) -> usize {
|
||||
if self.link_version >= 4 {
|
||||
4
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
pub fn get_cell<'a>(&self, bc: &mut Reader<'a>) -> Result<CellRef<'a>> {
|
||||
let circ = if self.circ_id_len() == 4 {
|
||||
CircID(bc.take_u32()?)
|
||||
} else {
|
||||
CircID(bc.take_u16()? as u32)
|
||||
};
|
||||
|
||||
let cmd = ChanCmd(bc.take_u8()?);
|
||||
|
||||
let body_len = if cmd.is_var_cell() {
|
||||
bc.take_u16()? as usize
|
||||
} else {
|
||||
CELL_DATA_LEN
|
||||
};
|
||||
let body = bc.take(body_len)?;
|
||||
|
||||
Ok(CellRef { circ, cmd, body })
|
||||
}
|
||||
pub fn enc_cell<'a, W: Writer>(&self, w: &mut W, cell: &CellRef<'a>) -> Result<()> {
|
||||
if self.circ_id_len() == 4 {
|
||||
w.write_u32(cell.circ.0);
|
||||
} else {
|
||||
if cell.circ.0 > std::u16::MAX as u32 {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
w.write_u16(cell.circ.0 as u16);
|
||||
}
|
||||
|
||||
w.write_u8(cell.cmd.0);
|
||||
|
||||
if cell.cmd.is_var_cell() {
|
||||
if cell.body.len() > std::u16::MAX as usize {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
w.write_u16(cell.body.len() as u16);
|
||||
w.write_all(cell.body);
|
||||
} else {
|
||||
if cell.body.len() > CELL_DATA_LEN {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
w.write_all(cell.body);
|
||||
w.write_zeros(CELL_DATA_LEN - cell.body.len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CellRef<'a> {
|
||||
fn to_cell(&self) -> ChanCell {
|
||||
ChanCell {
|
||||
circ: self.circ,
|
||||
cmd: self.cmd,
|
||||
body: self.body.into(),
|
||||
}
|
||||
}
|
||||
fn reader(&self) -> Reader<'_> {
|
||||
Reader::from_slice(&self.body[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl ChanCell {
|
||||
pub fn as_ref(&self) -> CellRef<'_> {
|
||||
CellRef {
|
||||
circ: self.circ,
|
||||
cmd: self.cmd,
|
||||
body: self.get_body(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CellData: Sized {
|
||||
fn get_circid(&self) -> CircID;
|
||||
fn get_cmd(&self) -> ChanCmd;
|
||||
fn get_body(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl CellData for ChanCell {
|
||||
fn get_circid(&self) -> CircID {
|
||||
self.circ
|
||||
}
|
||||
fn get_cmd(&self) -> ChanCmd {
|
||||
self.cmd
|
||||
}
|
||||
fn get_body(&self) -> &[u8] {
|
||||
&self.body[..]
|
||||
}
|
||||
}
|
||||
impl<'a> CellData for CellRef<'a> {
|
||||
fn get_circid(&self) -> CircID {
|
||||
self.circ
|
||||
}
|
||||
fn get_cmd(&self) -> ChanCmd {
|
||||
self.cmd
|
||||
}
|
||||
fn get_body(&self) -> &[u8] {
|
||||
self.body
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,588 +0,0 @@
|
|||
use crate::crypto::cell::{RawCellBody, CELL_BODY_LEN};
|
||||
use tor_bytes::{Error, Reader, Result, Writer};
|
||||
|
||||
use super::{CellData, CellRef, ChanCell, ChanCmd, CircID};
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
pub struct ChannelCell {
|
||||
circid: CircID,
|
||||
body: ChannelCellBody,
|
||||
}
|
||||
|
||||
impl ChannelCell {
|
||||
fn get_circid(&self) -> CircID {
|
||||
self.circid
|
||||
}
|
||||
fn get_cmd(&self) -> ChanCmd {
|
||||
self.body.get_cmd()
|
||||
}
|
||||
fn encode(self) -> ChanCell {
|
||||
let cmd = self.get_cmd();
|
||||
let circ = self.get_circid();
|
||||
let body = self.body.encode();
|
||||
ChanCell { cmd, circ, body }
|
||||
}
|
||||
fn decode(c: ChanCell) -> Result<Self> {
|
||||
let circid = c.get_circid();
|
||||
let cmd = c.get_cmd();
|
||||
let body = ChannelCellBody::decode(cmd, c.body)?;
|
||||
Ok(ChannelCell { circid, body })
|
||||
}
|
||||
fn decode_ref(c: &CellRef<'_>) -> Result<Self> {
|
||||
let circid = c.get_circid();
|
||||
let cmd = c.get_cmd();
|
||||
let mut r = Reader::from_slice(c.body);
|
||||
let body = ChannelCellBody::decode_from_reader(cmd, &mut r)?;
|
||||
Ok(ChannelCell { circid, body })
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub enum ChannelCellBody {
|
||||
Padding(PaddingBody),
|
||||
VPadding(VPaddingBody),
|
||||
Create(CreateBody),
|
||||
CreateFast(CreateFastBody),
|
||||
Create2(Create2Body),
|
||||
Created(CreatedBody),
|
||||
CreatedFast(CreatedFastBody),
|
||||
Created2(Created2Body),
|
||||
Relay(RelayBody),
|
||||
RelayEarly(RelayBody),
|
||||
Destroy(DestroyBody),
|
||||
Netinfo(NetinfoBody),
|
||||
Versions(VersionsBody),
|
||||
PaddingNegotiate(PaddingNegotiateBody),
|
||||
Certs(CertsBody),
|
||||
AuthChallenge(AuthChallengeBody),
|
||||
Authenticate(AuthenticateBody),
|
||||
Authorize(AuthorizeBody),
|
||||
Unrecognized(ChanCmd, UnrecognizedBody),
|
||||
}
|
||||
|
||||
impl ChannelCellBody {
|
||||
pub fn get_cmd(&self) -> ChanCmd {
|
||||
use ChannelCellBody::*;
|
||||
match self {
|
||||
Padding(_) => ChanCmd::PADDING,
|
||||
VPadding(_) => ChanCmd::VPADDING,
|
||||
Create(_) => ChanCmd::CREATE,
|
||||
CreateFast(_) => ChanCmd::CREATE_FAST,
|
||||
Create2(_) => ChanCmd::CREATE2,
|
||||
Created(_) => ChanCmd::CREATED,
|
||||
CreatedFast(_) => ChanCmd::CREATED_FAST,
|
||||
Created2(_) => ChanCmd::CREATED2,
|
||||
Relay(_) => ChanCmd::RELAY,
|
||||
RelayEarly(_) => ChanCmd::RELAY_EARLY,
|
||||
Destroy(_) => ChanCmd::DESTROY,
|
||||
Netinfo(_) => ChanCmd::NETINFO,
|
||||
Versions(_) => ChanCmd::VERSIONS,
|
||||
PaddingNegotiate(_) => ChanCmd::PADDING_NEGOTIATE,
|
||||
Certs(_) => ChanCmd::CERTS,
|
||||
AuthChallenge(_) => ChanCmd::AUTH_CHALLENGE,
|
||||
Authenticate(_) => ChanCmd::AUTHENTICATE,
|
||||
Authorize(_) => ChanCmd::AUTHORIZE,
|
||||
Unrecognized(c, _) => *c,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(self) -> Vec<u8> {
|
||||
use ChannelCellBody::*;
|
||||
match self {
|
||||
Padding(b) => b.encode(),
|
||||
VPadding(b) => b.encode(),
|
||||
Create(b) => b.encode(),
|
||||
CreateFast(b) => b.encode(),
|
||||
Create2(b) => b.encode(),
|
||||
Created(b) => b.encode(),
|
||||
CreatedFast(b) => b.encode(),
|
||||
Created2(b) => b.encode(),
|
||||
Relay(b) => b.encode(),
|
||||
RelayEarly(b) => b.encode(),
|
||||
Destroy(b) => b.encode(),
|
||||
Netinfo(b) => b.encode(),
|
||||
Versions(b) => b.encode(),
|
||||
PaddingNegotiate(b) => b.encode(),
|
||||
Certs(b) => b.encode(),
|
||||
AuthChallenge(b) => b.encode(),
|
||||
Authenticate(b) => b.encode(),
|
||||
Authorize(b) => b.encode(),
|
||||
Unrecognized(_, b) => b.encode(),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(cmd: ChanCmd, b: Vec<u8>) -> Result<Self> {
|
||||
use ChannelCellBody::*;
|
||||
Ok(match cmd {
|
||||
ChanCmd::PADDING => Padding(PaddingBody::decode(b)?),
|
||||
ChanCmd::VPADDING => VPadding(VPaddingBody::decode(b)?),
|
||||
ChanCmd::CREATE => Create(CreateBody::decode(b)?),
|
||||
ChanCmd::CREATE_FAST => CreateFast(CreateFastBody::decode(b)?),
|
||||
ChanCmd::CREATE2 => Create2(Create2Body::decode(b)?),
|
||||
ChanCmd::CREATED => Created(CreatedBody::decode(b)?),
|
||||
ChanCmd::CREATED_FAST => CreatedFast(CreatedFastBody::decode(b)?),
|
||||
ChanCmd::CREATED2 => Created2(Created2Body::decode(b)?),
|
||||
ChanCmd::RELAY => Relay(RelayBody::decode(b)?),
|
||||
ChanCmd::RELAY_EARLY => RelayEarly(RelayBody::decode(b)?),
|
||||
ChanCmd::DESTROY => Destroy(DestroyBody::decode(b)?),
|
||||
ChanCmd::NETINFO => Netinfo(NetinfoBody::decode(b)?),
|
||||
ChanCmd::VERSIONS => Versions(VersionsBody::decode(b)?),
|
||||
ChanCmd::PADDING_NEGOTIATE => PaddingNegotiate(PaddingNegotiateBody::decode(b)?),
|
||||
ChanCmd::CERTS => Certs(CertsBody::decode(b)?),
|
||||
ChanCmd::AUTH_CHALLENGE => AuthChallenge(AuthChallengeBody::decode(b)?),
|
||||
ChanCmd::AUTHENTICATE => Authenticate(AuthenticateBody::decode(b)?),
|
||||
ChanCmd::AUTHORIZE => Authorize(AuthorizeBody::decode(b)?),
|
||||
_ => Unrecognized(cmd, UnrecognizedBody::decode(b)?),
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_from_reader(cmd: ChanCmd, r: &mut Reader<'_>) -> Result<Self> {
|
||||
use ChannelCellBody::*;
|
||||
Ok(match cmd {
|
||||
ChanCmd::PADDING => Padding(PaddingBody::decode_from_reader(r)?),
|
||||
ChanCmd::VPADDING => VPadding(VPaddingBody::decode_from_reader(r)?),
|
||||
ChanCmd::CREATE => Create(CreateBody::decode_from_reader(r)?),
|
||||
ChanCmd::CREATE_FAST => CreateFast(CreateFastBody::decode_from_reader(r)?),
|
||||
ChanCmd::CREATE2 => Create2(Create2Body::decode_from_reader(r)?),
|
||||
ChanCmd::CREATED => Created(CreatedBody::decode_from_reader(r)?),
|
||||
ChanCmd::CREATED_FAST => CreatedFast(CreatedFastBody::decode_from_reader(r)?),
|
||||
ChanCmd::CREATED2 => Created2(Created2Body::decode_from_reader(r)?),
|
||||
ChanCmd::RELAY => Relay(RelayBody::decode_from_reader(r)?),
|
||||
ChanCmd::RELAY_EARLY => RelayEarly(RelayBody::decode_from_reader(r)?),
|
||||
ChanCmd::DESTROY => Destroy(DestroyBody::decode_from_reader(r)?),
|
||||
ChanCmd::NETINFO => Netinfo(NetinfoBody::decode_from_reader(r)?),
|
||||
ChanCmd::VERSIONS => Versions(VersionsBody::decode_from_reader(r)?),
|
||||
ChanCmd::PADDING_NEGOTIATE => {
|
||||
PaddingNegotiate(PaddingNegotiateBody::decode_from_reader(r)?)
|
||||
}
|
||||
ChanCmd::CERTS => Certs(CertsBody::decode_from_reader(r)?),
|
||||
ChanCmd::AUTH_CHALLENGE => AuthChallenge(AuthChallengeBody::decode_from_reader(r)?),
|
||||
ChanCmd::AUTHENTICATE => Authenticate(AuthenticateBody::decode_from_reader(r)?),
|
||||
ChanCmd::AUTHORIZE => Authorize(AuthorizeBody::decode_from_reader(r)?),
|
||||
_ => Unrecognized(cmd, UnrecognizedBody::decode_from_reader(r)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
trait Body: Sized {
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self>;
|
||||
fn decode(body: Vec<u8>) -> Result<Self> {
|
||||
let mut reader = Reader::from_slice(&body[..]);
|
||||
Self::decode_from_reader(&mut reader)
|
||||
}
|
||||
fn encode(self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
pub struct PaddingBody {}
|
||||
impl Body for PaddingBody {
|
||||
fn decode_from_reader(_r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(PaddingBody {})
|
||||
}
|
||||
fn encode(self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VPaddingBody {
|
||||
len: u16,
|
||||
}
|
||||
impl Body for VPaddingBody {
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
if r.remaining() > std::u16::MAX as usize {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
Ok(VPaddingBody {
|
||||
len: r.remaining() as u16,
|
||||
}) // XXXX overflow?
|
||||
}
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut res = Vec::new();
|
||||
res.resize(self.len as usize, 0);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
// XXXX MOVE THESE
|
||||
pub const TAP_C_HANDSHAKE_LEN: usize = 128 * 2 + 42;
|
||||
pub const TAP_S_HANDSHAKE_LEN: usize = 128 + 20;
|
||||
|
||||
const FAST_C_HANDSHAKE_LEN: usize = 20;
|
||||
const FAST_S_HANDSHAKE_LEN: usize = 20 * 2;
|
||||
|
||||
pub struct CreateBody {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for CreateBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.handshake
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(CreateBody {
|
||||
handshake: r.take(TAP_C_HANDSHAKE_LEN)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreateFastBody {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for CreateFastBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.handshake
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(CreateFastBody {
|
||||
handshake: r.take(FAST_C_HANDSHAKE_LEN)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Create2Body {
|
||||
handshake_type: u16,
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for Create2Body {
|
||||
fn encode(mut self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_u16(self.handshake_type);
|
||||
body.write_u16(self.handshake.len() as u16); // XXXX overflow?
|
||||
body.append(&mut self.handshake);
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let handshake_type = r.take_u16()?;
|
||||
let hlen = r.take_u16()?;
|
||||
let handshake = r.take(hlen as usize)?.into();
|
||||
Ok(Create2Body {
|
||||
handshake_type,
|
||||
handshake,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreatedBody {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
impl Body for CreatedBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.handshake
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(CreatedBody {
|
||||
handshake: r.take(TAP_S_HANDSHAKE_LEN)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreatedFastBody {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
impl Body for CreatedFastBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.handshake
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(CreatedFastBody {
|
||||
handshake: r.take(FAST_S_HANDSHAKE_LEN)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Created2Body {
|
||||
handshake: Vec<u8>,
|
||||
}
|
||||
impl Body for Created2Body {
|
||||
fn encode(mut self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_u16(self.handshake.len() as u16); // XXX overflow?
|
||||
body.append(&mut self.handshake);
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let hlen = r.take_u16()?;
|
||||
let handshake = r.take(hlen as usize)?.into();
|
||||
Ok(Created2Body { handshake })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RelayBody {
|
||||
body: Box<RawCellBody>,
|
||||
}
|
||||
|
||||
impl Body for RelayBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
// Avoids copy.
|
||||
(self.body as Box<[_]>).into_vec()
|
||||
}
|
||||
|
||||
fn decode(body: Vec<u8>) -> Result<Self> {
|
||||
if body.len() != CELL_BODY_LEN {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
// Once rust has const generics this should be doable safely. XXXX
|
||||
// Till then, I'll avoid the unsafe.
|
||||
/*
|
||||
let boxed_slice = body.into_boxed_slice();
|
||||
let boxed_array = unsafe {
|
||||
Box::from_raw(Box::into_raw(boxed_slice) as *mut RawCellBody)
|
||||
};
|
||||
*/
|
||||
let mut boxed_array = Box::new([0u8; CELL_BODY_LEN]);
|
||||
(&mut boxed_array[..]).copy_from_slice(&body[..]);
|
||||
Ok(RelayBody { body: boxed_array })
|
||||
}
|
||||
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Self::decode(r.take(CELL_BODY_LEN)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DestroyBody {}
|
||||
impl Body for DestroyBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
}
|
||||
fn decode_from_reader(_r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(DestroyBody {})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetinfoBody {
|
||||
timestamp: u32,
|
||||
their_addr: IpAddr,
|
||||
my_addr: Vec<IpAddr>,
|
||||
}
|
||||
fn enc_one_netinfo_addr(w: &mut Vec<u8>, addr: &IpAddr) {
|
||||
match addr {
|
||||
IpAddr::V4(ipv4) => {
|
||||
w.write_u8(0x04); // type.
|
||||
w.write_u8(4); // length.
|
||||
w.write_all(&ipv4.octets()[..]);
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
w.write_u8(0x06); // type.
|
||||
w.write_u8(16); // length.
|
||||
w.write_all(&ipv6.octets()[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn take_one_netinfo_addr(r: &mut Reader<'_>) -> Result<Option<IpAddr>> {
|
||||
let atype = r.take_u8()?;
|
||||
let alen = r.take_u8()?;
|
||||
let abody = r.take(alen as usize)?;
|
||||
match (atype, alen) {
|
||||
(0x04, 4) => {
|
||||
let bytes = [abody[0], abody[1], abody[2], abody[3]];
|
||||
Ok(Some(IpAddr::V4(bytes.into())))
|
||||
}
|
||||
(0x06, 16) => {
|
||||
// XXXX is there a better way?
|
||||
let mut bytes = [0u8; 16];
|
||||
(&mut bytes[..]).copy_from_slice(abody);
|
||||
Ok(Some(IpAddr::V6(bytes.into())))
|
||||
}
|
||||
(0x04, _) => Err(Error::BadMessage("XX")),
|
||||
(0x06, _) => Err(Error::BadMessage("XX")),
|
||||
(_, _) => Ok(None),
|
||||
}
|
||||
}
|
||||
impl Body for NetinfoBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_u32(self.timestamp);
|
||||
enc_one_netinfo_addr(&mut body, &self.their_addr);
|
||||
body.write_u8(self.my_addr.len() as u8); // XXXX overflow?
|
||||
for addr in self.my_addr {
|
||||
enc_one_netinfo_addr(&mut body, &addr);
|
||||
}
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let timestamp = r.take_u32()?;
|
||||
let their_addr = take_one_netinfo_addr(r)?.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
|
||||
let mut my_addr = Vec::new();
|
||||
let my_n_addrs = r.take_u8()?;
|
||||
for _ in 0..my_n_addrs {
|
||||
if let Some(a) = take_one_netinfo_addr(r)? {
|
||||
my_addr.push(a);
|
||||
}
|
||||
}
|
||||
Ok(NetinfoBody {
|
||||
timestamp,
|
||||
their_addr,
|
||||
my_addr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VersionsBody {
|
||||
versions: Vec<u16>,
|
||||
}
|
||||
impl Body for VersionsBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
for v in self.versions {
|
||||
body.write_u16(v);
|
||||
}
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let mut versions = Vec::new();
|
||||
while r.remaining() > 0 {
|
||||
versions.push(r.take_u16()?);
|
||||
}
|
||||
Ok(VersionsBody { versions })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PaddingNegotiateBody {
|
||||
command: u8,
|
||||
ito_low_ms: u16,
|
||||
ito_high_ms: u16,
|
||||
}
|
||||
impl Body for PaddingNegotiateBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_u8(0); // version
|
||||
body.write_u8(self.command);
|
||||
body.write_u16(self.ito_low_ms);
|
||||
body.write_u16(self.ito_high_ms);
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let v = r.take_u8()?;
|
||||
if v != 0 {
|
||||
return Err(Error::BadMessage("XX"));
|
||||
}
|
||||
let command = r.take_u8()?;
|
||||
let ito_low_ms = r.take_u16()?;
|
||||
let ito_high_ms = r.take_u16()?;
|
||||
Ok(PaddingNegotiateBody {
|
||||
command,
|
||||
ito_low_ms,
|
||||
ito_high_ms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TorCert {
|
||||
certtype: u8,
|
||||
cert: Vec<u8>,
|
||||
}
|
||||
fn enc_one_tor_cert(w: &mut Vec<u8>, c: &TorCert) {
|
||||
w.write_u8(c.certtype);
|
||||
w.write_u16(c.cert.len() as u16); // XXXX overflow?
|
||||
w.write_all(&c.cert[..]);
|
||||
}
|
||||
fn take_one_tor_cert(r: &mut Reader<'_>) -> Result<TorCert> {
|
||||
let certtype = r.take_u8()?;
|
||||
let certlen = r.take_u16()?;
|
||||
let cert = r.take(certlen as usize)?;
|
||||
Ok(TorCert {
|
||||
certtype,
|
||||
cert: cert.into(),
|
||||
})
|
||||
}
|
||||
pub struct CertsBody {
|
||||
certs: Vec<TorCert>,
|
||||
}
|
||||
impl Body for CertsBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
// XXXX overflow.
|
||||
let mut w = Vec::new();
|
||||
w.write_u8(self.certs.len() as u8); //XXXXX overflow?
|
||||
for c in self.certs {
|
||||
enc_one_tor_cert(&mut w, &c)
|
||||
}
|
||||
w
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let n = r.take_u8()?;
|
||||
let mut certs = Vec::new();
|
||||
for _ in 0..n {
|
||||
certs.push(take_one_tor_cert(r)?);
|
||||
}
|
||||
Ok(CertsBody { certs })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthChallengeBody {
|
||||
challenge: Vec<u8>,
|
||||
methods: Vec<u16>,
|
||||
}
|
||||
const CHALLENGE_LEN: usize = 32;
|
||||
impl Body for AuthChallengeBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_all(&self.challenge[..]);
|
||||
body.write_u16(self.methods.len() as u16); // XXXXX overflow
|
||||
for m in self.methods {
|
||||
body.write_u16(m);
|
||||
}
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let challenge = r.take(CHALLENGE_LEN)?.into();
|
||||
let n_methods = r.take_u16()?;
|
||||
let mut methods = Vec::new();
|
||||
for _ in 0..n_methods {
|
||||
methods.push(r.take_u16()?);
|
||||
}
|
||||
Ok(AuthChallengeBody { challenge, methods })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthenticateBody {
|
||||
authtype: u16,
|
||||
auth: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for AuthenticateBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
let mut body = Vec::new();
|
||||
body.write_u16(self.authtype);
|
||||
body.write_u16(self.auth.len() as u16); // XXXX overflow
|
||||
body.write_all(&self.auth[..]);
|
||||
body
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let authtype = r.take_u16()?;
|
||||
let authlen = r.take_u16()?;
|
||||
let auth = r.take(authlen as usize)?.into();
|
||||
Ok(AuthenticateBody { authtype, auth })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthorizeBody {
|
||||
content: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for AuthorizeBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.content
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(AuthorizeBody {
|
||||
content: r.take(r.remaining())?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnrecognizedBody {
|
||||
content: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Body for UnrecognizedBody {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
self.content
|
||||
}
|
||||
fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
|
||||
Ok(UnrecognizedBody {
|
||||
content: r.take(r.remaining())?.into(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use super::cellmsg::{TAP_C_HANDSHAKE_LEN, TAP_S_HANDSHAKE_LEN};
|
||||
use super::StreamCmd;
|
||||
use super::StreamID;
|
||||
use crate::chancell::msg::{TAP_C_HANDSHAKE_LEN, TAP_S_HANDSHAKE_LEN};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use tor_bytes::{Error, Result};
|
||||
use tor_bytes::{Readable, Reader, Writeable, Writer};
|
||||
|
|
Loading…
Reference in New Issue