diff --git a/.travis.yml b/.travis.yml index e8bf79e90f53e5ef0103f1a4ceba899e1dbe718d..7df1431a4ce96d9928e610d52a51c294fd6f2195 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,14 @@ rust: os: - osx - linux +env: + global: + - FEATURES="tlsv1_2 tlsv1_1 dtlsv1 dtlsv1_2 sslv2 aes_xts npn alpn" before_install: - (test $TRAVIS_OS_NAME == "osx" || ./openssl/test/build.sh) before_script: - ./openssl/test/test.sh script: -- (cd openssl && LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH cargo test) -- (test $TRAVIS_OS_NAME == "osx" || (cd openssl && LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH cargo test --features "$FEATURES")) +- (test $TRAVIS_OS_NAME != "osx" || (cd openssl && cargo test)) +- (test $TRAVIS_OS_NAME == "osx" || (cd openssl && OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH cargo test)) +- (test $TRAVIS_OS_NAME == "osx" || (cd openssl && OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH cargo test --features "$FEATURES")) diff --git a/README.md b/README.md index f0e15d5772350e6beee15283859c686794a52f38..aafabaee19c0806c4ec40e553f1ffcdc21c10d68 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/sfackler/rust-openssl.svg?branch=master)](https://travis-ci.org/sfackler/rust-openssl) -[Documentation](https://sfackler.github.io/rust-openssl/doc/v0.6.3/openssl). +[Documentation](https://sfackler.github.io/rust-openssl/doc/v0.6.4/openssl). ## Building diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index f3fbfc1823854f886759c707bc7165db34536d65..5ed9d068ef365d4e14d63f845674383343a2de63 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "openssl-sys" -version = "0.6.3" +version = "0.6.4" authors = ["Alex Crichton ", "Steven Fackler "] license = "MIT" description = "FFI bindings to OpenSSL" repository = "https://github.com/sfackler/rust-openssl" -documentation = "https://sfackler.github.io/rust-openssl/doc/v0.6.3/openssl_sys" +documentation = "https://sfackler.github.io/rust-openssl/doc/v0.6.4/openssl_sys" links = "openssl" build = "build.rs" @@ -19,6 +19,7 @@ dtlsv1_2 = [] sslv2 = [] aes_xts = [] npn = [] +alpn = [] [dependencies] libc = "0.1" diff --git a/openssl-sys/build.rs b/openssl-sys/build.rs index de43c462f2c2ee40bf260e68f2e83835f197ca95..c1f1203443ad4e01a458f7ddf38f07dace1cd3b1 100644 --- a/openssl-sys/build.rs +++ b/openssl-sys/build.rs @@ -14,13 +14,16 @@ fn main() { let include_dir = env::var("OPENSSL_INCLUDE_DIR").ok(); if lib_dir.is_none() && include_dir.is_none() { - if let Ok(info) = pkg_config::find_library("openssl") { - build_old_openssl_shim(&info.include_paths); - return; + // rustc doesn't seem to work with pkg-config's output in mingw64 + if !target.contains("windows") { + if let Ok(info) = pkg_config::find_library("openssl") { + build_openssl_shim(&info.include_paths); + return; + } } if let Some(mingw_paths) = get_mingw_in_path() { for path in mingw_paths { - println!("cargo:rustc-flags=-L native={}", path); + println!("cargo:rustc-link-search=native={}", path); } } } @@ -46,11 +49,12 @@ fn main() { }; if let Some(lib_dir) = lib_dir { - println!("cargo:rustc-flags=-L native={}", lib_dir); + println!("cargo:rustc-link-search=native={}", lib_dir); } - let libs_arg = libs.iter().fold(String::new(), |args, lib| args + &format!(" -l {0}={1}", mode, lib)); - println!("cargo:rustc-flags={0}", libs_arg); + for lib in libs { + println!("cargo:rustc-link-lib={}={}", mode, lib); + } let mut include_dirs = vec![]; @@ -58,18 +62,18 @@ fn main() { include_dirs.push(PathBuf::from(&include_dir)); } - build_old_openssl_shim(&include_dirs); + build_openssl_shim(&include_dirs); } -fn build_old_openssl_shim(include_paths: &[PathBuf]) { +fn build_openssl_shim(include_paths: &[PathBuf]) { let mut config = gcc::Config::new(); for path in include_paths { config.include(path); } - config.file("src/old_openssl_shim.c") - .compile("libold_openssl_shim.a"); + config.file("src/openssl_shim.c") + .compile("libopenssl_shim.a"); } fn get_mingw_in_path() -> Option> { diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index d77264bec28c6278ef6a6239667ed9a87b3925c6..eb7750f7a437b1d46b08a69a30dbe3a2d03362fc 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] #![allow(dead_code)] -#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.3")] +#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.4")] extern crate libc; @@ -9,7 +9,6 @@ extern crate libressl_pnacl_sys; use libc::{c_void, c_int, c_char, c_ulong, c_long, c_uint, c_uchar, size_t}; use std::mem; -use std::ptr; use std::sync::{Mutex, MutexGuard}; use std::sync::{Once, ONCE_INIT}; @@ -263,35 +262,6 @@ pub fn init() { } } -// Functions converted from macros -pub unsafe fn BIO_eof(b: *mut BIO) -> bool { - BIO_ctrl(b, BIO_CTRL_EOF, 0, ptr::null_mut()) == 1 -} - -pub unsafe fn SSL_CTX_set_options(ssl: *mut SSL_CTX, op: c_long) -> c_long { - SSL_CTX_ctrl(ssl, SSL_CTRL_OPTIONS, op, ptr::null_mut()) -} - -pub unsafe fn BIO_set_mem_eof_return(b: *mut BIO, v: c_int) { - BIO_ctrl(b, BIO_C_SET_BUF_MEM_EOF_RETURN, v as c_long, ptr::null_mut()); -} - -pub unsafe fn SSL_CTX_get_options(ssl: *mut SSL_CTX) -> c_long { - SSL_CTX_ctrl(ssl, SSL_CTRL_OPTIONS, 0, ptr::null_mut()) -} - -pub unsafe fn SSL_CTX_clear_options(ssl: *mut SSL_CTX, op: c_long) -> c_long { - SSL_CTX_ctrl(ssl, SSL_CTRL_CLEAR_OPTIONS, (op), ptr::null_mut()) -} - -pub unsafe fn SSL_CTX_add_extra_chain_cert(ssl: *mut SSL_CTX, cert: *mut X509) -> c_long { - SSL_CTX_ctrl(ssl, SSL_CTRL_EXTRA_CHAIN_CERT, 0, cert) -} - -pub unsafe fn SSL_CTX_set_read_ahead(ctx: *mut SSL_CTX, m: c_long) -> c_long { - SSL_CTX_ctrl(ctx, SSL_CTRL_SET_READ_AHEAD, m, ptr::null_mut()) -} - // True functions extern "C" { pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; @@ -301,6 +271,7 @@ extern "C" { pub fn BIO_ctrl(b: *mut BIO, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long; pub fn BIO_free_all(b: *mut BIO); pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO; + pub fn BIO_new_socket(sock: c_int, close_flag: c_int) -> *mut BIO; pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int; pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int; pub fn BIO_s_mem() -> *const BIO_METHOD; @@ -445,10 +416,20 @@ extern "C" { pub fn HMAC_CTX_copy(dst: *mut HMAC_CTX, src: *const HMAC_CTX) -> c_int; // Pre-1.0 versions of these didn't return anything, so the shims bridge that gap + #[cfg_attr(not(target_os = "nacl"), link_name = "HMAC_Init_ex_shim")] + pub fn HMAC_Init_ex(ctx: *mut HMAC_CTX, key: *const u8, keylen: c_int, md: *const EVP_MD, imple: *const ENGINE) -> c_int; + #[cfg_attr(not(target_os = "nacl"), link_name = "HMAC_Final_shim")] + pub fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint) -> c_int; + #[cfg_attr(not(target_os = "nacl"), link_name = "HMAC_Update_shim")] + pub fn HMAC_Update(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint) -> c_int; + + /// Deprecated - use the non "_shim" version #[cfg_attr(target_os = "nacl", link_name = "HMAC_Init_ex")] pub fn HMAC_Init_ex_shim(ctx: *mut HMAC_CTX, key: *const u8, keylen: c_int, md: *const EVP_MD, imple: *const ENGINE) -> c_int; + /// Deprecated - use the non "_shim" version #[cfg_attr(target_os = "nacl", link_name = "HMAC_Final")] pub fn HMAC_Final_shim(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint) -> c_int; + /// Deprecated - use the non "_shim" version #[cfg_attr(target_os = "nacl", link_name = "HMAC_Update")] pub fn HMAC_Update_shim(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint) -> c_int; @@ -566,13 +547,31 @@ extern "C" { inlen: c_uint, arg: *mut c_void) -> c_int, arg: *mut c_void); - #[cfg(feature = "npn")] + #[cfg(any(feature = "alpn", feature = "npn"))] pub fn SSL_select_next_proto(out: *mut *mut c_uchar, outlen: *mut c_uchar, inbuf: *const c_uchar, inlen: c_uint, client: *const c_uchar, client_len: c_uint) -> c_int; #[cfg(feature = "npn")] pub fn SSL_get0_next_proto_negotiated(s: *const SSL, data: *mut *const c_uchar, len: *mut c_uint); + #[cfg(feature = "alpn")] + pub fn SSL_CTX_set_alpn_protos(s: *mut SSL_CTX, data: *const c_uchar, len: c_uint) -> c_int; + + #[cfg(feature = "alpn")] + pub fn SSL_set_alpn_protos(s: *mut SSL, data: *const c_uchar, len: c_uint) -> c_int; + + #[cfg(feature = "alpn")] + pub fn SSL_CTX_set_alpn_select_cb(ssl: *mut SSL_CTX, + cb: extern "C" fn(ssl: *mut SSL, + out: *mut *mut c_uchar, + outlen: *mut c_uchar, + inbuf: *const c_uchar, + inlen: c_uint, + arg: *mut c_void) -> c_int, + arg: *mut c_void); + #[cfg(feature = "alpn")] + pub fn SSL_get0_alpn_selected(s: *const SSL, data: *mut *const c_uchar, len: *mut c_uint); + pub fn X509_add_ext(x: *mut X509, ext: *mut X509_EXTENSION, loc: c_int) -> c_int; pub fn X509_digest(x: *mut X509, digest: *const EVP_MD, buf: *mut c_char, len: *mut c_uint) -> c_int; pub fn X509_free(x: *mut X509); @@ -610,6 +609,24 @@ extern "C" { pub fn d2i_RSA_PUBKEY(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA; pub fn i2d_RSAPrivateKey(k: *mut RSA, buf: *const *mut u8) -> c_int; pub fn d2i_RSAPrivateKey(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA; + + // These functions are defined in OpenSSL as macros, so we shim them + #[link_name = "BIO_eof_shim"] + pub fn BIO_eof(b: *mut BIO) -> c_int; + #[link_name = "BIO_set_mem_eof_return_shim"] + pub fn BIO_set_mem_eof_return(b: *mut BIO, v: c_int); + #[link_name = "SSL_CTX_set_options_shim"] + pub fn SSL_CTX_set_options(ctx: *mut SSL_CTX, options: c_long) -> c_long; + #[link_name = "SSL_CTX_get_options_shim"] + pub fn SSL_CTX_get_options(ctx: *mut SSL_CTX) -> c_long; + #[link_name = "SSL_CTX_clear_options_shim"] + pub fn SSL_CTX_clear_options(ctx: *mut SSL_CTX, options: c_long) -> c_long; + #[link_name = "SSL_CTX_add_extra_chain_cert_shim"] + pub fn SSL_CTX_add_extra_chain_cert(ctx: *mut SSL_CTX, x509: *mut X509) -> c_long; + #[link_name = "SSL_CTX_set_read_ahead_shim"] + pub fn SSL_CTX_set_read_ahead(ctx: *mut SSL_CTX, m: c_long) -> c_long; + #[link_name = "SSL_set_tlsext_host_name_shim"] + pub fn SSL_set_tlsext_host_name(s: *mut SSL, name: *const c_char) -> c_long; } pub mod probe; diff --git a/openssl-sys/src/old_openssl_shim.c b/openssl-sys/src/openssl_shim.c similarity index 62% rename from openssl-sys/src/old_openssl_shim.c rename to openssl-sys/src/openssl_shim.c index 19ce74fc0f947e71e8b279d3c245de0da9bad747..7b4f9c74d963abeaba09fbd5d134f53666d09385 100644 --- a/openssl-sys/src/old_openssl_shim.c +++ b/openssl-sys/src/openssl_shim.c @@ -1,4 +1,5 @@ #include +#include #if OPENSSL_VERSION_NUMBER < 0x1000000L // Copied from openssl crypto/hmac/hmac.c @@ -47,3 +48,37 @@ int HMAC_Final_shim(HMAC_CTX *ctx, unsigned char *md, unsigned int *len) { return HMAC_Final(ctx, md, len); } #endif + +// shims for OpenSSL macros + +int BIO_eof_shim(BIO *b) { + return BIO_eof(b); +} + +void BIO_set_mem_eof_return_shim(BIO *b, int v) { + BIO_set_mem_eof_return(b, v); +} + +long SSL_CTX_set_options_shim(SSL_CTX *ctx, long options) { + return SSL_CTX_set_options(ctx, options); +} + +long SSL_CTX_get_options_shim(SSL_CTX *ctx) { + return SSL_CTX_get_options(ctx); +} + +long SSL_CTX_clear_options_shim(SSL_CTX *ctx, long options) { + return SSL_CTX_clear_options(ctx, options); +} + +long SSL_CTX_add_extra_chain_cert_shim(SSL_CTX *ctx, X509 *x509) { + return SSL_CTX_add_extra_chain_cert(ctx, x509); +} + +long SSL_CTX_set_read_ahead_shim(SSL_CTX *ctx, long m) { + return SSL_CTX_set_read_ahead(ctx, m); +} + +long SSL_set_tlsext_host_name_shim(SSL *s, char *name) { + return SSL_set_tlsext_host_name(s, name); +} diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index c20977744353670e5c578b8b6c3e1fe8f47f7b5b..8a6b4ddca61c9a8e121ffaddafe35cbe232ee5d7 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "openssl" -version = "0.6.3" +version = "0.6.4" authors = ["Steven Fackler "] license = "Apache-2.0" description = "OpenSSL bindings" repository = "https://github.com/sfackler/rust-openssl" -documentation = "https://sfackler.github.io/rust-openssl/doc/v0.6.3/openssl" +documentation = "https://sfackler.github.io/rust-openssl/doc/v0.6.4/openssl" readme = "../README.md" keywords = ["crypto", "tls", "ssl", "dtls"] @@ -17,13 +17,14 @@ dtlsv1_2 = ["openssl-sys/dtlsv1_2"] sslv2 = ["openssl-sys/sslv2"] aes_xts = ["openssl-sys/aes_xts"] npn = ["openssl-sys/npn"] +alpn = ["openssl-sys/alpn"] [dependencies.openssl-sys] path = "../openssl-sys" -version = "0.6.3" +version = "0.6.4" [dependencies] -bitflags = "0.2" +bitflags = ">= 0.2, < 0.4" lazy_static = "0.1" libc = "0.1" diff --git a/openssl/src/bio/mod.rs b/openssl/src/bio/mod.rs index e81694a4d9d1038e047fd690de0bcbf06940572c..7eea16d8c5ad27d4a5b5e24dd668bbfe26c07c49 100644 --- a/openssl/src/bio/mod.rs +++ b/openssl/src/bio/mod.rs @@ -73,11 +73,10 @@ impl Read for MemBio { if ret <= 0 { let is_eof = unsafe { ffi::BIO_eof(self.bio) }; - if is_eof { + if is_eof != 0 { Ok(0) } else { - Err(io::Error::new(io::ErrorKind::Other, - SslError::get())) + Err(io::Error::new(io::ErrorKind::Other, SslError::get())) } } else { Ok(ret as usize) @@ -93,8 +92,7 @@ impl Write for MemBio { }; if ret < 0 { - Err(io::Error::new(io::ErrorKind::Other, - SslError::get())) + Err(io::Error::new(io::ErrorKind::Other, SslError::get())) } else { Ok(ret as usize) } diff --git a/openssl/src/crypto/hmac.rs b/openssl/src/crypto/hmac.rs index a59cb929f675d2ea64c5936d9cadf26692f571af..5c9f757699ccdc3da6447428d1ee36c7a75d4d92 100644 --- a/openssl/src/crypto/hmac.rs +++ b/openssl/src/crypto/hmac.rs @@ -88,9 +88,9 @@ impl HMAC { #[inline] fn init_once(&mut self, md: *const ffi::EVP_MD, key: &[u8]) { unsafe { - let r = ffi::HMAC_Init_ex_shim(&mut self.ctx, - key.as_ptr(), key.len() as c_int, - md, 0 as *const _); + let r = ffi::HMAC_Init_ex(&mut self.ctx, + key.as_ptr(), key.len() as c_int, + md, 0 as *const _); assert_eq!(r, 1); } self.state = Reset; @@ -106,9 +106,9 @@ impl HMAC { // If the key and/or md is not supplied it's reused from the last time // avoiding redundant initializations unsafe { - let r = ffi::HMAC_Init_ex_shim(&mut self.ctx, - 0 as *const _, 0, - 0 as *const _, 0 as *const _); + let r = ffi::HMAC_Init_ex(&mut self.ctx, + 0 as *const _, 0, + 0 as *const _, 0 as *const _); assert_eq!(r, 1); } self.state = Reset; @@ -120,7 +120,7 @@ impl HMAC { self.init(); } unsafe { - let r = ffi::HMAC_Update_shim(&mut self.ctx, data.as_ptr(), data.len() as c_uint); + let r = ffi::HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint); assert_eq!(r, 1); } self.state = Updated; @@ -135,7 +135,7 @@ impl HMAC { let mut res: Vec = repeat(0).take(md_len).collect(); unsafe { let mut len = 0; - let r = ffi::HMAC_Final_shim(&mut self.ctx, res.as_mut_ptr(), &mut len); + let r = ffi::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut len); self.state = Finalized; assert_eq!(len as usize, md_len); assert_eq!(r, 1); @@ -181,7 +181,7 @@ impl Drop for HMAC { if self.state != Finalized { let mut buf: Vec = repeat(0).take(self.type_.md_len()).collect(); let mut len = 0; - ffi::HMAC_Final_shim(&mut self.ctx, buf.as_mut_ptr(), &mut len); + ffi::HMAC_Final(&mut self.ctx, buf.as_mut_ptr(), &mut len); } ffi::HMAC_CTX_cleanup(&mut self.ctx); } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 06c514a138ead471bd6101d8d6aa4c9f8e467ed8..62d18dcebc9dd3d028aad568ac958d28d34d2ff7 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.3")] +#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.4")] #[macro_use] extern crate bitflags; diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs index 08424ae85d0b4a2cfe5c9c963fea00f3d81caad8..c5b9e277446df584de229a7244b056145ce21f9d 100644 --- a/openssl/src/nid.rs +++ b/openssl/src/nid.rs @@ -37,6 +37,7 @@ pub enum Nid { DES_EDE, DES_EDE3, IDEA_CBC, + IDEA_CFB, IDEA_ECB, RC2_CBC, RC2_ECB, @@ -55,6 +56,7 @@ pub enum Nid { MessageDigest, SigningTime, CounterSignature, + ChallengePassword, UnstructuredAddress, ExtendedCertificateAttributes, Netscape, @@ -93,6 +95,7 @@ pub enum Nid { AuthorityKeyIdentifier, BF_CBC, BF_ECB, + BF_CFB, BF_OFB, MDC2, RSA_MDC2, @@ -117,7 +120,8 @@ pub enum Nid { RSA_SHA1_2, DSA, RIPEMD160, - RSA_RIPEMD160, + /* 118 missing */ + RSA_RIPEMD160=119, RC5_CBC, RC5_ECB, RC5_CFB, @@ -142,7 +146,6 @@ pub enum Nid { CRLReason, InvalidityDate, SXNetID, - Pkcs12, PBE_SHA1_RC4_128, PBE_SHA1_RC4_40, PBE_SHA1_3DES, diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index a0f97b170b206c71ca333382535deab646d51612..88ba9af440d60d862d73505ca8e56999642c4c29 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -13,9 +13,9 @@ use std::sync::{Once, ONCE_INIT, Arc, Mutex}; use std::ops::{Deref, DerefMut}; use std::cmp; use std::any::Any; -#[cfg(feature = "npn")] +#[cfg(any(feature = "npn", feature = "alpn"))] use libc::{c_uchar, c_uint}; -#[cfg(feature = "npn")] +#[cfg(any(feature = "npn", feature = "alpn"))] use std::slice; use bio::{MemBio}; @@ -170,49 +170,37 @@ lazy_static! { // Registers a destructor for the data which will be called // when context is freed fn get_verify_data_idx() -> c_int { - extern fn free_data_box(_parent: *mut c_void, ptr: *mut c_void, - _ad: *mut ffi::CRYPTO_EX_DATA, _idx: c_int, - _argl: c_long, _argp: *mut c_void) { - if ptr != 0 as *mut _ { - let _: Box = unsafe { mem::transmute(ptr) }; - } - } - *INDEXES.lock().unwrap().entry(TypeId::of::()).or_insert_with(|| { - unsafe { - let f: ffi::CRYPTO_EX_free = free_data_box::; - let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, Some(f)); - assert!(idx >= 0); - idx - } + get_new_idx::() }) } -/// Creates a static index for the list of NPN protocols. -/// Registers a destructor for the data which will be called -/// when the context is freed. #[cfg(feature = "npn")] -fn get_npn_protos_idx() -> c_int { - static mut NPN_PROTOS_IDX: c_int = -1; - static mut INIT: Once = ONCE_INIT; +lazy_static! { + static ref NPN_PROTOS_IDX: c_int = get_new_idx::>(); +} +#[cfg(feature = "alpn")] +lazy_static! { + static ref ALPN_PROTOS_IDX: c_int = get_new_idx::>(); +} - extern fn free_data_box(_parent: *mut c_void, ptr: *mut c_void, +/// Determine a new index to use for SSL CTX ex data. +/// Registers a destruct for the data which will be called by openssl when the context is freed. +fn get_new_idx() -> c_int { + extern fn free_data_box(_parent: *mut c_void, ptr: *mut c_void, _ad: *mut ffi::CRYPTO_EX_DATA, _idx: c_int, _argl: c_long, _argp: *mut c_void) { if !ptr.is_null() { - let _: Box> = unsafe { mem::transmute(ptr) }; + let _: Box = unsafe { mem::transmute(ptr) }; } } unsafe { - INIT.call_once(|| { - let f: ffi::CRYPTO_EX_free = free_data_box; - let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, - None, Some(f)); - assert!(idx >= 0); - NPN_PROTOS_IDX = idx; - }); - NPN_PROTOS_IDX + let f: ffi::CRYPTO_EX_free = free_data_box::; + let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, + None, Some(f)); + assert!(idx >= 0); + idx } } @@ -264,6 +252,26 @@ extern fn raw_verify_with_data(preverify_ok: c_int, } } +#[cfg(any(feature = "npn", feature = "alpn"))] +unsafe fn select_proto_using(ssl: *mut ffi::SSL, + out: *mut *mut c_uchar, outlen: *mut c_uchar, + inbuf: *const c_uchar, inlen: c_uint, + ex_data: c_int) -> c_int { + + // First, get the list of protocols (that the client should support) saved in the context + // extra data. + let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); + let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, ex_data); + let protocols: &Vec = mem::transmute(protocols); + // Prepare the client list parameters to be passed to the OpenSSL function... + let client = protocols.as_ptr(); + let client_len = protocols.len() as c_uint; + // Finally, let OpenSSL find a protocol to be used, by matching the given server and + // client lists. + ffi::SSL_select_next_proto(out, outlen, inbuf, inlen, client, client_len); + ffi::SSL_TLSEXT_ERR_OK +} + /// The function is given as the callback to `SSL_CTX_set_next_proto_select_cb`. /// /// It chooses the protocol that the client wishes to use, out of the given list of protocols @@ -276,20 +284,18 @@ extern fn raw_next_proto_select_cb(ssl: *mut ffi::SSL, inbuf: *const c_uchar, inlen: c_uint, _arg: *mut c_void) -> c_int { unsafe { - // First, get the list of protocols (that the client should support) saved in the context - // extra data. - let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_npn_protos_idx()); - let protocols: &Vec = mem::transmute(protocols); - // Prepare the client list parameters to be passed to the OpenSSL function... - let client = protocols.as_ptr(); - let client_len = protocols.len() as c_uint; - // Finally, let OpenSSL find a protocol to be used, by matching the given server and - // client lists. - ffi::SSL_select_next_proto(out, outlen, inbuf, inlen, client, client_len); + select_proto_using(ssl, out, outlen, inbuf, inlen, *NPN_PROTOS_IDX) } +} - ffi::SSL_TLSEXT_ERR_OK +#[cfg(feature = "alpn")] +extern fn raw_alpn_select_cb(ssl: *mut ffi::SSL, + out: *mut *mut c_uchar, outlen: *mut c_uchar, + inbuf: *const c_uchar, inlen: c_uint, + _arg: *mut c_void) -> c_int { + unsafe { + select_proto_using(ssl, out, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) + } } /// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`. @@ -306,7 +312,7 @@ extern fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL, unsafe { // First, get the list of (supported) protocols saved in the context extra data. let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_npn_protos_idx()); + let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, *NPN_PROTOS_IDX); if protocols.is_null() { *out = b"".as_ptr(); *outlen = 0; @@ -322,6 +328,24 @@ extern fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL, ffi::SSL_TLSEXT_ERR_OK } +/// Convert a set of byte slices into a series of byte strings encoded for SSL. Encoding is a byte +/// containing the length followed by the string. +#[cfg(any(feature = "npn", feature = "alpn"))] +fn ssl_encode_byte_strings(strings: &[&[u8]]) -> Vec +{ + let mut enc = Vec::new(); + for string in strings { + let len = string.len() as u8; + if len as usize != string.len() { + // If the item does not fit, discard it + continue; + } + enc.push(len); + enc.extend(string[..len as usize].to_vec()); + } + enc +} + /// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; @@ -495,7 +519,7 @@ impl SslContext { pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(),SslError> { wrap_ssl_result( unsafe { - let cipher_list = CString::new(cipher_list.as_bytes()).unwrap(); + let cipher_list = CString::new(cipher_list).unwrap(); ffi::SSL_CTX_set_cipher_list(self.ctx, cipher_list.as_ptr()) }) } @@ -531,19 +555,12 @@ impl SslContext { pub fn set_npn_protocols(&mut self, protocols: &[&[u8]]) { // Firstly, convert the list of protocols to a byte-array that can be passed to OpenSSL // APIs -- a list of length-prefixed strings. - let mut npn_protocols = Vec::new(); - for protocol in protocols { - let len = protocol.len() as u8; - npn_protocols.push(len); - // If the length is greater than the max `u8`, this truncates the protocol name. - npn_protocols.extend(protocol[..len as usize].to_vec()); - } - let protocols: Box> = Box::new(npn_protocols); + let protocols: Box> = Box::new(ssl_encode_byte_strings(protocols)); unsafe { // Attach the protocol list to the OpenSSL context structure, // so that we can refer to it within the callback. - ffi::SSL_CTX_set_ex_data(self.ctx, get_npn_protos_idx(), + ffi::SSL_CTX_set_ex_data(self.ctx, *NPN_PROTOS_IDX, mem::transmute(protocols)); // Now register the callback that performs the default protocol // matching based on the client-supported list of protocols that @@ -554,6 +571,35 @@ impl SslContext { ffi::SSL_CTX_set_next_protos_advertised_cb(self.ctx, raw_next_protos_advertise_cb, ptr::null_mut()); } } + + /// Set the protocols to be used during ALPN (application layer protocol negotiation). + /// If this is a server, these are the protocols we report to the client. + /// If this is a client, these are the protocols we try to match with those reported by the + /// server. + /// + /// Note that ordering of the protocols controls the priority with which they are chosen. + /// + /// This method needs the `alpn` feature. + #[cfg(feature = "alpn")] + pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) { + let protocols: Box> = Box::new(ssl_encode_byte_strings(protocols)); + unsafe { + // Set the context's internal protocol list for use if we are a server + ffi::SSL_CTX_set_alpn_protos(self.ctx, protocols.as_ptr(), protocols.len() as c_uint); + + // Rather than use the argument to the callback to contain our data, store it in the + // ssl ctx's ex_data so that we can configure a function to free it later. In the + // future, it might make sense to pull this into our internal struct Ssl instead of + // leaning on openssl and using function pointers. + ffi::SSL_CTX_set_ex_data(self.ctx, *ALPN_PROTOS_IDX, + mem::transmute(protocols)); + + // Now register the callback that performs the default protocol + // matching based on the client-supported list of protocols that + // has been saved. + ffi::SSL_CTX_set_alpn_select_cb(self.ctx, raw_alpn_select_cb, ptr::null_mut()); + } + } } #[allow(dead_code)] @@ -603,11 +649,6 @@ impl Ssl { return Err(SslError::get()); } let ssl = Ssl { ssl: ssl }; - - let rbio = try!(MemBio::new()); - let wbio = try!(MemBio::new()); - - unsafe { ffi::SSL_set_bio(ssl.ssl, rbio.unwrap(), wbio.unwrap()) } Ok(ssl) } @@ -655,16 +696,8 @@ impl Ssl { /// Set the host name to be used with SNI (Server Name Indication). pub fn set_hostname(&self, hostname: &str) -> Result<(), SslError> { - let ret = unsafe { - // This is defined as a macro: - // #define SSL_set_tlsext_host_name(s,name) \ - // SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name) - - let hostname = CString::new(hostname.as_bytes()).unwrap(); - ffi::SSL_ctrl(self.ssl, ffi::SSL_CTRL_SET_TLSEXT_HOSTNAME, - ffi::TLSEXT_NAMETYPE_host_name, - hostname.as_ptr() as *mut c_void) - }; + let cstr = CString::new(hostname).unwrap(); + let ret = unsafe { ffi::SSL_set_tlsext_host_name(self.ssl, cstr.as_ptr()) }; // For this case, 0 indicates failure. if ret == 0 { @@ -708,6 +741,29 @@ impl Ssl { } } + /// Returns the protocol selected by performing ALPN, if any. + /// + /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client + /// to interpret it. + /// + /// This method needs the `alpn` feature. + #[cfg(feature = "alpn")] + pub fn get_selected_alpn_protocol(&self) -> Option<&[u8]> { + unsafe { + let mut data: *const c_uchar = ptr::null(); + let mut len: c_uint = 0; + // Get the negotiated protocol from the SSL instance. + // `data` will point at a `c_uchar` array; `len` will contain the length of this array. + ffi::SSL_get0_alpn_selected(self.ssl, &mut data, &mut len); + + if data.is_null() { + None + } else { + Some(slice::from_raw_parts(data, len as usize)) + } + } + } + /// pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). pub fn pending(&self) -> usize { unsafe { @@ -747,71 +803,395 @@ make_LibSslError! { ErrorWantAccept = SSL_ERROR_WANT_ACCEPT } -/// A stream wrapper which handles SSL encryption for an underlying stream. +struct IndirectStream { + stream: S, + ssl: Arc, + // Max TLS record size is 16k + buf: Box<[u8; 16 * 1024]>, +} + +impl Clone for IndirectStream { + fn clone(&self) -> IndirectStream { + IndirectStream { + stream: self.stream.clone(), + ssl: self.ssl.clone(), + buf: Box::new(*self.buf) + } + } +} + +impl IndirectStream { + fn try_clone(&self) -> io::Result> { + Ok(IndirectStream { + stream: try!(self.stream.try_clone()), + ssl: self.ssl.clone(), + buf: Box::new(*self.buf) + }) + } +} + +impl IndirectStream { + fn new_base(ssl: T, stream: S) -> Result, SslError> { + let ssl = try!(ssl.into_ssl()); + + let rbio = try!(MemBio::new()); + let wbio = try!(MemBio::new()); + + unsafe { ffi::SSL_set_bio(ssl.ssl, rbio.unwrap(), wbio.unwrap()) } + + Ok(IndirectStream { + stream: stream, + ssl: Arc::new(ssl), + buf: Box::new([0; 16 * 1024]), + }) + } + + fn connect(ssl: T, stream: S) -> Result, SslError> { + let mut ssl = try!(IndirectStream::new_base(ssl, stream)); + try!(ssl.in_retry_wrapper(|ssl| ssl.connect())); + Ok(ssl) + } + + fn accept(ssl: T, stream: S) -> Result, SslError> { + let mut ssl = try!(IndirectStream::new_base(ssl, stream)); + try!(ssl.in_retry_wrapper(|ssl| ssl.accept())); + Ok(ssl) + } + + fn in_retry_wrapper(&mut self, mut blk: F) -> Result + where F: FnMut(&Ssl) -> c_int { + loop { + let ret = blk(&self.ssl); + if ret > 0 { + return Ok(ret); + } + + let e = self.ssl.get_error(ret); + match e { + LibSslError::ErrorWantRead => { + try_ssl_stream!(self.flush()); + let len = try_ssl_stream!(self.stream.read(&mut self.buf[..])); + if len == 0 { + self.ssl.get_rbio().set_eof(true); + } else { + try_ssl_stream!(self.ssl.get_rbio().write_all(&self.buf[..len])); + } + } + LibSslError::ErrorWantWrite => { try_ssl_stream!(self.flush()) } + LibSslError::ErrorZeroReturn => return Err(SslSessionClosed), + LibSslError::ErrorSsl => return Err(SslError::get()), + LibSslError::ErrorSyscall if ret == 0 => return Ok(0), + err => panic!("unexpected error {:?} with ret {}", err, ret), + } + } + } + + fn write_through(&mut self) -> io::Result<()> { + io::copy(&mut *self.ssl.get_wbio(), &mut self.stream).map(|_| ()) + } +} + +impl Read for IndirectStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self.in_retry_wrapper(|ssl| { ssl.read(buf) }) { + Ok(len) => Ok(len as usize), + Err(SslSessionClosed) => Ok(0), + Err(StreamError(e)) => Err(e), + Err(e @ OpenSslErrors(_)) => { + Err(io::Error::new(io::ErrorKind::Other, e)) + } + } + } +} + +impl Write for IndirectStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + let count = match self.in_retry_wrapper(|ssl| ssl.write(buf)) { + Ok(len) => len as usize, + Err(SslSessionClosed) => 0, + Err(StreamError(e)) => return Err(e), + Err(e @ OpenSslErrors(_)) => return Err(io::Error::new(io::ErrorKind::Other, e)), + }; + try!(self.write_through()); + Ok(count) + } + + fn flush(&mut self) -> io::Result<()> { + try!(self.write_through()); + self.stream.flush() + } +} + #[derive(Clone)] -pub struct SslStream { +struct DirectStream { stream: S, ssl: Arc, - buf: Vec +} + +impl DirectStream { + fn try_clone(&self) -> io::Result> { + Ok(DirectStream { + stream: try!(self.stream.try_clone()), + ssl: self.ssl.clone(), + }) + } +} + +impl DirectStream { + fn new_base(ssl: Ssl, stream: S, sock: c_int) -> Result, SslError> { + unsafe { + let bio = ffi::BIO_new_socket(sock, 0); + if bio == ptr::null_mut() { + return Err(SslError::get()); + } + ffi::SSL_set_bio(ssl.ssl, bio, bio); + } + + Ok(DirectStream { + stream: stream, + ssl: Arc::new(ssl), + }) + } + + fn connect(ssl: Ssl, stream: S, sock: c_int) -> Result, SslError> { + let ssl = try!(DirectStream::new_base(ssl, stream, sock)); + let ret = ssl.ssl.connect(); + if ret > 0 { + Ok(ssl) + } else { + Err(ssl.make_error(ret)) + } + } + + fn accept(ssl: Ssl, stream: S, sock: c_int) -> Result, SslError> { + let ssl = try!(DirectStream::new_base(ssl, stream, sock)); + let ret = ssl.ssl.accept(); + if ret > 0 { + Ok(ssl) + } else { + Err(ssl.make_error(ret)) + } + } + + fn make_error(&self, ret: c_int) -> SslError { + match self.ssl.get_error(ret) { + LibSslError::ErrorSsl => SslError::get(), + LibSslError::ErrorSyscall => { + let err = SslError::get(); + let count = match err { + SslError::OpenSslErrors(ref v) => v.len(), + _ => unreachable!(), + }; + if count == 0 { + if ret == 0 { + SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted, + "unexpected EOF observed")) + } else { + SslError::StreamError(io::Error::last_os_error()) + } + } else { + err + } + } + err => panic!("unexpected error {:?} with ret {}", err, ret), + } + } +} + +impl Read for DirectStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let ret = self.ssl.read(buf); + if ret >= 0 { + return Ok(ret as usize); + } + + match self.make_error(ret) { + SslError::StreamError(e) => Err(e), + e => Err(io::Error::new(io::ErrorKind::Other, e)), + } + } +} + +impl Write for DirectStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + let ret = self.ssl.write(buf); + if ret > 0 { + return Ok(ret as usize); + } + + match self.make_error(ret) { + SslError::StreamError(e) => Err(e), + e => Err(io::Error::new(io::ErrorKind::Other, e)), + } + } + + fn flush(&mut self) -> io::Result<()> { + self.stream.flush() + } +} + +#[derive(Clone)] +enum StreamKind { + Indirect(IndirectStream), + Direct(DirectStream), +} + +impl StreamKind { + fn stream(&self) -> &S { + match *self { + StreamKind::Indirect(ref s) => &s.stream, + StreamKind::Direct(ref s) => &s.stream, + } + } + + fn mut_stream(&mut self) -> &mut S { + match *self { + StreamKind::Indirect(ref mut s) => &mut s.stream, + StreamKind::Direct(ref mut s) => &mut s.stream, + } + } + + fn ssl(&self) -> &Ssl { + match *self { + StreamKind::Indirect(ref s) => &s.ssl, + StreamKind::Direct(ref s) => &s.ssl, + } + } +} + +/// A stream wrapper which handles SSL encryption for an underlying stream. +#[derive(Clone)] +pub struct SslStream { + kind: StreamKind, } impl SslStream { /// Create a new independently owned handle to the underlying socket. pub fn try_clone(&self) -> io::Result> { + let kind = match self.kind { + StreamKind::Indirect(ref s) => StreamKind::Indirect(try!(s.try_clone())), + StreamKind::Direct(ref s) => StreamKind::Direct(try!(s.try_clone())) + }; Ok(SslStream { - stream: try!(self.stream.try_clone()), - ssl: self.ssl.clone(), - buf: self.buf.clone(), + kind: kind }) } } impl fmt::Debug for SslStream where S: fmt::Debug { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "SslStream {{ stream: {:?}, ssl: {:?} }}", self.stream, self.ssl) + write!(fmt, "SslStream {{ stream: {:?}, ssl: {:?} }}", self.kind.stream(), self.kind.ssl()) + } +} + +#[cfg(unix)] +impl SslStream { + /// Creates an SSL/TLS client operating over the provided stream. + /// + /// Streams passed to this method must implement `AsRawFd` on Unixy + /// platforms and `AsRawSocket` on Windows. Use `connect_generic` for + /// streams that do not. + pub fn connect(ssl: T, stream: S) -> Result, SslError> { + let ssl = try!(ssl.into_ssl()); + let fd = stream.as_raw_fd() as c_int; + let stream = try!(DirectStream::connect(ssl, stream, fd)); + Ok(SslStream { + kind: StreamKind::Direct(stream) + }) + } + + /// Creates an SSL/TLS server operating over the provided stream. + /// + /// Streams passed to this method must implement `AsRawFd` on Unixy + /// platforms and `AsRawSocket` on Windows. Use `accept_generic` for + /// streams that do not. + pub fn accept(ssl: T, stream: S) -> Result, SslError> { + let ssl = try!(ssl.into_ssl()); + let fd = stream.as_raw_fd() as c_int; + let stream = try!(DirectStream::accept(ssl, stream, fd)); + Ok(SslStream { + kind: StreamKind::Direct(stream) + }) + } +} + +#[cfg(windows)] +impl SslStream { + /// Creates an SSL/TLS client operating over the provided stream. + /// + /// Streams passed to this method must implement `AsRawFd` on Unixy + /// platforms and `AsRawSocket` on Windows. Use `connect_generic` for + /// streams that do not. + pub fn connect(ssl: T, stream: S) -> Result, SslError> { + let ssl = try!(ssl.into_ssl()); + let fd = stream.as_raw_socket() as c_int; + let stream = try!(DirectStream::connect(ssl, stream, fd)); + Ok(SslStream { + kind: StreamKind::Direct(stream) + }) + } + + /// Creates an SSL/TLS server operating over the provided stream. + /// + /// Streams passed to this method must implement `AsRawFd` on Unixy + /// platforms and `AsRawSocket` on Windows. Use `accept_generic` for + /// streams that do not. + pub fn accept(ssl: T, stream: S) -> Result, SslError> { + let ssl = try!(ssl.into_ssl()); + let fd = stream.as_raw_socket() as c_int; + let stream = try!(DirectStream::accept(ssl, stream, fd)); + Ok(SslStream { + kind: StreamKind::Direct(stream) + }) } } impl SslStream { - fn new_base(ssl:Ssl, stream: S) -> SslStream { - SslStream { - stream: stream, - ssl: Arc::new(ssl), - // Maximum TLS record size is 16k - // We're just using this as a buffer, so there's no reason to pay - // to memset it - buf: { - const CAP: usize = 16 * 1024; - let mut v = Vec::with_capacity(CAP); - unsafe { v.set_len(CAP); } - v - } - } + /// Creates an SSL/TLS client operating over the provided stream. + /// + /// `SslStream`s returned by this method will be less efficient than ones + /// returned by `connect`, so this method should only be used for streams + /// that do not implement `AsRawFd` and `AsRawSocket`. + pub fn connect_generic(ssl: T, stream: S) -> Result, SslError> { + let stream = try!(IndirectStream::connect(ssl, stream)); + Ok(SslStream { + kind: StreamKind::Indirect(stream) + }) } + /// Creates an SSL/TLS server operating over the provided stream. + /// + /// `SslStream`s returned by this method will be less efficient than ones + /// returned by `accept`, so this method should only be used for streams + /// that do not implement `AsRawFd` and `AsRawSocket`. + pub fn accept_generic(ssl: T, stream: S) -> Result, SslError> { + let stream = try!(IndirectStream::accept(ssl, stream)); + Ok(SslStream { + kind: StreamKind::Indirect(stream) + }) + } + + /// # Deprecated + pub fn new_server(ssl: &SslContext, stream: S) -> Result, SslError> { + SslStream::accept_generic(ssl, stream) + } + + /// # Deprecated pub fn new_server_from(ssl: Ssl, stream: S) -> Result, SslError> { - let mut ssl = SslStream::new_base(ssl, stream); - ssl.in_retry_wrapper(|ssl| { ssl.accept() }).and(Ok(ssl)) + SslStream::accept_generic(ssl, stream) } - /// Attempts to create a new SSL stream from a given `Ssl` instance. + /// # Deprecated pub fn new_from(ssl: Ssl, stream: S) -> Result, SslError> { - let mut ssl = SslStream::new_base(ssl, stream); - ssl.in_retry_wrapper(|ssl| { ssl.connect() }).and(Ok(ssl)) + SslStream::connect_generic(ssl, stream) } - /// Creates a new SSL stream + /// # Deprecated pub fn new(ctx: &SslContext, stream: S) -> Result, SslError> { - let ssl = try!(Ssl::new(ctx)); - SslStream::new_from(ssl, stream) - } - - /// Creates a new SSL server stream - pub fn new_server(ctx: &SslContext, stream: S) -> Result, SslError> { - let ssl = try!(Ssl::new(ctx)); - SslStream::new_server_from(ssl, stream) + SslStream::connect_generic(ctx, stream) } + /// # Deprecated #[doc(hidden)] pub fn get_inner(&mut self) -> &mut S { self.get_mut() @@ -819,12 +1199,12 @@ impl SslStream { /// Returns a reference to the underlying stream. pub fn get_ref(&self) -> &S { - &self.stream + self.kind.stream() } /// Return the certificate of the peer pub fn get_peer_certificate(&self) -> Option { - self.ssl.get_peer_certificate() + self.kind.ssl().get_peer_certificate() } /// Returns a mutable reference to the underlying stream. @@ -832,48 +1212,16 @@ impl SslStream { /// ## Warning /// /// It is inadvisable to read from or write to the underlying stream as it - /// will most likely desynchronize the SSL session. + /// will most likely corrupt the SSL session. pub fn get_mut(&mut self) -> &mut S { - &mut self.stream - } - - fn in_retry_wrapper(&mut self, mut blk: F) - -> Result where F: FnMut(&Ssl) -> c_int { - loop { - let ret = blk(&self.ssl); - if ret > 0 { - return Ok(ret); - } - - let e = self.ssl.get_error(ret); - match e { - LibSslError::ErrorWantRead => { - try_ssl_stream!(self.flush()); - let len = try_ssl_stream!(self.stream.read(&mut self.buf[..])); - if len == 0 { - self.ssl.get_rbio().set_eof(true); - } else { - try_ssl_stream!(self.ssl.get_rbio().write_all(&self.buf[..len])); - } - } - LibSslError::ErrorWantWrite => { try_ssl_stream!(self.flush()) } - LibSslError::ErrorZeroReturn => return Err(SslSessionClosed), - LibSslError::ErrorSsl => return Err(SslError::get()), - LibSslError::ErrorSyscall if ret == 0 => return Ok(0), - err => panic!("unexpected error {:?} with ret {}", err, ret), - } - } - } - - fn write_through(&mut self) -> io::Result<()> { - io::copy(&mut *self.ssl.get_wbio(), &mut self.stream).map(|_| ()) + self.kind.mut_stream() } /// Get the compression currently in use. The result will be /// either None, indicating no compression is in use, or a string /// with the compression name. pub fn get_compression(&self) -> Option { - let ptr = unsafe { ffi::SSL_get_current_compression(self.ssl.ssl) }; + let ptr = unsafe { ffi::SSL_get_current_compression(self.kind.ssl().ssl) }; if ptr == ptr::null() { return None; } @@ -894,43 +1242,64 @@ impl SslStream { /// This method needs the `npn` feature. #[cfg(feature = "npn")] pub fn get_selected_npn_protocol(&self) -> Option<&[u8]> { - self.ssl.get_selected_npn_protocol() + self.kind.ssl().get_selected_npn_protocol() + } + + /// Returns the protocol selected by performing ALPN, if any. + /// + /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client + /// to interpret it. + /// + /// This method needs the `alpn` feature. + #[cfg(feature = "alpn")] + pub fn get_selected_alpn_protocol(&self) -> Option<&[u8]> { + self.kind.ssl().get_selected_alpn_protocol() } /// pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). pub fn pending(&self) -> usize { - self.ssl.pending() + self.kind.ssl().pending() } } impl Read for SslStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self.in_retry_wrapper(|ssl| { ssl.read(buf) }) { - Ok(len) => Ok(len as usize), - Err(SslSessionClosed) => Ok(0), - Err(StreamError(e)) => Err(e), - Err(e @ OpenSslErrors(_)) => { - Err(io::Error::new(io::ErrorKind::Other, e)) - } + match self.kind { + StreamKind::Indirect(ref mut s) => s.read(buf), + StreamKind::Direct(ref mut s) => s.read(buf), } } } impl Write for SslStream { fn write(&mut self, buf: &[u8]) -> io::Result { - let count = match self.in_retry_wrapper(|ssl| ssl.write(buf)) { - Ok(len) => len as usize, - Err(SslSessionClosed) => 0, - Err(StreamError(e)) => return Err(e), - Err(e @ OpenSslErrors(_)) => return Err(io::Error::new(io::ErrorKind::Other, e)), - }; - try!(self.write_through()); - Ok(count) + match self.kind { + StreamKind::Indirect(ref mut s) => s.write(buf), + StreamKind::Direct(ref mut s) => s.write(buf), + } } fn flush(&mut self) -> io::Result<()> { - try!(self.write_through()); - self.stream.flush() + match self.kind { + StreamKind::Indirect(ref mut s) => s.flush(), + StreamKind::Direct(ref mut s) => s.flush(), + } + } +} + +pub trait IntoSsl { + fn into_ssl(self) -> Result; +} + +impl IntoSsl for Ssl { + fn into_ssl(self) -> Result { + Ok(self) + } +} + +impl<'a> IntoSsl for &'a SslContext { + fn into_ssl(self) -> Result { + Ssl::new(self) } } diff --git a/openssl/src/ssl/tests.rs b/openssl/src/ssl/tests.rs index dcaee21564a18f15a9acabe12a8a6c3c03b54681..8401836d74e4c4116db27f7c0e077965dbf00f5d 100644 --- a/openssl/src/ssl/tests.rs +++ b/openssl/src/ssl/tests.rs @@ -83,14 +83,14 @@ run_test!(new_ctx, |method, _| { }); run_test!(new_sslstream, |method, stream| { - SslStream::new(&SslContext::new(method).unwrap(), stream).unwrap(); + SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); }); run_test!(verify_untrusted, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); ctx.set_verify(SSL_VERIFY_PEER, None); - match SslStream::new(&ctx, stream) { + match SslStream::connect_generic(&ctx, stream) { Ok(_) => panic!("expected failure"), Err(err) => println!("error {:?}", err) } @@ -104,7 +104,7 @@ run_test!(verify_trusted, |method, stream| { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - match SslStream::new(&ctx, stream) { + match SslStream::connect_generic(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err) } @@ -118,7 +118,7 @@ run_test!(verify_untrusted_callback_override_ok, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - match SslStream::new(&ctx, stream) { + match SslStream::connect_generic(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err) } @@ -132,7 +132,7 @@ run_test!(verify_untrusted_callback_override_bad, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_err()); + assert!(SslStream::connect_generic(&ctx, stream).is_err()); }); run_test!(verify_trusted_callback_override_ok, |method, stream| { @@ -147,7 +147,7 @@ run_test!(verify_trusted_callback_override_ok, |method, stream| { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - match SslStream::new(&ctx, stream) { + match SslStream::connect_generic(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err) } @@ -165,7 +165,7 @@ run_test!(verify_trusted_callback_override_bad, |method, stream| { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - assert!(SslStream::new(&ctx, stream).is_err()); + assert!(SslStream::connect_generic(&ctx, stream).is_err()); }); run_test!(verify_callback_load_certs, |method, stream| { @@ -177,7 +177,7 @@ run_test!(verify_callback_load_certs, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_ok()); + assert!(SslStream::connect_generic(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_ok, |method, stream| { @@ -193,7 +193,7 @@ run_test!(verify_trusted_get_error_ok, |method, stream| { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - assert!(SslStream::new(&ctx, stream).is_ok()); + assert!(SslStream::connect_generic(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_err, |method, stream| { @@ -205,7 +205,7 @@ run_test!(verify_trusted_get_error_err, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_err()); + assert!(SslStream::connect_generic(&ctx, stream).is_err()); }); run_test!(verify_callback_data, |method, stream| { @@ -230,7 +230,7 @@ run_test!(verify_callback_data, |method, stream| { ctx.set_verify_with_data(SSL_VERIFY_PEER, callback, node_id); ctx.set_verify_depth(1); - match SslStream::new(&ctx, stream) { + match SslStream::connect_generic(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err) } @@ -245,7 +245,7 @@ fn test_write_hits_stream() { let guard = thread::spawn(move || { let ctx = SslContext::new(Sslv23).unwrap(); let stream = TcpStream::connect(addr).unwrap(); - let mut stream = SslStream::new(&ctx, stream).unwrap(); + let mut stream = SslStream::connect_generic(&ctx, stream).unwrap(); stream.write_all(b"hello").unwrap(); stream @@ -256,7 +256,7 @@ fn test_write_hits_stream() { ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM).unwrap(); ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM).unwrap(); let stream = listener.accept().unwrap().0; - let mut stream = SslStream::new_server(&ctx, stream).unwrap(); + let mut stream = SslStream::accept(&ctx, stream).unwrap(); let mut buf = [0; 5]; assert_eq!(5, stream.read(&mut buf).unwrap()); @@ -310,7 +310,17 @@ run_test!(clear_ctx_options, |method, _| { #[test] fn test_write() { let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + stream.write_all("hello".as_bytes()).unwrap(); + stream.flush().unwrap(); + stream.write_all(" there".as_bytes()).unwrap(); + stream.flush().unwrap(); +} + +#[test] +fn test_write_direct() { + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); stream.write_all("hello".as_bytes()).unwrap(); stream.flush().unwrap(); stream.write_all(" there".as_bytes()).unwrap(); @@ -318,8 +328,7 @@ fn test_write() { } run_test!(get_peer_certificate, |method, stream| { - //let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let stream = SslStream::new(&SslContext::new(method).unwrap(), stream).unwrap(); + let stream = SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); let cert = stream.get_peer_certificate().unwrap(); let fingerprint = cert.fingerprint(SHA256).unwrap(); let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b"; @@ -333,7 +342,7 @@ fn test_write_dtlsv1() { let sock = UdpSocket::bind("127.0.0.1:0").unwrap(); let stream = sock.connect("127.0.0.1:15410").unwrap(); - let mut stream = SslStream::new(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); stream.write_all("hello".as_bytes()).unwrap(); stream.flush().unwrap(); stream.write_all(" there".as_bytes()).unwrap(); @@ -343,17 +352,25 @@ fn test_write_dtlsv1() { #[test] fn test_read() { let tcp = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); io::copy(&mut stream, &mut io::sink()).ok().expect("read error"); } +#[test] +fn test_read_direct() { + let tcp = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); + stream.flush().unwrap(); + io::copy(&mut stream, &mut io::sink()).ok().expect("read error"); +} #[test] fn test_pending() { let tcp = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); @@ -373,6 +390,28 @@ fn test_pending() { assert_eq!(pending, len); } +/// Tests that connecting with the client using NPN, but the server not does not +/// break the existing connection behavior. +#[test] +#[cfg(feature = "alpn")] +fn test_connect_with_unilateral_alpn() { + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err) + } + let stream = match SslStream::new(&ctx, stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err) + }; + // Since the socket to which we connected is not configured to use NPN, + // there should be no selected protocol... + assert!(stream.get_selected_alpn_protocol().is_none()); +} + /// Tests that connecting with the client using NPN, but the server not does not /// break the existing connection behavior. #[test] @@ -386,7 +425,7 @@ fn test_connect_with_unilateral_npn() { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - let stream = match SslStream::new(&ctx, stream) { + let stream = match SslStream::connect_generic(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err) }; @@ -395,6 +434,30 @@ fn test_connect_with_unilateral_npn() { assert!(stream.get_selected_npn_protocol().is_none()); } +/// Tests that when both the client as well as the server use ALPN and their +/// lists of supported protocols have an overlap, the correct protocol is chosen. +#[test] +#[cfg(feature = "alpn")] +fn test_connect_with_alpn_successful_multiple_matching() { + // A different port than the other tests: an `openssl` process that has + // NPN enabled. + let stream = TcpStream::connect("127.0.0.1:15419").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err) + } + let stream = match SslStream::new(&ctx, stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err) + }; + // The server prefers "http/1.1", so that is chosen, even though the client + // would prefer "spdy/3.1" + assert_eq!(b"http/1.1", stream.get_selected_alpn_protocol().unwrap()); +} + /// Tests that when both the client as well as the server use NPN and their /// lists of supported protocols have an overlap, the correct protocol is chosen. #[test] @@ -410,7 +473,7 @@ fn test_connect_with_npn_successful_multiple_matching() { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - let stream = match SslStream::new(&ctx, stream) { + let stream = match SslStream::connect_generic(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err) }; @@ -419,6 +482,32 @@ fn test_connect_with_npn_successful_multiple_matching() { assert_eq!(b"http/1.1", stream.get_selected_npn_protocol().unwrap()); } +/// Tests that when both the client as well as the server use ALPN and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +#[cfg(feature = "alpn")] +fn test_connect_with_alpn_successful_single_match() { + // A different port than the other tests: an `openssl` process that has + // ALPN enabled. + let stream = TcpStream::connect("127.0.0.1:15419").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_alpn_protocols(&[b"spdy/3.1"]); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err) + } + let stream = match SslStream::new(&ctx, stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err) + }; + // The client now only supports one of the server's protocols, so that one + // is used. + assert_eq!(b"spdy/3.1", stream.get_selected_alpn_protocol().unwrap()); +} + + /// Tests that when both the client as well as the server use NPN and their /// lists of supported protocols have an overlap -- with only ONE protocol /// being valid for both. @@ -435,7 +524,7 @@ fn test_connect_with_npn_successful_single_match() { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err) } - let stream = match SslStream::new(&ctx, stream) { + let stream = match SslStream::connect_generic(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err) }; @@ -465,7 +554,7 @@ fn test_npn_server_advertise_multiple() { // Have the listener wait on the connection in a different thread. thread::spawn(move || { let (stream, _) = listener.accept().unwrap(); - let _ = SslStream::new_server(&listener_ctx, stream).unwrap(); + let _ = SslStream::accept(&listener_ctx, stream).unwrap(); }); let mut ctx = SslContext::new(Sslv23).unwrap(); @@ -477,7 +566,7 @@ fn test_npn_server_advertise_multiple() { } // Now connect to the socket and make sure the protocol negotiation works... let stream = TcpStream::connect(localhost).unwrap(); - let stream = match SslStream::new(&ctx, stream) { + let stream = match SslStream::connect_generic(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err) }; @@ -485,6 +574,47 @@ fn test_npn_server_advertise_multiple() { assert_eq!(b"spdy/3.1", stream.get_selected_npn_protocol().unwrap()); } +/// Tests that when the `SslStream` is created as a server stream, the protocols +/// are correctly advertised to the client. +#[test] +#[cfg(feature = "alpn")] +fn test_alpn_server_advertise_multiple() { + let localhost = "127.0.0.1:15421"; + let listener = TcpListener::bind(localhost).unwrap(); + // We create a different context instance for the server... + let listener_ctx = { + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); + assert!(ctx.set_certificate_file( + &Path::new("test/cert.pem"), X509FileType::PEM).is_ok()); + ctx.set_private_key_file( + &Path::new("test/key.pem"), X509FileType::PEM).unwrap(); + ctx + }; + // Have the listener wait on the connection in a different thread. + thread::spawn(move || { + let (stream, _) = listener.accept().unwrap(); + let _ = SslStream::accept(&listener_ctx, stream).unwrap(); + }); + + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_alpn_protocols(&[b"spdy/3.1"]); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err) + } + // Now connect to the socket and make sure the protocol negotiation works... + let stream = TcpStream::connect(localhost).unwrap(); + let stream = match SslStream::new(&ctx, stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err) + }; + // SPDY is selected since that's the only thing the client supports. + assert_eq!(b"spdy/3.1", stream.get_selected_alpn_protocol().unwrap()); +} + #[cfg(feature="dtlsv1")] #[cfg(test)] mod dtlsv1 { @@ -514,7 +644,7 @@ fn test_read_dtlsv1() { let server = udp::next_server(); let stream = sock.connect(&server[..]).unwrap(); - let mut stream = SslStream::new(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); let mut buf = [0u8;100]; assert!(stream.read(&mut buf).is_ok()); } @@ -523,5 +653,5 @@ fn test_read_dtlsv1() { #[cfg(feature = "sslv2")] fn test_sslv2_connect_failure() { let tcp = TcpStream::connect("127.0.0.1:15420").unwrap(); - SslStream::new(&SslContext::new(Sslv2).unwrap(), tcp).err().unwrap(); + SslStream::connect_generic(&SslContext::new(Sslv2).unwrap(), tcp).err().unwrap(); } diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 6d95b9666c06dccc15351f94bdbfaaa3d7b29a0b..4e1c4f15728a9ef6427281ebd3d55fab87426db6 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -69,3 +69,32 @@ fn test_subject_read_cn() { assert_eq!(&cn as &str, "test_cert") } + +#[test] +fn test_nid_values() { + let cert_path = Path::new("test/nid_test_cert.pem"); + let mut file = File::open(&cert_path) + .ok() + .expect("Failed to open `test/nid_test_cert.pem`"); + + let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let subject = cert.subject_name(); + + let cn = match subject.text_by_nid(Nid::CN) { + Some(x) => x, + None => panic!("Failed to read CN from cert") + }; + assert_eq!(&cn as &str, "example.com"); + + let email = match subject.text_by_nid(Nid::Email) { + Some(x) => x, + None => panic!("Failed to read subject email address from cert") + }; + assert_eq!(&email as &str, "test@example.com"); + + let friendly = match subject.text_by_nid(Nid::FriendlyName) { + Some(x) => x, + None => panic!("Failed to read subject friendly name from cert") + }; + assert_eq!(&friendly as &str, "Example"); +} diff --git a/openssl/test/nid_test_cert.pem b/openssl/test/nid_test_cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..6f17e734cde30b7d5e869cd91f17e13033340202 --- /dev/null +++ b/openssl/test/nid_test_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1DCCAX6gAwIBAgIJAMzXWZGWHleWMA0GCSqGSIb3DQEBCwUAMFYxHzAdBgkq +hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29t +MR0wGwYJKoZIhvcNAQkUHg4ARQB4AGEAbQBwAGwAZTAeFw0xNTA3MDEwNjQ3NDRa +Fw0xNTA3MzEwNjQ3NDRaMFYxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5j +b20xFDASBgNVBAMMC2V4YW1wbGUuY29tMR0wGwYJKoZIhvcNAQkUHg4ARQB4AGEA +bQBwAGwAZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCmejzp4+o35FD0hAnx2trL +08h07X5jZca9DgZH35hWXPh7fMucLt/IPXIRnz2zKEa/Mo6D2V/fx03Mqo0epid7 +AgMBAAGjLzAtMB0GA1UdDgQWBBRQa57tXz3rZNRz+fTbo3w3jQJMBTAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAm0iY9cr+gvC+vcQIebdofpQ4GcDW8U6W +Bxs8ZXinLl69P0jYLum3+XITNFRiyQqcivaxdxthxDNOX7P+aKwkJA== +-----END CERTIFICATE----- diff --git a/openssl/test/test.sh b/openssl/test/test.sh index 975b124e83400d8b6d6d2290f566c75561545dc7..9beb37c38b8653e710ff7a3bb7141ff0c0209648 100755 --- a/openssl/test/test.sh +++ b/openssl/test/test.sh @@ -4,7 +4,7 @@ cd $(dirname $0) openssl s_server -accept 15418 -www -cert cert.pem -key key.pem >/dev/null 2>&1 & openssl s_server -accept 15419 -www -cert cert.pem -key key.pem \ - -nextprotoneg "http/1.1,spdy/3.1" >/dev/null 2>&1 & + -nextprotoneg "http/1.1,spdy/3.1" -alpn "http/1.1,spdy/3.1" >/dev/null 2>&1 & openssl s_server -no_ssl2 -accept 15420 -www -cert cert.pem -key key.pem >/dev/null 2>&1 & if test "$TRAVIS_OS_NAME" == "osx"; then