Commit 1fe16382 authored by Steven Fackler's avatar Steven Fackler Committed by GitHub
Browse files

Merge pull request #472 from sfackler/alpn-test

Fix test_alpn_server_select_none
parents 3ea2a9cb 7ac05996
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -318,6 +318,18 @@ pub unsafe fn SSL_set_tlsext_host_name(s: *mut SSL, name: *mut c_char) -> c_long
             name as *mut c_void)
}

pub fn ERR_GET_LIB(l: c_ulong) -> c_int {
    ((l >> 24) & 0x0FF) as c_int
}

pub fn ERR_GET_FUNC(l: c_ulong) -> c_int {
    ((l >> 12) & 0xFFF) as c_int
}

pub fn ERR_GET_REASON(l: c_ulong) -> c_int {
    (l & 0xFFF) as c_int
}

extern {
    pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int;
    pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING;
+54 −42
Original line number Diff line number Diff line
@@ -76,72 +76,84 @@ impl Error {
    }

    /// Returns the raw OpenSSL error code for this error.
    pub fn error_code(&self) -> c_ulong {
    pub fn code(&self) -> c_ulong {
        self.0
    }

    /// Returns the name of the library reporting the error.
    pub fn library(&self) -> &'static str {
        get_lib(self.0)
    /// Returns the name of the library reporting the error, if available.
    pub fn library(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_lib_error_string(self.0);
            if cstr.is_null() {
                return None;
            }
            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
            Some(str::from_utf8(bytes).unwrap())
        }
    }

    /// Returns the name of the function reporting the error.
    pub fn function(&self) -> &'static str {
        get_func(self.0)
    pub fn function(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_func_error_string(self.0);
            if cstr.is_null() {
                return None;
            }
            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
            Some(str::from_utf8(bytes).unwrap())
        }
    }

    /// Returns the reason for the error.
    pub fn reason(&self) -> &'static str {
        get_reason(self.0)
    pub fn reason(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_reason_error_string(self.0);
            if cstr.is_null() {
                return None;
            }
            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
            Some(str::from_utf8(bytes).unwrap())
        }

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Error")
           .field("library", &self.library())
           .field("function", &self.function())
           .field("reason", &self.reason())
           .finish()
    }
}

impl fmt::Display for Error {
impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.reason())
        let mut builder = fmt.debug_struct("Error");
        builder.field("code", &self.code());
        if let Some(library) = self.library() {
            builder.field("library", &library);
        }
        if let Some(function) = self.function() {
            builder.field("function", &function);
        }

impl error::Error for Error {
    fn description(&self) -> &str {
        "An OpenSSL error"
        if let Some(reason) = self.reason() {
            builder.field("reason", &reason);
        }
        builder.finish()
    }
}

fn get_lib(err: c_ulong) -> &'static str {
    unsafe {
        let cstr = ffi::ERR_lib_error_string(err);
        assert!(!cstr.is_null(), "bad lib: {}", err);
        let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
        str::from_utf8(bytes).unwrap()
impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "error:{:08X}", self.0));
        match self.library() {
            Some(l) => try!(write!(fmt, ":{}", l)),
            None => try!(write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.0))),
        }
        match self.function() {
            Some(f) => try!(write!(fmt, ":{}", f)),
            None => try!(write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.0))),
        }
        match self.reason() {
            Some(r) => write!(fmt, ":{}", r),
            None => write!(fmt, ":reason({})", ffi::ERR_GET_FUNC(self.0)),
        }

fn get_func(err: c_ulong) -> &'static str {
    unsafe {
        let cstr = ffi::ERR_func_error_string(err);
        assert!(!cstr.is_null(), "bad func: {}", err);
        let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
        str::from_utf8(bytes).unwrap()
    }
}

fn get_reason(err: c_ulong) -> &'static str {
    unsafe {
        let cstr = ffi::ERR_reason_error_string(err);
        assert!(!cstr.is_null(), "bad reason: {}", err);
        let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
        str::from_utf8(bytes).unwrap()
impl error::Error for Error {
    fn description(&self) -> &str {
        "An OpenSSL error"
    }
}
+34 −12
Original line number Diff line number Diff line
@@ -726,10 +726,7 @@ fn test_alpn_server_advertise_multiple() {
/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match
/// the client's reported protocol.
#[test]
#[cfg(feature = "openssl-102")]
// TODO: not sure why this test is failing on OpenSSL 1.1.0, may be related to
//       something about SSLv3 though?
#[cfg_attr(ossl110, ignore)]
#[cfg(all(feature = "openssl-102", ossl102))]
fn test_alpn_server_select_none() {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let localhost = listener.local_addr().unwrap();
@@ -753,21 +750,46 @@ fn test_alpn_server_select_none() {
    let mut ctx = SslContext::new(Tls).unwrap();
    ctx.set_verify(SSL_VERIFY_PEER);
    ctx.set_alpn_protocols(&[b"http/2"]);
    match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
        Ok(_) => {}
        Err(err) => panic!("Unexpected error {:?}", err),
    }
    ctx.set_CA_file(&Path::new("test/root-ca.pem")).unwrap();
    // Now connect to the socket and make sure the protocol negotiation works...
    let stream = TcpStream::connect(localhost).unwrap();
    let stream = match SslStream::connect(&ctx, stream) {
        Ok(stream) => stream,
        Err(err) => panic!("Expected success, got {:?}", err),
    };
    let stream = SslStream::connect(&ctx, stream).unwrap();

    // Since the protocols from the server and client don't overlap at all, no protocol is selected
    assert_eq!(None, stream.ssl().selected_alpn_protocol());
}

// In 1.1.0, ALPN negotiation failure is a fatal error
#[test]
#[cfg(all(feature = "openssl-102", ossl110))]
fn test_alpn_server_select_none() {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let localhost = listener.local_addr().unwrap();
    // We create a different context instance for the server...
    let listener_ctx = {
        let mut ctx = SslContext::new(Tls).unwrap();
        ctx.set_verify(SSL_VERIFY_PEER);
        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();
        assert!(SslStream::accept(&listener_ctx, stream).is_err());
    });

    let mut ctx = SslContext::new(Tls).unwrap();
    ctx.set_verify(SSL_VERIFY_PEER);
    ctx.set_alpn_protocols(&[b"http/2"]);
    ctx.set_CA_file(&Path::new("test/root-ca.pem")).unwrap();
    // Now connect to the socket and make sure the protocol negotiation works...
    let stream = TcpStream::connect(localhost).unwrap();
    assert!(SslStream::connect(&ctx, stream).is_err());
}

#[cfg(test)]
mod dtlsv1 {