mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-02-09 01:59:16 +08:00
udp relay supports AEAD ciphers
This commit is contained in:
113
src/relay/udprelay/crypto_io.rs
Normal file
113
src/relay/udprelay/crypto_io.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
//! Crypto protocol for ShadowSocks UDP
|
||||
//!
|
||||
//! Payload with stream cipher
|
||||
//! ```plain
|
||||
//! +-------+----------+
|
||||
//! | IV | Payload |
|
||||
//! +-------+----------+
|
||||
//! | Fixed | Variable |
|
||||
//! +-------+----------+
|
||||
//! ```
|
||||
//!
|
||||
//! Payload with AEAD cipher
|
||||
//!
|
||||
//! ```plain
|
||||
//! UDP (after encryption, *ciphertext*)
|
||||
//! +--------+-----------+-----------+
|
||||
//! | NONCE | *Data* | Data_TAG |
|
||||
//! +--------+-----------+-----------+
|
||||
//! | Fixed | Variable | Fixed |
|
||||
//! +--------+-----------+-----------+
|
||||
//! ```
|
||||
|
||||
use std::io;
|
||||
|
||||
use crypto::{self, CipherType, CipherCategory, CryptoMode};
|
||||
use crypto::StreamCipher;
|
||||
|
||||
/// Encrypt payload into ShadowSocks UDP encrypted packet
|
||||
pub fn encrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
match t.category() {
|
||||
CipherCategory::Stream => encrypt_payload_stream(t, key, payload),
|
||||
CipherCategory::Aead => encrypt_payload_aead(t, key, payload),
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
let mut iv = t.gen_init_vec();
|
||||
let mut cipher = crypto::new_stream(t, key, &iv, CryptoMode::Encrypt);
|
||||
|
||||
let mut send_payload = Vec::with_capacity(iv.len() + payload.len());
|
||||
send_payload.append(&mut iv);
|
||||
try!(cipher.update(&payload[..], &mut send_payload));
|
||||
try!(cipher.finalize(&mut send_payload));
|
||||
Ok(send_payload)
|
||||
}
|
||||
|
||||
fn encrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
let mut iv = t.gen_init_vec();
|
||||
let tag_size = t.tag_size();
|
||||
let mut cipher = crypto::new_aead_encryptor(t, key, &iv);
|
||||
|
||||
let mut send_payload = Vec::with_capacity(iv.len() + payload.len() + tag_size);
|
||||
send_payload.append(&mut iv);
|
||||
let start_pos = send_payload.len();
|
||||
send_payload.resize(start_pos + payload.len(), 0);
|
||||
|
||||
let mut tag_buf = vec![0u8; tag_size];
|
||||
|
||||
cipher.encrypt(payload, &mut send_payload[start_pos..], &mut tag_buf);
|
||||
|
||||
send_payload.append(&mut tag_buf);
|
||||
|
||||
Ok(send_payload)
|
||||
}
|
||||
|
||||
/// Decrypt payload from ShadowSocks UDP encrypted packet
|
||||
pub fn decrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
match t.category() {
|
||||
CipherCategory::Stream => decrypt_payload_stream(t, key, payload),
|
||||
CipherCategory::Aead => decrypt_payload_aead(t, key, payload),
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
let iv_size = t.iv_size();
|
||||
if payload.len() < iv_size {
|
||||
let err = io::Error::new(io::ErrorKind::Other, "udp packet too short");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let iv = &payload[..iv_size];
|
||||
let data = &payload[iv_size..];
|
||||
|
||||
let mut cipher = crypto::new_stream(t, key, iv, CryptoMode::Decrypt);
|
||||
|
||||
let mut recv_payload = Vec::with_capacity(data.len());
|
||||
try!(cipher.update(data, &mut recv_payload));
|
||||
try!(cipher.finalize(&mut recv_payload));
|
||||
|
||||
Ok(recv_payload)
|
||||
}
|
||||
|
||||
fn decrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
|
||||
let tag_size = t.tag_size();
|
||||
let iv_size = t.iv_size();
|
||||
|
||||
if payload.len() < tag_size + iv_size {
|
||||
let err = io::Error::new(io::ErrorKind::Other, "udp packet too short");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let nounce = &payload[..iv_size];
|
||||
let data = &payload[iv_size..payload.len() - tag_size];
|
||||
let tag = &payload[payload.len() - tag_size..];
|
||||
let data_length = payload.len() - tag_size - iv_size;
|
||||
|
||||
let mut cipher = crypto::new_aead_decryptor(t, key, nounce);
|
||||
|
||||
let mut recv_payload = vec![0u8; data_length];
|
||||
try!(cipher.decrypt(data, &mut recv_payload, tag));
|
||||
|
||||
Ok(recv_payload)
|
||||
}
|
||||
@@ -77,11 +77,9 @@ use relay::{BoxIoFuture, boxed_future};
|
||||
use relay::loadbalancing::server::{LoadBalancer, RoundRobin};
|
||||
use relay::dns_resolver::DnsResolver;
|
||||
use relay::socks5::{Address, UdpAssociateHeader};
|
||||
use crypto::{self, StreamCipher};
|
||||
use crypto::CryptoMode;
|
||||
|
||||
use super::{MAXIMUM_ASSOCIATE_MAP_SIZE, MAXIMUM_UDP_PAYLOAD_SIZE};
|
||||
use super::{send_to, recv_from};
|
||||
use super::crypto_io::{encrypt_payload, decrypt_payload};
|
||||
|
||||
type AssociateMap = LruCache<Address, SocketAddr>;
|
||||
type ServerCache = LruCache<SocketAddr, Rc<ServerConfig>>;
|
||||
@@ -126,24 +124,7 @@ impl Client {
|
||||
svr_cfg.addr(),
|
||||
buf.len());
|
||||
|
||||
let iv_len = svr_cfg.method().iv_size();
|
||||
if buf.len() < iv_len {
|
||||
error!("Invalid ShadowSocks UDP packet, expected IV length {}, packet length {}",
|
||||
iv_len,
|
||||
buf.len());
|
||||
let err = io::Error::new(io::ErrorKind::Other, "early eof");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let iv = &buf[..iv_len];
|
||||
let mut cipher = crypto::new_stream(svr_cfg.method(), svr_cfg.key(), iv, CryptoMode::Decrypt);
|
||||
|
||||
// Decrypt payload with cipher
|
||||
let mut payload = Vec::with_capacity(buf.len());
|
||||
try!(cipher.update(&buf[iv_len..], &mut payload));
|
||||
try!(cipher.finalize(&mut payload));
|
||||
|
||||
Ok(payload)
|
||||
decrypt_payload(svr_cfg.method(), svr_cfg.key(), buf)
|
||||
})
|
||||
.and_then(move |payload| {
|
||||
// Get Address from the front of payload (ShadowSocks protocol)
|
||||
@@ -180,7 +161,7 @@ impl Client {
|
||||
})
|
||||
})
|
||||
.and_then(|(client_addr, assoc, reply_body)| {
|
||||
send_to(socket, reply_body, client_addr).map(move |(socket, _, _)| {
|
||||
socket.send_dgram(reply_body, client_addr).map(move |(socket, _)| {
|
||||
Client {
|
||||
assoc: assoc,
|
||||
servers: servers,
|
||||
@@ -235,26 +216,15 @@ impl Client {
|
||||
let svr_cfg = server_picker.borrow_mut().pick_server();
|
||||
|
||||
// Client -> Proxy
|
||||
let iv = svr_cfg.method().gen_init_vec();
|
||||
let mut cipher = crypto::new_stream(svr_cfg.method(),
|
||||
svr_cfg.key(),
|
||||
&iv[..],
|
||||
CryptoMode::Encrypt);
|
||||
|
||||
let payload_buf = Vec::with_capacity(payload.len());
|
||||
// Append Address to the front (ShadowSocks protocol)
|
||||
assoc_addr.write_to(payload_buf)
|
||||
assoc_addr.write_to(Vec::with_capacity(payload.len()))
|
||||
.and_then(move |mut payload_buf| {
|
||||
payload_buf.extend_from_slice(&payload[header_len..]);
|
||||
Ok(payload_buf)
|
||||
})
|
||||
.and_then(move |payload| -> io::Result<_> {
|
||||
// Encrypt the whole body as payload
|
||||
let mut send_payload = Vec::with_capacity(iv.len() + payload.len());
|
||||
send_payload.extend_from_slice(&iv[..]);
|
||||
try!(cipher.update(&payload[..], &mut send_payload));
|
||||
try!(cipher.finalize(&mut send_payload));
|
||||
Ok((svr_cfg, send_payload))
|
||||
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &payload).map(move |b| (svr_cfg, b))
|
||||
})
|
||||
.map_err(From::from)
|
||||
.and_then(move |(svr_cfg, payload)| {
|
||||
@@ -267,8 +237,8 @@ impl Client {
|
||||
svrs_ref.insert(addr, svr_cfg.clone());
|
||||
}
|
||||
|
||||
send_to(socket, payload, addr).map(|(socket, body, len)| {
|
||||
trace!("Body size: {}, sent packet size: {}", body.len(), len);
|
||||
socket.send_dgram(payload, addr).map(|(socket, body)| {
|
||||
trace!("Sent body, size: {}", body.len());
|
||||
Client {
|
||||
assoc: assoc,
|
||||
server_picker: server_picker,
|
||||
@@ -287,7 +257,7 @@ impl Client {
|
||||
fn handle_once(self) -> BoxIoFuture<Client> {
|
||||
let Client { assoc, server_picker, servers, socket } = self;
|
||||
|
||||
let fut = recv_from(socket, vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]).and_then(move |(socket, buf, n, src)| {
|
||||
let fut = socket.recv_dgram(vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]).and_then(move |(socket, buf, n, src)| {
|
||||
// Reassemble Client
|
||||
let c = Client {
|
||||
assoc: assoc,
|
||||
|
||||
@@ -21,19 +21,15 @@
|
||||
|
||||
//! Relay for UDP implementation
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::mem;
|
||||
use std::io;
|
||||
|
||||
use tokio_core::net::UdpSocket;
|
||||
|
||||
use net2::UdpBuilder;
|
||||
|
||||
use futures::{Future, Poll, Async};
|
||||
|
||||
pub mod local;
|
||||
pub mod server;
|
||||
|
||||
mod crypto_io;
|
||||
|
||||
/// The maximum UDP payload size (defined in the original shadowsocks Python)
|
||||
///
|
||||
/// *I cannot find any references about why clowwindy used this value as the maximum
|
||||
@@ -44,85 +40,6 @@ pub const MAXIMUM_UDP_PAYLOAD_SIZE: usize = 65536;
|
||||
/// Maximum associations to maintain
|
||||
pub const MAXIMUM_ASSOCIATE_MAP_SIZE: usize = 65536;
|
||||
|
||||
/// Future for `recv_from`
|
||||
pub enum RecvFromUdpSocket<B: AsMut<[u8]>> {
|
||||
Pending { socket: UdpSocket, buf: B },
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<B: AsMut<[u8]>> Future for RecvFromUdpSocket<B> {
|
||||
type Item = (UdpSocket, B, usize, SocketAddr);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let (length, src) = match *self {
|
||||
RecvFromUdpSocket::Empty => panic!("poll after RecvFromUdpSocket is finished"),
|
||||
RecvFromUdpSocket::Pending { ref socket, ref mut buf } => {
|
||||
if socket.poll_read().is_not_ready() {
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
|
||||
try_nb!(socket.recv_from(buf.as_mut()))
|
||||
}
|
||||
};
|
||||
|
||||
match mem::replace(self, RecvFromUdpSocket::Empty) {
|
||||
RecvFromUdpSocket::Pending { socket, buf } => Ok((socket, buf, length, src).into()),
|
||||
RecvFromUdpSocket::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive from UdpSocket
|
||||
pub fn recv_from<B: AsMut<[u8]>>(socket: UdpSocket, buf: B) -> RecvFromUdpSocket<B> {
|
||||
RecvFromUdpSocket::Pending {
|
||||
socket: socket,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
|
||||
/// Future for `send_to`
|
||||
pub enum SendToUdpSocket<B: AsRef<[u8]>> {
|
||||
Pending {
|
||||
socket: UdpSocket,
|
||||
buf: B,
|
||||
target_addr: SocketAddr,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> Future for SendToUdpSocket<B> {
|
||||
type Item = (UdpSocket, B, usize);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let length = match self {
|
||||
&mut SendToUdpSocket::Empty => panic!("poll after SendToUdpSocket is finished"),
|
||||
&mut SendToUdpSocket::Pending { ref socket, ref buf, ref target_addr } => {
|
||||
if socket.poll_write().is_not_ready() {
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
|
||||
try_nb!(socket.send_to(buf.as_ref(), target_addr))
|
||||
}
|
||||
};
|
||||
|
||||
match mem::replace(self, SendToUdpSocket::Empty) {
|
||||
SendToUdpSocket::Pending { socket, buf, .. } => Ok((socket, buf, length).into()),
|
||||
SendToUdpSocket::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send data to `UdpSocket`
|
||||
pub fn send_to<B: AsRef<[u8]>>(socket: UdpSocket, buf: B, target: SocketAddr) -> SendToUdpSocket<B> {
|
||||
SendToUdpSocket::Pending {
|
||||
socket: socket,
|
||||
buf: buf,
|
||||
target_addr: target,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn reuse_port(builder: &UdpBuilder) -> io::Result<&UdpBuilder> {
|
||||
use net2::unix::UnixUdpBuilderExt;
|
||||
|
||||
@@ -41,11 +41,9 @@ use config::{Config, ServerConfig};
|
||||
use relay::{BoxIoFuture, boxed_future};
|
||||
use relay::dns_resolver::DnsResolver;
|
||||
use relay::socks5::Address;
|
||||
use crypto::{self, StreamCipher};
|
||||
use crypto::CryptoMode;
|
||||
|
||||
use super::{MAXIMUM_ASSOCIATE_MAP_SIZE, MAXIMUM_UDP_PAYLOAD_SIZE};
|
||||
use super::{send_to, recv_from};
|
||||
use super::crypto_io::{encrypt_payload, decrypt_payload};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Associate {
|
||||
@@ -72,25 +70,7 @@ impl ConnectionContext {
|
||||
// Client -> Remote
|
||||
let fut = futures::lazy(move || {
|
||||
let buf = &buf[..n];
|
||||
|
||||
let iv_len = svr_cfg.method().iv_size();
|
||||
if buf.len() < iv_len {
|
||||
error!("Invalid ShadowSocks UDP packet, expected IV length {}, packet length {}",
|
||||
iv_len,
|
||||
buf.len());
|
||||
let err = io::Error::new(io::ErrorKind::Other, "early eof");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let iv = &buf[..iv_len];
|
||||
let mut cipher = crypto::new_stream(svr_cfg.method(), svr_cfg.key(), iv, CryptoMode::Decrypt);
|
||||
|
||||
// Decrypt payload from Client
|
||||
let mut payload = Vec::with_capacity(buf.len());
|
||||
try!(cipher.update(&buf[iv_len..], &mut payload));
|
||||
try!(cipher.finalize(&mut payload));
|
||||
|
||||
Ok((payload, svr_cfg))
|
||||
decrypt_payload(svr_cfg.method(), svr_cfg.key(), buf).map(move |b| (b, svr_cfg))
|
||||
})
|
||||
.and_then(move |(payload, svr_cfg)| {
|
||||
// Read Address in the front (ShadowSocks protocol)
|
||||
@@ -117,10 +97,10 @@ impl ConnectionContext {
|
||||
client_addr: src,
|
||||
});
|
||||
|
||||
send_to(socket, body, remote_addr)
|
||||
socket.send_dgram(body, remote_addr)
|
||||
})
|
||||
.map(move |(socket, body, len)| {
|
||||
trace!("Sent body {} bytes, actual {} bytes", body.len(), len);
|
||||
.map(move |(socket, body)| {
|
||||
trace!("Sent body, len: {} bytes", body.len());
|
||||
ConnectionContext {
|
||||
assoc: assoc,
|
||||
svr_cfg: svr_cfg,
|
||||
@@ -149,26 +129,20 @@ impl ConnectionContext {
|
||||
buf_len);
|
||||
|
||||
// Client <- Remote
|
||||
let mut iv = svr_cfg.method().gen_init_vec();
|
||||
let mut cipher = crypto::new_stream(svr_cfg.method(),
|
||||
svr_cfg.key(),
|
||||
&iv[..],
|
||||
CryptoMode::Encrypt);
|
||||
|
||||
// Append Address in front of body (ShadowSocks protocol)
|
||||
let cloned_svr_cfg = svr_cfg.clone();
|
||||
let fut = address.write_to(Vec::with_capacity(buf_len))
|
||||
.map(move |mut send_buf| {
|
||||
send_buf.extend_from_slice(&buf[..n]);
|
||||
send_buf
|
||||
})
|
||||
.and_then(move |send_buf| -> io::Result<_> {
|
||||
try!(cipher.update(&send_buf[..], &mut iv));
|
||||
try!(cipher.finalize(&mut iv));
|
||||
Ok(iv)
|
||||
let svr_cfg = cloned_svr_cfg;
|
||||
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &send_buf)
|
||||
})
|
||||
.and_then(move |final_buf| send_to(socket, final_buf, client_addr))
|
||||
.map(|(socket, buf, len)| {
|
||||
trace!("Sent body {} actual {}", buf.len(), len);
|
||||
.and_then(move |final_buf| socket.send_dgram(final_buf, client_addr))
|
||||
.map(|(socket, buf)| {
|
||||
trace!("Sent body len: {}", buf.len());
|
||||
ConnectionContext {
|
||||
assoc: assoc,
|
||||
svr_cfg: svr_cfg,
|
||||
@@ -183,7 +157,7 @@ impl ConnectionContext {
|
||||
fn handle_once(self) -> BoxIoFuture<ConnectionContext> {
|
||||
let ConnectionContext { assoc, svr_cfg, socket } = self;
|
||||
|
||||
let fut = recv_from(socket, vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE])
|
||||
let fut = socket.recv_dgram(vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE])
|
||||
.and_then(move |(socket, buf, n, src)| {
|
||||
let c = ConnectionContext {
|
||||
assoc: assoc.clone(),
|
||||
|
||||
Reference in New Issue
Block a user