mirror of https://github.com/ctz/rustls
Add MTU control, and test fragmentation paths
This commit is contained in:
parent
adc43ca1fa
commit
5aadeb0e87
|
@ -247,7 +247,7 @@ before making the connection. --http replaces this with a
|
|||
basic HTTP GET request for /.
|
||||
|
||||
Usage:
|
||||
tlsclient [--verbose] [-p PORT] [--http] [--cache CACHE] [--cafile CAFILE] [--suite SUITE...] [--proto PROTOCOL...] <hostname>
|
||||
tlsclient [--verbose] [-p PORT] [--http] [--mtu MTU] [--cache CACHE] [--cafile CAFILE] [--suite SUITE...] [--proto PROTOCOL...] <hostname>
|
||||
tlsclient --version
|
||||
tlsclient --help
|
||||
|
||||
|
@ -260,6 +260,7 @@ Options:
|
|||
--proto PROTOCOL Send ALPN extension containing PROTOCOL.
|
||||
--cache CACHE Save session cache to file CACHE.
|
||||
--verbose Emit log output.
|
||||
--mtu MTU Limit outgoing messages to MTU bytes.
|
||||
--version Show tool version.
|
||||
--help Show this screen.
|
||||
";
|
||||
|
@ -271,6 +272,7 @@ struct Args {
|
|||
flag_verbose: bool,
|
||||
flag_suite: Vec<String>,
|
||||
flag_proto: Vec<String>,
|
||||
flag_mtu: Option<usize>,
|
||||
flag_cafile: Option<String>,
|
||||
flag_cache: Option<String>,
|
||||
arg_hostname: String
|
||||
|
@ -336,6 +338,7 @@ fn make_config(args: &Args) -> Arc<ClientConfig> {
|
|||
|
||||
config.set_protocols(&args.flag_proto);
|
||||
config.set_persistence(persist);
|
||||
config.set_mtu(&args.flag_mtu);
|
||||
|
||||
Arc::new(config)
|
||||
}
|
||||
|
|
|
@ -63,7 +63,10 @@ pub struct ClientConfig {
|
|||
pub alpn_protocols: Vec<String>,
|
||||
|
||||
/// How we store session data or tickets.
|
||||
pub session_persistence: cell::RefCell<Box<StoresSessions>>
|
||||
pub session_persistence: cell::RefCell<Box<StoresSessions>>,
|
||||
|
||||
/// Our MTU. If None, we don't limit TLS message sizes.
|
||||
pub mtu: Option<usize>
|
||||
}
|
||||
|
||||
impl ClientConfig {
|
||||
|
@ -75,7 +78,8 @@ impl ClientConfig {
|
|||
ciphersuites: DEFAULT_CIPHERSUITES.to_vec(),
|
||||
root_store: verify::RootCertStore::empty(),
|
||||
alpn_protocols: Vec::new(),
|
||||
session_persistence: cell::RefCell::new(Box::new(NoSessionStorage {}))
|
||||
session_persistence: cell::RefCell::new(Box::new(NoSessionStorage {})),
|
||||
mtu: None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,9 +92,16 @@ impl ClientConfig {
|
|||
self.alpn_protocols.extend_from_slice(protocols);
|
||||
}
|
||||
|
||||
/// Sets persistence layer to `persist`.
|
||||
pub fn set_persistence(&mut self, persist: Box<StoresSessions>) {
|
||||
self.session_persistence = cell::RefCell::new(persist);
|
||||
}
|
||||
|
||||
/// Sets MTU to `mtu`. If None, the default is used.
|
||||
/// If Some(x) then x must be greater than 5 bytes.
|
||||
pub fn set_mtu(&mut self, mtu: &Option<usize>) {
|
||||
self.mtu = mtu.clone();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientHandshakeData {
|
||||
|
@ -173,7 +184,7 @@ pub struct ClientSession {
|
|||
pub message_fragmenter: MessageFragmenter,
|
||||
pub sendable_plaintext: Vec<u8>,
|
||||
pub received_plaintext: Vec<u8>,
|
||||
pub tls_queue: VecDeque<Message>,
|
||||
tls_queue: VecDeque<Message>,
|
||||
pub state: ConnState
|
||||
}
|
||||
|
||||
|
@ -191,7 +202,8 @@ impl ClientSession {
|
|||
alpn_protocol: None,
|
||||
message_deframer: MessageDeframer::new(),
|
||||
handshake_joiner: HandshakeJoiner::new(),
|
||||
message_fragmenter: MessageFragmenter::new(MAX_FRAGMENT_LEN),
|
||||
message_fragmenter: MessageFragmenter::new(client_config.mtu
|
||||
.unwrap_or(MAX_FRAGMENT_LEN)),
|
||||
sendable_plaintext: Vec::new(),
|
||||
received_plaintext: Vec::new(),
|
||||
tls_queue: VecDeque::new(),
|
||||
|
@ -264,11 +276,11 @@ impl ClientSession {
|
|||
|
||||
/* Warnings are nonfatal. */
|
||||
if alert.level == AlertLevel::Warning {
|
||||
warn!("TLS alert warning received: {:?}", msg);
|
||||
warn!("TLS alert warning received: {:#?}", msg);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
error!("TLS alert received: {:?}", msg);
|
||||
error!("TLS alert received: {:#?}", msg);
|
||||
return Err(HandshakeError::AlertReceived(alert.description.clone()));
|
||||
} else {
|
||||
unreachable!();
|
||||
|
@ -371,6 +383,17 @@ impl ClientSession {
|
|||
wr.write_all(&data)
|
||||
}
|
||||
|
||||
/// Send a raw TLS message, fragmenting it if needed.
|
||||
pub fn send_msg(&mut self, m: &Message, must_encrypt: bool) {
|
||||
if !must_encrypt {
|
||||
self.message_fragmenter.fragment(m, &mut self.tls_queue);
|
||||
} else {
|
||||
self.send_msg_encrypt(m);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send plaintext application data, fragmenting and
|
||||
/// encrypting it as it goes out.
|
||||
pub fn send_plain(&mut self, data: &[u8]) {
|
||||
use msgs::enums::{ContentType, ProtocolVersion};
|
||||
|
||||
|
@ -391,8 +414,14 @@ impl ClientSession {
|
|||
payload: MessagePayload::opaque(data.to_vec())
|
||||
};
|
||||
|
||||
self.send_msg_encrypt(&m);
|
||||
}
|
||||
|
||||
/// Fragment `m`, encrypt the fragments, and then queue
|
||||
/// the encrypted fragments for sending.
|
||||
pub fn send_msg_encrypt(&mut self, m: &Message) {
|
||||
let mut plain_messages = VecDeque::new();
|
||||
self.message_fragmenter.fragment(&m, &mut plain_messages);
|
||||
self.message_fragmenter.fragment(m, &mut plain_messages);
|
||||
|
||||
for m in plain_messages {
|
||||
let em = self.encrypt_outgoing(&m);
|
||||
|
@ -400,6 +429,8 @@ impl ClientSession {
|
|||
}
|
||||
}
|
||||
|
||||
/// Send any buffered plaintext. Plaintext is buffered if
|
||||
/// written during handshake.
|
||||
pub fn flush_plaintext(&mut self) {
|
||||
if self.state != ConnState::Traffic {
|
||||
return;
|
||||
|
|
|
@ -99,10 +99,10 @@ pub fn emit_client_hello(sess: &mut ClientSession) {
|
|||
)
|
||||
};
|
||||
|
||||
debug!("Sending ClientHello {:?}", sh);
|
||||
debug!("Sending ClientHello {:#?}", sh);
|
||||
|
||||
sh.payload.encode(&mut sess.handshake_data.client_hello);
|
||||
sess.tls_queue.push_back(sh);
|
||||
sess.send_msg(&sh, false);
|
||||
}
|
||||
|
||||
fn expect_server_hello() -> Expectation {
|
||||
|
@ -114,7 +114,7 @@ fn expect_server_hello() -> Expectation {
|
|||
|
||||
fn handle_server_hello(sess: &mut ClientSession, m: &Message) -> Result<ConnState, HandshakeError> {
|
||||
let server_hello = extract_handshake!(m, HandshakePayload::ServerHello).unwrap();
|
||||
debug!("We got ServerHello {:?}", server_hello);
|
||||
debug!("We got ServerHello {:#?}", server_hello);
|
||||
|
||||
if server_hello.server_version != ProtocolVersion::TLSv1_2 {
|
||||
return Err(HandshakeError::General("server does not support TLSv1_2".to_string()));
|
||||
|
@ -265,7 +265,7 @@ fn emit_clientkx(sess: &mut ClientSession, kxd: &suites::KeyExchangeResult) {
|
|||
};
|
||||
|
||||
sess.handshake_data.hash_message(&ckx);
|
||||
sess.tls_queue.push_back(ckx);
|
||||
sess.send_msg(&ckx, false);
|
||||
}
|
||||
|
||||
fn emit_ccs(sess: &mut ClientSession) {
|
||||
|
@ -275,7 +275,7 @@ fn emit_ccs(sess: &mut ClientSession) {
|
|||
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {})
|
||||
};
|
||||
|
||||
sess.tls_queue.push_back(ccs);
|
||||
sess.send_msg(&ccs, false);
|
||||
}
|
||||
|
||||
fn emit_finished(sess: &mut ClientSession) {
|
||||
|
@ -297,8 +297,7 @@ fn emit_finished(sess: &mut ClientSession) {
|
|||
};
|
||||
|
||||
sess.handshake_data.hash_message(&f);
|
||||
let ef = sess.encrypt_outgoing(&f);
|
||||
sess.tls_queue.push_back(ef);
|
||||
sess.send_msg(&f, true);
|
||||
}
|
||||
|
||||
fn handle_server_hello_done(sess: &mut ClientSession, m: &Message) -> Result<ConnState, HandshakeError> {
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct TlsClient {
|
|||
pub suites: Vec<String>,
|
||||
pub protos: Vec<String>,
|
||||
pub verbose: bool,
|
||||
pub mtu: Option<usize>,
|
||||
pub expect_fails: bool,
|
||||
pub expect_output: Vec<String>,
|
||||
pub expect_log: Vec<String>
|
||||
|
@ -32,6 +33,7 @@ impl TlsClient {
|
|||
cafile: None,
|
||||
cache: None,
|
||||
verbose: false,
|
||||
mtu: None,
|
||||
suites: Vec::new(),
|
||||
protos: Vec::new(),
|
||||
expect_fails: false,
|
||||
|
@ -55,6 +57,11 @@ impl TlsClient {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn mtu(&mut self, mtu: usize) -> &mut TlsClient {
|
||||
self.mtu = Some(mtu);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn port(&mut self, port: u16) -> &mut TlsClient {
|
||||
self.port = port;
|
||||
self
|
||||
|
@ -86,6 +93,7 @@ impl TlsClient {
|
|||
}
|
||||
|
||||
pub fn go(&mut self) -> Option<()> {
|
||||
let mut mtustring = "".to_string();
|
||||
let portstring = self.port.to_string();
|
||||
let mut args = Vec::<&str>::new();
|
||||
args.push(&self.hostname);
|
||||
|
@ -121,6 +129,12 @@ impl TlsClient {
|
|||
args.push("--verbose");
|
||||
}
|
||||
|
||||
if self.mtu.is_some() {
|
||||
args.push("--mtu");
|
||||
mtustring = self.mtu.unwrap().to_string();
|
||||
args.push(&mtustring);
|
||||
}
|
||||
|
||||
let output = process::Command::new("target/debug/examples/tlsclient")
|
||||
.args(&args)
|
||||
.output()
|
||||
|
|
|
@ -77,3 +77,25 @@ fn resumption() {
|
|||
.expect("1 session cache hits")
|
||||
.go();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_low_mtu() {
|
||||
let mut server = OpenSSLServer::new_rsa(8300);
|
||||
server.arg("-mtu").arg("32");
|
||||
server.run();
|
||||
|
||||
server.client()
|
||||
.expect("Ciphers common between both SSL end points")
|
||||
.go();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_low_mtu() {
|
||||
let mut server = OpenSSLServer::new_rsa(8400);
|
||||
server.run();
|
||||
|
||||
server.client()
|
||||
.mtu(128)
|
||||
.expect("Ciphers common between both SSL end points")
|
||||
.go();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue