# class Puma::MiniSSL::Engine

### Public Class Methods

client() click to toggle source

    VALUE engine_init_client(VALUE klass) {
      VALUE obj;
      ms_conn* conn = engine_alloc(klass, &obj);

      conn->ctx = SSL_CTX_new(DTLSv1_method());
      conn->ssl = SSL_new(conn->ctx);
      SSL_set_app_data(conn->ssl, NULL);
      SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);

      SSL_set_bio(conn->ssl, conn->read, conn->write);

      SSL_set_connect_state(conn->ssl);
      return obj;
    }

server(p1) click to toggle source

    VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
      VALUE obj;
      SSL_CTX* ctx;
      SSL* ssl;

      ms_conn* conn = engine_alloc(self, &obj);

      ID sym_key = rb_intern("key");
      VALUE key = rb_funcall(mini_ssl_ctx, sym_key, 0);

      StringValue(key);

      ID sym_cert = rb_intern("cert");
      VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);

      StringValue(cert);

      ID sym_ca = rb_intern("ca");
      VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);

      ID sym_verify_mode = rb_intern("verify_mode");
      VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);

      ctx = SSL_CTX_new(SSLv23_server_method());
      conn->ctx = ctx;

      SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
      SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);

      if (!NIL_P(ca)) {
        StringValue(ca);
        SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
      }

      SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
      SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);

      SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");

      DH *dh = get_dh1024();
      SSL_CTX_set_tmp_dh(ctx, dh);

    #ifndef OPENSSL_NO_ECDH
      EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
      if (ecdh) {
        SSL_CTX_set_tmp_ecdh(ctx, ecdh);
        EC_KEY_free(ecdh);
      }
    #endif

      ssl = SSL_new(ctx);
      conn->ssl = ssl;
      SSL_set_app_data(ssl, NULL);

      if (NIL_P(verify_mode)) {
        /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
      } else {
        SSL_set_verify(ssl, NUM2INT(verify_mode), engine_verify_callback);
      }

      SSL_set_bio(ssl, conn->read, conn->write);

      SSL_set_accept_state(ssl);
      return obj;
    }

### Public Instance Methods

extract() click to toggle source

    VALUE engine_extract(VALUE self) {
      ms_conn* conn;
      int bytes;
      size_t pending;
      char buf[512];

      Data_Get_Struct(self, ms_conn, conn);

      pending = BIO_pending(conn->write);
      if(pending > 0) {
        bytes = BIO_read(conn->write, buf, sizeof(buf));
        if(bytes > 0) {
          return rb_str_new(buf, bytes);
        } else if(!BIO_should_retry(conn->write)) {
          raise_error(conn->ssl, bytes);
        }
      }

      return Qnil;
    }

init?() click to toggle source

    VALUE engine_init(VALUE self) {
      ms_conn* conn;

      Data_Get_Struct(self, ms_conn, conn);

      return SSL_in_init(conn->ssl) ? Qtrue : Qfalse;
    }

inject(p1) click to toggle source

    VALUE engine_inject(VALUE self, VALUE str) {
      ms_conn* conn;
      long used;

      Data_Get_Struct(self, ms_conn, conn);

      StringValue(str);

      used = BIO_write(conn->read, RSTRING_PTR(str), (int)RSTRING_LEN(str));

      if(used == 0 || used == -1) {
        return Qfalse;
      }

      return INT2FIX(used);
    }

peercert() click to toggle source

    VALUE engine_peercert(VALUE self) {
      ms_conn* conn;
      X509* cert;
      int bytes;
      unsigned char* buf = NULL;
      ms_cert_buf* cert_buf = NULL;
      VALUE rb_cert_buf;

      Data_Get_Struct(self, ms_conn, conn);

      cert = SSL_get_peer_certificate(conn->ssl);
      if(!cert) {
        /*
         * See if there was a failed certificate associated with this client.
         */
        cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
        if(!cert_buf) {
          return Qnil;
        }
        buf = cert_buf->buf;
        bytes = cert_buf->bytes;

      } else {
        bytes = i2d_X509(cert, &buf);
        X509_free(cert);

        if(bytes < 0) {
          return Qnil;
        }
      }

      rb_cert_buf = rb_str_new((const char*)(buf), bytes);
      if(!cert_buf) {
        OPENSSL_free(buf);
      }

      return rb_cert_buf;
    }

read() click to toggle source

    VALUE engine_read(VALUE self) {
      ms_conn* conn;
      char buf[512];
      int bytes, error;

      Data_Get_Struct(self, ms_conn, conn);

      ERR_clear_error();

      bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));

      if(bytes > 0) {
        return rb_str_new(buf, bytes);
      }

      if(SSL_want_read(conn->ssl)) return Qnil;

      error = SSL_get_error(conn->ssl, bytes);

      if(error == SSL_ERROR_ZERO_RETURN) {
        rb_eof_error();
      } else {
        raise_error(conn->ssl, bytes);
      }

      return Qnil;
    }

shutdown() click to toggle source

    VALUE engine_shutdown(VALUE self) {
      ms_conn* conn;
      int ok;

      Data_Get_Struct(self, ms_conn, conn);

      ERR_clear_error();

      ok = SSL_shutdown(conn->ssl);
      if (ok == 0) {
        return Qfalse;
      }

      return Qtrue;
    }

write(p1) click to toggle source

    VALUE engine_write(VALUE self, VALUE str) {
      ms_conn* conn;
      int bytes;

      Data_Get_Struct(self, ms_conn, conn);

      StringValue(str);

      ERR_clear_error();

      bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
      if(bytes > 0) {
        return INT2FIX(bytes);
      }

      if(SSL_want_write(conn->ssl)) return Qnil;

      raise_error(conn->ssl, bytes);

      return Qnil;
    }