123456789_123456789_123456789_123456789_123456789_

Class: OpenSSL::SSL::SSLSocket

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: ext/openssl/ossl_ssl.c,
ext/openssl/lib/openssl/ssl.rb

Constant Summary

::OpenSSL::Buffering - Included

BLOCK_SIZE

Class Method Summary

Instance Attribute Summary

SocketForwarder - Included

#close_on_exec=, #close_on_exec?, #closed?, #do_not_reverse_lookup=,
#timeout

See additional method definition at line 249.

#timeout=

See additional method definition at line 253.

::OpenSSL::Buffering - Included

#eof

Alias for Buffering#eof?.

#sync

The "sync mode" of the SSLSocket.

Instance Method Summary

SocketForwarder - Included

#addr, #fcntl,
#fileno

The file descriptor for the socket.

#getsockopt, #local_address, #peeraddr, #remote_address, #setsockopt,
#wait

Calls IO#wait on the underlying socket.

#wait_readable

Calls IO#wait_readable on the underlying socket.

#wait_writable

Calls IO#wait_writable on the underlying socket.

::OpenSSL::Buffering - Included

#<<

Writes s to the stream.

#close

Closes the SSLSocket and flushes any unwritten data.

#each

Executes the block for every line in the stream where lines are separated by eol.

#each_byte

Calls the given block once for each byte in the stream.

#each_char

Calls the given block once for each character in the stream.

#each_line

Alias for Buffering#each.

#eof?

Returns true if the stream is at file which means there is no more data to be read.

#flush

Flushes buffered data to the SSLSocket.

#getbyte

Get the next 8bit byte from ssl.

#getc

Reads one character from the stream.

#gets

Reads the next "line" from the stream.

#initialize

Creates an instance of OpenSSL's buffering ::IO module.

#print

Writes args to the stream.

#printf

Formats and writes to the stream converting parameters under control of the format string.

#putc

Writes character ch to the stream.

#puts

Writes args to the stream along with a newline.

#read

Reads size bytes from the stream.

#read_nonblock

Reads at most maxlen bytes in the non-blocking manner.

#readbyte

Get the next 8bit byte.

#readchar

Reads a one-character string from the stream.

#readline

Reads a line from the stream which is separated by eol.

#readlines

Reads lines from the stream which are separated by eol.

#readpartial

Reads at most maxlen bytes from the stream.

#ungetbyte

Pushes byte c back onto the stream such that a subsequent buffered byte read will return it.

#ungetc

Pushes character c back onto the stream such that a subsequent buffered character read will return it.

#write

Writes s to the stream.

#write_nonblock

Writes s in the non-blocking manner.

#consume_rbuff

Consumes size bytes from the buffer.

#do_write

Writes s to the buffer.

#fill_rbuff

Fills the buffer from the underlying SSLSocket.

#gets_regexp, #gets_slurp, #gets_string, #swallow_newlines

Constructor Details

.new(io) ⇒ SSLSocket .new(io, ctx) ⇒ SSLSocket .new(io, ctx, sync_close:) ⇒ SSLSocket

Creates a new ::OpenSSL::SSL socket from io which must be a real ::IO object (not an IO-like object that responds to read/write).

If ctx is provided the ::OpenSSL::SSL Sockets initial params will be taken from the context.

The optional sync_close keyword parameter sets the sync_close instance variable. Setting this to true will cause the underlying socket to be closed when the SSL/TLS connection is shut down.

The ::OpenSSL::Buffering module provides additional ::IO methods.

This method will freeze the SSLContext if one is provided; however, session management is still allowed in the frozen SSLContext.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1692

static VALUE
ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
{
    static ID kw_ids[1];
    VALUE kw_args[1];
    VALUE opts;

    VALUE io, v_ctx;
    SSL *ssl;
    SSL_CTX *ctx;

    argc = rb_scan_args(argc, argv, "11:", &io, &v_ctx, &opts);
    if (!kw_ids[0]) {
        kw_ids[0] = rb_intern_const("sync_close");
    }
    rb_get_kwargs(opts, kw_ids, 0, 1, kw_args);
    ossl_want_uninitialized(self, &ossl_ssl_type);

    if (argc == 1)
        v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0);
    if (kw_args[0] != Qundef) {
        rb_ivar_set(self, id_i_sync_close, kw_args[0]);
    }

    GetSSLCTX(v_ctx, ctx);
    rb_ivar_set(self, id_i_context, v_ctx);
    rb_ivar_set(self, id_original_context, v_ctx);
    ossl_sslctx_setup(v_ctx);

    if (rb_respond_to(io, rb_intern("nonblock=")))
        rb_funcall(io, rb_intern("nonblock="), 1, Qtrue);
    Check_Type(io, T_FILE);
    rb_ivar_set(self, id_i_io, io);

    ssl = SSL_new(ctx);
    if (!ssl)
        ossl_raise(eSSLError, NULL);
    RTYPEDDATA_DATA(self) = ssl;

    if (!SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self))
        ossl_raise(eSSLError, "SSL_set_ex_data");
    SSL_set_info_callback(ssl, ssl_info_cb);

    rb_call_super(0, NULL);

    return self;
}

Class Method Details

.open(remote_host, remote_port, local_host = nil, local_port = nil, context: nil)

Creates a new instance of SSLSocket. remote_host and remote_port are used to open TCPSocket. If local_host and local_port are specified, then those parameters are used on the local end to establish the connection. If context is provided, the ::OpenSSL::SSL Sockets initial params will be taken from the context.

Examples

sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
sock.connect # Initiates a connection to localhost:443

with SSLContext:

ctx = OpenSSL::SSL::SSLContext.new
sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
sock.connect # Initiates a connection to localhost:443 with SSLContext
[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 647

def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
  sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
  if context.nil?
    return OpenSSL::SSL::SSLSocket.new(sock)
  else
    return OpenSSL::SSL::SSLSocket.new(sock, context)
  end
end

Instance Attribute Details

#context (readonly)

The SSLContext object used in this connection.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 363

attr_reader :context

#hostname (rw)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 356

attr_reader :hostname

#hostname=(hostname) ⇒ hostname (rw)

Sets the server hostname used for SNI. This needs to be set before #connect.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2266

static VALUE
ossl_ssl_set_hostname(VALUE self, VALUE arg)
{
    SSL *ssl;
    char *hostname = NULL;

    GetSSL(self, ssl);

    if (!NIL_P(arg))
        hostname = StringValueCStr(arg);

    if (!SSL_set_tlsext_host_name(ssl, hostname))
        ossl_raise(eSSLError, NULL);

    /* for SSLSocket#hostname */
    rb_ivar_set(self, id_i_hostname, arg);

    return arg;
}

#io (readonly) Also known as: #to_io

The underlying ::IO object.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 359

attr_reader :io

#session ⇒ aSession (rw)

Returns the SSLSession object currently used, or nil if the session is not established.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 573

def session
  SSL::Session.new(self)
rescue SSL::Session::SessionError
  nil
end

#session=(session) ⇒ session (rw)

Sets the Session to be used when the connection is established.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2244

static VALUE
ossl_ssl_set_session(VALUE self, VALUE arg1)
{
    SSL *ssl;
    SSL_SESSION *sess;

    GetSSL(self, ssl);
    GetSSLSession(arg1, sess);

    if (SSL_set_session(ssl, sess) != 1)
        ossl_raise(eSSLError, "SSL_set_session");

    return arg1;
}

#session_reused? ⇒ Boolean (readonly)

Returns true if a reused session was negotiated during the handshake.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2228

static VALUE
ossl_ssl_session_reused(VALUE self)
{
    SSL *ssl;

    GetSSL(self, ssl);

    return SSL_session_reused(ssl) ? Qtrue : Qfalse;
}

#sync_close (rw)

Whether to close the underlying socket as well, when the SSL/TLS connection is shut down. This defaults to false.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 367

attr_accessor :sync_close

#to_io (readonly)

Alias for #io.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 360

alias :to_io :io

#using_anon_cipher? ⇒ Boolean (readonly, private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 607

def using_anon_cipher?
  ctx = OpenSSL::SSL::SSLContext.new
  ctx.ciphers = "aNULL"
  ctx.ciphers.include?(cipher)
end

Instance Method Details

#accept(timeout: ssl.timeout) ⇒ self

Waits for a SSL/TLS client to initiate a handshake.

If timeout is specified, and if the handshake does not complete within timeout seconds, IO::TimeoutError is raised.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 466

def accept(timeout: self.timeout)
  blocking(timeout) { ssl_accept }
end

#accept_nonblock ⇒ self #accept_nonblock(exception: false) ⇒ self, ...

Initiates the SSL/TLS handshake as a server in non-blocking manner.

# emulates blocking accept
begin
ssl.accept_nonblock
rescue IO::WaitReadable
IO.select([s2])
retry
rescue IO::WaitWritable
IO.select(nil, [s2])
retry
end

By specifying a keyword argument exception to false, you can indicate that accept_nonblock should not raise an ::IO::WaitReadable or ::IO::WaitWritable exception, but return the symbol :wait_readable or :wait_writable instead.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 491

def accept_nonblock(exception: true)
  ret = ssl_accept
  check_nonblock(ret) if exception
  ret
end

#alpn_protocol ⇒ String | nil

Returns the ALPN protocol string that was finally selected by the server during the handshake.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2411

static VALUE
ossl_ssl_alpn_protocol(VALUE self)
{
    SSL *ssl;
    const unsigned char *out;
    unsigned int outlen;

    GetSSL(self, ssl);

    SSL_get0_alpn_selected(ssl, &out, &outlen);
    if (!outlen)
        return Qnil;
    else
        return rb_str_new((const char *) out, outlen);
}

#blocking(timeout = self.timeout) (private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 386

private def blocking(timeout = self.timeout)
  if timeout
    remaining = timeout
    start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  end

  while true
    ret = yield
    if :wait_readable == ret
      io.wait_readable(remaining) or raise IO_TimeoutError,
        "Timed out while waiting to become readable!"
    elsif :wait_writable == ret
      io.wait_writable(remaining) or raise IO_TimeoutError,
        "Timed out while waiting to become writable!"
    else
      return ret
    end

    if timeout
      now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
      remaining = [0, timeout - (now - start)].max
    end
  end
end

#cert ⇒ cert?

The X509 certificate for this socket endpoint.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2066

static VALUE
ossl_ssl_get_cert(VALUE self)
{
    SSL *ssl;
    X509 *cert = NULL;

    GetSSL(self, ssl);

    /*
     * Is this OpenSSL bug? Should add a ref?
     * TODO: Ask for.
     */
    cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */

    if (!cert) {
        return Qnil;
    }
    return ossl_x509_new(cert);
}

#check_nonblock(ret) (private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 411

private def check_nonblock(ret)
  if :wait_readable == ret
    raise SSLErrorWaitReadable, "read would block"
  elsif :wait_writable == ret
    raise SSLErrorWaitWritable, "write would block"
  else
    ret
  end
end

#cipher ⇒ nil, ...

Returns the cipher suite actually used in the current session, or nil if no session has been established.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2165

static VALUE
ossl_ssl_get_cipher(VALUE self)
{
    SSL *ssl;
    const SSL_CIPHER *cipher;

    GetSSL(self, ssl);
    cipher = SSL_get_current_cipher(ssl);
    return cipher ? ossl_ssl_cipher_to_ary(cipher) : Qnil;
}

#client_ca ⇒ Array, ...

Returns the list of client CAs. Please note that in contrast to SSLContext#client_ca= no array of ::OpenSSL::X509::Certificate is returned but ::OpenSSL::X509::Name instances of the CA's subject distinguished name.

In server mode, returns the list set by SSLContext#client_ca=. In client mode, returns the list of client CAs sent from the server.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2365

static VALUE
ossl_ssl_get_client_ca_list(VALUE self)
{
    SSL *ssl;
    STACK_OF(X509_NAME) *ca;

    GetSSL(self, ssl);

    ca = SSL_get_client_CA_list(ssl);
    if (!ca)
        return Qnil;
    return ossl_x509name_sk2ary(ca);
}

#client_cert_cb (private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 613

def client_cert_cb
  @context.client_cert_cb
end

#close_read

Close the stream for reading. This method is ignored by ::OpenSSL as there is no reasonable way to implement it, but exists for compatibility with ::IO.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 582

def close_read
  # Unsupported and ignored.
  # Just don't read any more.
end

#close_write

Closes the stream for writing. The behavior of this method depends on the version of ::OpenSSL and the TLS protocol in use.

  • Sends a 'close_notify' alert to the peer.
  • Does not wait for the peer's 'close_notify' alert in response.

In TLS 1.2 and earlier:

  • On receipt of a 'close_notify' alert, responds with a 'close_notify' alert of its own and close down the connection immediately, discarding any pending writes.

Therefore, on TLS 1.2, this method will cause the connection to be completely shut down. On TLS 1.3, the connection will remain open for reading only.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 601

def close_write
  stop
end

#connect(timeout: ssl.timeout) ⇒ self

Initiates an SSL/TLS handshake with a server.

If timeout is specified, and if the handshake does not complete within timeout seconds, IO::TimeoutError is raised.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 428

def connect(timeout: self.timeout)
  blocking(timeout) { ssl_connect }
end

#connect_nonblock ⇒ self #connect_nonblock(exception: false) ⇒ self, ...

Initiates the SSL/TLS handshake as a client in non-blocking manner.

# emulates blocking connect
begin
ssl.connect_nonblock
rescue IO::WaitReadable
IO.select([s2])
retry
rescue IO::WaitWritable
IO.select(nil, [s2])
retry
end

By specifying a keyword argument exception to false, you can indicate that connect_nonblock should not raise an ::IO::WaitReadable or ::IO::WaitWritable exception, but return the symbol :wait_readable or :wait_writable instead.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 453

def connect_nonblock(exception: true)
  ret = ssl_connect
  check_nonblock(ret) if exception
  ret
end

#export_keying_material(label, length) ⇒ String

Enables use of shared session key material in accordance with RFC 5705.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2433

static VALUE
ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self)
{
    SSL *ssl;
    VALUE str;
    VALUE label;
    VALUE length;
    VALUE context;
    unsigned char *p;
    size_t len;
    int use_ctx = 0;
    unsigned char *ctx = NULL;
    size_t ctx_len = 0;
    int ret;

    rb_scan_args(argc, argv, "21", &label, &length, &context);
    StringValue(label);

    GetSSL(self, ssl);

    len = (size_t)NUM2LEN(length);
    str = rb_str_new(0, len);
    p = (unsigned char *)RSTRING_PTR(str);
    if (!NIL_P(context)) {
        use_ctx = 1;
        StringValue(context);
        ctx = (unsigned char *)RSTRING_PTR(context);
        ctx_len = RSTRING_LEN(context);
    }
    ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label),
                                     RSTRING_LENINT(label), ctx, ctx_len, use_ctx);
    if (ret == 0 || ret == -1) {
        ossl_raise(eSSLError, "SSL_export_keying_material");
    }
    return str;
}

#finished_message ⇒ String?

Returns the contents of the last Finished message sent to the peer.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2311

static VALUE
ossl_ssl_get_finished(VALUE self)
{
    SSL *ssl;
    char sizer[1];
    size_t len;

    GetSSL(self, ssl);

    len = SSL_get_finished(ssl, sizer, 0);
    if (len == 0)
        return Qnil;

    VALUE str = rb_str_new(NULL, len);
    SSL_get_finished(ssl, RSTRING_PTR(str), len);
    return str;
}

#group ⇒ String?

Returns the name of the group that was used for the key agreement of the current TLS session establishment.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2536

static VALUE
ossl_ssl_get_group(VALUE self)
{
    SSL *ssl;
    const char *name;

    GetSSL(self, ssl);
    if (!(name = SSL_get0_group_name(ssl)))
        return Qnil;
    return rb_str_new_cstr(name);
}

#npn_protocol ⇒ String | nil

Returns the protocol string that was finally selected by the client during the handshake.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2387

static VALUE
ossl_ssl_npn_protocol(VALUE self)
{
    SSL *ssl;
    const unsigned char *out;
    unsigned int outlen;

    GetSSL(self, ssl);

    SSL_get0_next_proto_negotiated(ssl, &out, &outlen);
    if (!outlen)
        return Qnil;
    else
        return rb_str_new((const char *) out, outlen);
}

#peer_cert ⇒ cert?

The X509 certificate for this socket's peer.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2092

static VALUE
ossl_ssl_get_peer_cert(VALUE self)
{
    SSL *ssl;
    X509 *cert = NULL;
    VALUE obj;

    GetSSL(self, ssl);

    cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */

    if (!cert) {
        return Qnil;
    }
    obj = ossl_x509_new(cert);
    X509_free(cert);

    return obj;
}

#peer_cert_chain ⇒ Array, ...

The X509 certificate chain for this socket's peer.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2118

static VALUE
ossl_ssl_get_peer_cert_chain(VALUE self)
{
    SSL *ssl;
    STACK_OF(X509) *chain;
    X509 *cert;
    VALUE ary;
    int i, num;

    GetSSL(self, ssl);

    chain = SSL_get_peer_cert_chain(ssl);
    if(!chain) return Qnil;
    num = sk_X509_num(chain);
    ary = rb_ary_new2(num);
    for (i = 0; i < num; i++){
        cert = sk_X509_value(chain, i);
        rb_ary_push(ary, ossl_x509_new(cert));
    }

    return ary;
}

#peer_finished_message ⇒ String?

Returns the contents of the last Finished message expected to be sent by the peer.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2336

static VALUE
ossl_ssl_get_peer_finished(VALUE self)
{
    SSL *ssl;
    char sizer[1];
    size_t len;

    GetSSL(self, ssl);

    len = SSL_get_peer_finished(ssl, sizer, 0);
    if (len == 0)
        return Qnil;

    VALUE str = rb_str_new(NULL, len);
    SSL_get_peer_finished(ssl, RSTRING_PTR(str), len);
    return str;
}

#peer_sigalg ⇒ String?

Returns the signature algorithm name, the IANA name of the signature scheme used by the peer to sign the TLS handshake.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2515

static VALUE
ossl_ssl_get_peer_sigalg(VALUE self)
{
    SSL *ssl;
    const char *name;

    GetSSL(self, ssl);
    if (!SSL_get0_peer_signature_name(ssl, &name))
        return Qnil;
    return rb_str_new_cstr(name);
}

#pending ⇒ Integer

Returns the number of bytes buffered by the ::OpenSSL library and immediately available for reading with #sysread.

This does not include data read ahead and buffered by SSLSocket. It may therefore return 0 even when data is available for reading with methods that are aware of the SSLSocket buffer, such as #read or #gets.

See also the man page SSL_pending(3).

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2212

static VALUE
ossl_ssl_pending(VALUE self)
{
    SSL *ssl;

    GetSSL(self, ssl);

    return INT2NUM(SSL_pending(ssl));
}

#post_connection_check(hostname) ⇒ true

Perform hostname verification following RFC 6125.

This method MUST be called after calling #connect to ensure that the hostname of a remote peer has been verified.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 552

def post_connection_check(hostname)
  if peer_cert.nil?
    msg = "Peer verification enabled, but no certificate received."
    if using_anon_cipher?
      msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
             "Anonymous suites must be disabled to use peer verification."
    end
    raise SSLError, msg
  end

  unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
    raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
  end
  return true
end

#session_get_cb (private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 621

def session_get_cb
  @context.session_get_cb
end

#session_new_cb (private)

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 617

def session_new_cb
  @context.session_new_cb
end

#sigalg ⇒ String?

Returns the signature algorithm name, the IANA name of the signature scheme used by the local to sign the TLS handshake.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2496

static VALUE
ossl_ssl_get_sigalg(VALUE self)
{
    SSL *ssl;
    const char *name;

    GetSSL(self, ssl);
    if (!SSL_get0_signature_name(ssl, &name))
        return Qnil;
    return rb_str_new_cstr(name);
}

#ssl_accept ⇒ self, ... (private)

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1869

static VALUE
ossl_ssl_accept(VALUE self)
{
    ossl_ssl_setup(self);
    return ossl_start_ssl(self, SSL_accept, "SSL_accept");
}

#ssl_connect ⇒ self, ... (private)

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1858

static VALUE
ossl_ssl_connect(VALUE self)
{
    ossl_ssl_setup(self);
    return ossl_start_ssl(self, SSL_connect, "SSL_connect");
}

#ssl_read(len, str) ⇒ String, ... (private)

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1892

static VALUE
ossl_ssl_read(VALUE self, VALUE len, VALUE str)
{
    SSL *ssl;
    int ilen;
    VALUE cb_state;

    GetSSL(self, ssl);
    if (!ssl_started(ssl))
        rb_raise(eSSLError, "SSL session is not started yet");

    ilen = NUM2INT(len);
    if (ilen < 0)
        rb_raise(rb_eArgError, "negative length %d given", ilen);
    StringValue(str);
    if ((rb_len_t)rb_str_capacity(str) >= ilen)
        rb_str_modify(str);
    else
        rb_str_modify_expand(str, ilen - RSTRING_LEN(str));

    if (ilen == 0) {
        rb_str_set_len(str, 0);
        return str;
    }

    rb_str_locktmp(str);
    int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
    rb_str_unlocktmp(str);
    int saved_errno = errno_mapped();

    cb_state = rb_attr_get(self, ID_callback_state);
    if (!NIL_P(cb_state)) {
        rb_ivar_set(self, ID_callback_state, Qnil);
        ossl_clear_error();
        rb_jump_tag(NUM2INT(cb_state));
    }

    switch (SSL_get_error(ssl, nread)) {
      case SSL_ERROR_NONE:
        rb_str_set_len(str, nread);
        return str;
      case SSL_ERROR_ZERO_RETURN:
        return Qnil;
      case SSL_ERROR_WANT_WRITE:
        return sym_wait_writable;
      case SSL_ERROR_WANT_READ:
        return sym_wait_readable;
      case SSL_ERROR_SYSCALL:
        if (saved_errno) {
            ossl_clear_error();
            rb_exc_raise(rb_syserr_new(saved_errno, "SSL_read"));
        }
        if (!ERR_peek_error()) {
            /*
             * The underlying BIO returned 0. This is actually a
             * protocol error. But unfortunately, not all
             * implementations cleanly shutdown the TLS connection
             * but just shutdown/close the TCP connection. So report
             * EOF for now...
             */
            return Qnil;
        }
        /* fall through */
      default:
        ossl_raise(eSSLError, "SSL_read");
    }
}

#ssl_version ⇒ String

Returns a String representing the SSL/TLS version that was negotiated for the connection, for example "TLSv1.2".

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2148

static VALUE
ossl_ssl_get_version(VALUE self)
{
    SSL *ssl;

    GetSSL(self, ssl);

    return rb_str_new2(SSL_get_version(ssl));
}

#ssl_write(str) ⇒ Integer, ... (private)

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1964

static VALUE
ossl_ssl_write(VALUE self, VALUE str)
{
    SSL *ssl;
    VALUE cb_state;

    GetSSL(self, ssl);
    if (!ssl_started(ssl))
        rb_raise(eSSLError, "SSL session is not started yet");

    str = rb_obj_as_string(str);
    /* SSL_write(3ssl) manpage states num == 0 is undefined */
    int num = RSTRING_LENINT(str);
    if (num == 0)
        return INT2FIX(0);

#ifdef __APPLE__
  retry:;
#endif
    int frozen = RB_OBJ_FROZEN(str);
    if (!frozen) {
        rb_str_locktmp(str);
    }
    int nwritten = SSL_write(ssl, RSTRING_PTR(str), num);
    if (!frozen) {
        rb_str_unlocktmp(str);
    }
    int saved_errno = errno_mapped();

    cb_state = rb_attr_get(self, ID_callback_state);
    if (!NIL_P(cb_state)) {
        rb_ivar_set(self, ID_callback_state, Qnil);
        ossl_clear_error();
        rb_jump_tag(NUM2INT(cb_state));
    }

    switch (SSL_get_error(ssl, nwritten)) {
      case SSL_ERROR_NONE:
        return INT2NUM(nwritten);
      case SSL_ERROR_WANT_WRITE:
        return sym_wait_writable;
      case SSL_ERROR_WANT_READ:
        return sym_wait_readable;
      case SSL_ERROR_SYSCALL:
        if (saved_errno) {
            ossl_clear_error();
#ifdef __APPLE__
            /*
             * It appears that send syscall can return EPROTOTYPE if the
             * socket is being torn down. Retry to get a proper errno to
             * make the error handling in line with the socket library.
             * [Bug #14713] https://bugs.ruby-lang.org/issues/14713
             */
            if (saved_errno == EPROTOTYPE)
                goto retry;
#endif
            rb_exc_raise(rb_syserr_new(saved_errno, "SSL_write"));
        }
        /* fallthrough */
      default:
        ossl_raise(eSSLError, "SSL_write");
    }
}

#state ⇒ String

A description of the current connection state. This is for diagnostic purposes only.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2183

static VALUE
ossl_ssl_get_state(VALUE self)
{
    SSL *ssl;
    VALUE ret;

    GetSSL(self, ssl);

    ret = rb_str_new2(SSL_state_string(ssl));
    if (ruby_verbose) {
        rb_str_cat2(ret, ": ");
        rb_str_cat2(ret, SSL_state_string_long(ssl));
    }
    return ret;
}

#stop ⇒ nil (private)

Sends "close notify" to the peer and tries to shut down the ::OpenSSL::SSL connection gracefully.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2035

static VALUE
ossl_ssl_stop(VALUE self)
{
    SSL *ssl;
    int ret;

    GetSSL(self, ssl);
    if (!ssl_started(ssl))
        return Qnil;
    ret = SSL_shutdown(ssl);
    if (ret == 1) /* Have already received close_notify */
        return Qnil;
    if (ret == 0) /* Sent close_notify, but we don't wait for reply */
        return Qnil;

    /*
     * XXX: Something happened. Possibly it failed because the underlying socket
     * is not writable/readable, since it is in non-blocking mode. We should do
     * some proper error handling using SSL_get_error() and maybe retry, but we
     * can't block here. Give up for now.
     */
    ossl_clear_error();
    return Qnil;
}

#string_new_capacity(len) (private)

TODO: This shouldn't be necessary, but we do not want to use String.new(capacity: len) in #sysread because it allocates Hash objects for the kwargs.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 1881

static VALUE
ossl_ssl_string_new_capacity(VALUE self, VALUE len)
{
    rb_len_t capa = NUM2LEN(len);
    return rb_str_buf_new(capa >= 0 ? capa : 0);
}

#sysclose ⇒ nil

Sends "close notify" to the peer and tries to shut down the ::OpenSSL::SSL connection gracefully.

If sync_close is set to true, the underlying ::IO is also closed.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 376

def sysclose
  return if closed?
  stop
  io.close if sync_close
end

#sysread(length) ⇒ String #sysread(length, buffer) ⇒ buffer

Reads length bytes from the ::OpenSSL::SSL connection. If a pre-allocated buffer is provided the data will be written into it.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 503

def sysread(length, buffer = nil)
  buffer ||= string_new_capacity(length)
  blocking {
    ssl_read(length, buffer) or raise EOFError, "end of file reached"
  }
end

#sysread_nonblock(length) ⇒ String (private) #sysread_nonblock(length, buffer) ⇒ buffer #sysread_nonblock(length, buffer, exception: false) ⇒ buffer, ...

A non-blocking version of #sysread.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 516

private def sysread_nonblock(length, buffer = nil, exception: true)
  buffer ||= string_new_capacity(length)
  ret = ssl_read(length, buffer)
  if exception
    raise EOFError, "end of file reached" if ret.nil?
    check_nonblock(ret)
  end
  ret
end

#syswrite(string) ⇒ Integer

Writes string to the ::OpenSSL::SSL connection.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 530

def syswrite(string)
  blocking { ssl_write(string) }
end

#syswrite_nonblock(string) ⇒ Integer (private) #syswrite_nonblock(string, exception: false) ⇒ Integer, ...

Writes string to the ::OpenSSL::SSL connection in a non-blocking manner.

[ GitHub ]

  
# File 'ext/openssl/lib/openssl/ssl.rb', line 539

private def syswrite_nonblock(string, exception: true)
  ret = ssl_write(string)
  check_nonblock(ret) if exception
  ret
end

#tmp_key ⇒ PKey?

Returns the ephemeral key used in case of forward secrecy cipher.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2476

static VALUE
ossl_ssl_tmp_key(VALUE self)
{
    SSL *ssl;
    EVP_PKEY *key;

    GetSSL(self, ssl);
    if (!SSL_get_server_tmp_key(ssl, &key))
        return Qnil;
    return ossl_pkey_wrap(key);
}

#verify_result ⇒ Integer

Returns the result of the peer certificates verification. See verify(1) for error values and descriptions.

If no peer certificate was presented X509_V_OK is returned.

[ GitHub ]

  
# File 'ext/openssl/ossl_ssl.c', line 2295

static VALUE
ossl_ssl_get_verify_result(VALUE self)
{
    SSL *ssl;

    GetSSL(self, ssl);

    return LONG2NUM(SSL_get_verify_result(ssl));
}