This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

crypto: use better memory BIO implementation

  • Loading branch information...
indutny committed Apr 3, 2013
1 parent 0f460b0 commit 58f93ffc4a23aa7240808288acf8cf9f3022abea
Showing with 412 additions and 5 deletions.
  1. +2 −1 node.gyp
  2. +5 −4 src/node_crypto.cc
  3. +312 −0 src/node_crypto_bio.cc
  4. +93 −0 src/node_crypto_bio.h
View
@@ -110,6 +110,7 @@
'src/node_buffer.h',
'src/node_constants.h',
'src/node_crypto.h',
+ 'src/node_crypto_bio.h',
'src/node_extensions.h',
'src/node_file.h',
'src/node_http_parser.h',
@@ -147,7 +148,7 @@
'conditions': [
[ 'node_use_openssl=="true"', {
'defines': [ 'HAVE_OPENSSL=1' ],
- 'sources': [ 'src/node_crypto.cc' ],
+ 'sources': [ 'src/node_crypto.cc', 'src/node_crypto_bio.cc' ],
'conditions': [
[ 'node_shared_openssl=="false"', {
'dependencies': [ './deps/openssl/openssl.gyp:openssl' ],
View
@@ -20,6 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node_crypto.h"
+#include "node_crypto_bio.h"
#include "node_crypto_groups.h"
#include "v8.h"
@@ -291,7 +292,7 @@ int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) {
// Takes a string or buffer and loads it into a BIO.
// Caller responsible for BIO_free-ing the returned object.
static BIO* LoadBIO (Handle<Value> v) {
- BIO *bio = BIO_new(BIO_s_mem());
+ BIO *bio = BIO_new(NodeBIO::GetMethod());
if (!bio) return NULL;
HandleScope scope;
@@ -544,7 +545,7 @@ Handle<Value> SecureContext::AddRootCerts(const Arguments& args) {
root_cert_store = X509_STORE_new();
for (int i = 0; root_certs[i]; i++) {
- BIO *bp = BIO_new(BIO_s_mem());
+ BIO *bp = BIO_new(NodeBIO::GetMethod());
if (!BIO_write(bp, root_certs[i], strlen(root_certs[i]))) {
BIO_free(bp);
@@ -1201,8 +1202,8 @@ Handle<Value> Connection::New(const Arguments& args) {
bool is_server = args[1]->BooleanValue();
p->ssl_ = SSL_new(sc->ctx_);
- p->bio_read_ = BIO_new(BIO_s_mem());
- p->bio_write_ = BIO_new(BIO_s_mem());
+ p->bio_read_ = BIO_new(NodeBIO::GetMethod());
+ p->bio_write_ = BIO_new(NodeBIO::GetMethod());
SSL_set_app_data(p->ssl_, p);
View
@@ -0,0 +1,312 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "node_crypto_bio.h"
+#include "openssl/bio.h"
+#include <string.h>
+
+namespace node {
+
+BIO_METHOD NodeBIO::method_ = {
+ BIO_TYPE_MEM,
+ "node.js SSL buffer",
+ NodeBIO::Write,
+ NodeBIO::Read,
+ NodeBIO::Puts,
+ NodeBIO::Gets,
+ NodeBIO::Ctrl,
+ NodeBIO::New,
+ NodeBIO::Free,
+ NULL
+};
+
+
+int NodeBIO::New(BIO* bio) {
+ bio->ptr = new NodeBIO();
+
+ // XXX Why am I doing it?!
+ bio->shutdown = 1;
+ bio->init = 1;
+ bio->num = -1;
+
+ return 1;
+}
+
+
+int NodeBIO::Free(BIO* bio) {
+ if (bio == NULL) return 0;
+
+ if (bio->shutdown) {
+ if (bio->init && bio->ptr != NULL) {
+ delete FromBIO(bio);
+ bio->ptr = NULL;
+ }
+ }
+
+ return 1;
+}
+
+
+int NodeBIO::Read(BIO* bio, char* out, int len) {
+ int bytes;
+ BIO_clear_retry_flags(bio);
+
+ bytes = FromBIO(bio)->Read(out, len);
+
+ if (bytes == 0) {
+ bytes = bio->num;
+ if (bytes != 0) {
+ BIO_set_retry_read(bio);
+ }
+ }
+
+ return bytes;
+}
+
+
+int NodeBIO::Write(BIO* bio, const char* data, int len) {
+ BIO_clear_retry_flags(bio);
+
+ FromBIO(bio)->Write(data, len);
+
+ return len;
+}
+
+
+int NodeBIO::Puts(BIO* bio, const char* str) {
+ return Write(bio, str, strlen(str));
+}
+
+
+int NodeBIO::Gets(BIO* bio, char* out, int size) {
+ NodeBIO* nbio = FromBIO(bio);
+
+ if (nbio->Length() == 0)
+ return 0;
+
+ int i = nbio->IndexOf('\n', size);
+
+ // Include '\n'
+ if (i < size) i++;
+
+ // Shift `i` a bit to NULL-terminate string later
+ if (size == i) i--;
+
+ // Flush read data
+ nbio->Read(out, i);
+
+ out[i] = 0;
+
+ return i;
+}
+
+
+long NodeBIO::Ctrl(BIO* bio, int cmd, long num, void* ptr) {
+ NodeBIO* nbio;
+ long ret;
+
+ nbio = FromBIO(bio);
+ ret = 1;
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ nbio->Reset();
+ break;
+ case BIO_CTRL_EOF:
+ ret = nbio->Length() == 0;
+ break;
+ case BIO_C_SET_BUF_MEM_EOF_RETURN:
+ bio->num = num;
+ break;
+ case BIO_CTRL_INFO:
+ ret = nbio->Length();
+ if (ptr != NULL)
+ *reinterpret_cast<void**>(ptr) = NULL;
+ break;
+ case BIO_C_SET_BUF_MEM:
+ assert(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
+ abort();
+ break;
+ case BIO_C_GET_BUF_MEM_PTR:
+ assert(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
+ ret = 0;
+ break;
+ case BIO_CTRL_GET_CLOSE:
+ ret = bio->shutdown;
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ bio->shutdown = num;
+ break;
+ case BIO_CTRL_WPENDING:
+ ret = 0;
+ break;
+ case BIO_CTRL_PENDING:
+ ret = nbio->Length();
+ break;
+ case BIO_CTRL_DUP:
+ case BIO_CTRL_FLUSH:
+ ret = 1;
+ break;
+ case BIO_CTRL_PUSH:
+ case BIO_CTRL_POP:
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+
+size_t NodeBIO::Read(char* out, size_t size) {
+ size_t bytes_read = 0;
+ size_t expected = Length() > size ? size : Length();
+
+ while (bytes_read < expected) {
+ assert(read_head_->read_pos_ <= read_head_->write_pos_);
+ size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
+ if (avail > size)
+ avail = size;
+
+ // Copy data
+ if (out != NULL)
+ memcpy(out, read_head_->data_ + read_head_->read_pos_, avail);
+ read_head_->read_pos_ += avail;
+
+ // Move pointers
+ bytes_read += avail;
+ out += avail;
+ size -= avail;
+
+ // Move to next buffer
+ if (read_head_->read_pos_ == read_head_->write_pos_) {
+ read_head_->read_pos_ = 0;
+ read_head_->write_pos_ = 0;
+ read_head_ = read_head_->next_;
+ }
+ }
+ assert(expected == bytes_read);
+ length_ -= bytes_read;
+
+ return bytes_read;
+}
+
+
+size_t NodeBIO::IndexOf(char delim, size_t limit) {
+ size_t bytes_read = 0;
+ size_t max = Length() > limit ? limit : Length();
+ Buffer* current = read_head_;
+
+ while (bytes_read < max) {
+ assert(current->read_pos_ <= current->write_pos_);
+ size_t avail = current->write_pos_ - current->read_pos_;
+ if (avail > limit)
+ avail = limit;
+
+ // Walk through data
+ char* tmp = current->data_ + current->read_pos_;
+ size_t off = 0;
+ while (off < avail && *tmp != delim) {
+ off++;
+ tmp++;
+ }
+
+ // Move pointers
+ bytes_read += off;
+ limit -= off;
+
+ // Found `delim`
+ if (off != avail) {
+ return bytes_read;
+ }
+
+ // Move to next buffer
+ if (current->read_pos_ + avail == kBufferLength) {
+ current = current->next_;
+ }
+ }
+ assert(max == bytes_read);
+
+ return max;
+}
+
+
+void NodeBIO::Write(const char* data, size_t len) {
+ while (len > 0) {
+ size_t to_write = len;
+ assert(write_head_->write_pos_ <= kBufferLength);
+ size_t avail = kBufferLength - write_head_->write_pos_;
+
+ if (to_write > avail)
+ to_write = avail;
+
+ // Copy data
+ memcpy(write_head_->data_ + write_head_->write_pos_, data, to_write);
+ write_head_->write_pos_ += to_write;
+ assert(write_head_->write_pos_ <= kBufferLength);
+
+ // Move pointers
+ len -= to_write;
+ data += to_write;
+ length_ += to_write;
+
+ // Still have some bytes left:
+ // 1. Go to next buffer
+ // 2. Allocate new if next is already full or is partially read
+ // (is read head)
+ if (write_head_->next_->write_pos_ == kBufferLength ||
+ write_head_->next_->read_pos_ != 0) {
+ Buffer* next = new Buffer();
+ next->next_ = write_head_->next_;
+ write_head_->next_ = next;
+ }
+ write_head_ = write_head_->next_;
+ }
+ assert(len == 0);
+}
+
+
+void NodeBIO::Reset() {
+ while (read_head_->read_pos_ != read_head_->write_pos_) {
+ assert(read_head_->write_pos_ > read_head_->read_pos_);
+
+ length_ -= read_head_->write_pos_ - read_head_->read_pos_;
+ read_head_->write_pos_ = 0;
+ read_head_->read_pos_ = 0;
+
+ read_head_ = read_head_->next_;
+ }
+ assert(length_ == 0);
+}
+
+
+NodeBIO::~NodeBIO() {
+ Buffer* current = head_.next_;
+ while (current != &head_) {
+ Buffer* next = current->next_;
+ delete current;
+ current = next;
+ }
+
+ read_head_ = NULL;
+ write_head_ = NULL;
+}
+
+} // namespace node
Oops, something went wrong.

0 comments on commit 58f93ff

Please sign in to comment.